示例#1
0
class ChartWidget(QWidget):
    def __init__(self, number: int, parent=None):
        super(QWidget, self).__init__(parent)

        self.layout = QVBoxLayout(self)

        self.chart = QChart()
        self.chartView = QChartView(self.chart)
        self.chartView.setRenderHint(QPainter.Antialiasing)  # 抗锯齿
        self.layout.addWidget(self.chartView)

        self.channelNumber = number
        for index in range(self.channelNumber):
            tmp = QLineSeries()
            tmp.setName("channel "+str(index))
            self.chart.addSeries(tmp)

        self.chart.createDefaultAxes()
        self.chart.axisX().setRange(0, 25)
        self.chart.axisX().setTitleText("AAA")
        self.chart.axisY().setRange(0, 25)
        self.chart.axisY().setTitleText("BBB")

        # 标题
        self.chart.setTitle("XXX XXX XXX")
        # 指示颜色所代表的内容
        # self.chart.legend().hide()
        # 动画效果
        self.chart.setAnimationOptions(QChart.AllAnimations)

    def set_line(self, *lines):
        for index in range(len(lines)):
            self.chart.series()[index].clear()
            for v1, v2 in enumerate(lines[index]):
                self.chart.series()[index].append(v1, v2)
示例#2
0
class pieChartView(QChartView):
    def __init__(self, *args, **kwargs):
        super(pieChartView, self).__init__(*args, **kwargs)
        self.initChart()
        self.refresh()

    def initChart(self):
        self._chart = QChart()
        # 调整边距
        self._chart.layout().setContentsMargins(0, 0, 0, 0)  # 外界
        self._chart.setMargins(QMargins(3, 0, 3, 0))  # 内界
        self._chart.setBackgroundRoundness(0)
        self._chart.setBackgroundVisible(False)
        # 设置主题
        self._chart.setTheme(QChart.ChartThemeBlueIcy)
        # 抗锯齿
        self.setRenderHint(QPainter.Antialiasing)
        # 开启动画效果
        self._chart.setAnimationOptions(QChart.SeriesAnimations)
        self._series = QPieSeries()
        self._series.setPieSize(0.8)
        self._chart.addSeries(self._series)
        self.setChart(self._chart)

    def refresh(self):
        # 提示widget
        self.toolTipWidget = GraphicsProxyWidget(self._chart)

    def mouseMoveEvent(self, event):
        super(pieChartView, self).mouseMoveEvent(event)
        pos = event.pos()
        x = pos.x()
        y = pos.y()
        # 得到在坐标系中的所有区域
        self.min_x, self.max_x = self._chart.geometry().width(
        ) * 0.2, self._chart.geometry().width() * 0.8
        self.min_y, self.max_y = self._chart.geometry().height(
        ) * 0.2, self._chart.geometry().height() * 0.9
        serie = self._chart.series()[0]
        slices = [(slice, slice.value()) for slice in serie.slices()]
        if self.min_x <= x <= self.max_x and self.min_y <= y <= self.max_y:
            t_width = self.toolTipWidget.width()
            t_height = self.toolTipWidget.height()
            title = "数据组成"
            # 如果鼠标位置离右侧的距离小于tip宽度
            x = pos.x() - t_width if self.width() - \
                                     pos.x() - 20 < t_width else pos.x()
            # 如果鼠标位置离底部的高度小于tip高度
            y = pos.y() - t_height if self.height() - \
                                      pos.y() - 20 < t_height else pos.y()
            self.toolTipWidget.show(title, slices, QPoint(x, y))
        else:
            self.toolTipWidget.hide()
class MainWindow(ChartViewToolTips):
    def __init__(self):
        super().__init__()

        series = QLineSeries()
        series.setPointsVisible(True)
        series.setPointLabelsVisible(True)
        series.setPointLabelsFormat("(@xPoint, @yPoint)")
        series.hovered.connect(self.show_series_tooltip)

        series.append(0, 6)
        series.append(2, 4)
        series.append(3, 8)
        series.append(7, 4)
        series.append(10, 5)

        self._chart = QChart()
        self._chart.setMinimumSize(640, 480)
        self._chart.setTitle("Line Chart Example")
        self._chart.setAnimationOptions(QChart.SeriesAnimations)
        self._chart.legend().hide()
        self._chart.addSeries(series)
        self._chart.createDefaultAxes()

        self.setChart(self._chart)

    def show_series_tooltip(self, point, state: bool):
        # value -> pos
        point = self._chart.mapToPosition(point)

        if not self._tooltip:
            self._tooltip = Callout(self._chart)

        if state:
            distance = 20
            for series in self._chart.series():
                for p_value in series.pointsVector():
                    p = self._chart.mapToPosition(p_value)

                    current_distance = math.sqrt(
                        (p.x() - point.x()) * (p.x() - point.x())
                        + (p.y() - point.y()) * (p.y() - point.y())
                    )
                    if current_distance < distance:
                        self._tooltip.setText("X: {}\nY: {}".format(p.x(), p.y()))
                        self._tooltip.setAnchor(p_value)
                        self._tooltip.setZValue(11)
                        self._tooltip.updateGeometry()
                        self._tooltip.show()
        else:
            self._tooltip.hide()
示例#4
0
class barChartView(QChartView):
    def __init__(self, xAxis=[], *args, **kwargs):
        super(barChartView, self).__init__(*args, **kwargs)
        self.initChart(xAxis)

        # line 宽度需要调整
        self.lineItem = QGraphicsLineItem(self._chart)
        pen = QPen(Qt.gray)
        self.lineItem.setPen(pen)
        self.lineItem.setZValue(998)
        self.lineItem.hide()
        self.cal()

    # 一些固定计算,减少mouseMoveEvent中的计算量
    def cal(self):
        # 提示widget
        self.toolTipWidget = GraphicsProxyWidget(self._chart)
        # 获取x和y轴的最小最大值
        axisX, axisY = self._chart.axisX(), self._chart.axisY()
        self.category_len = len(axisX.categories())
        self.min_x, self.max_x = -0.5, self.category_len - 0.5
        self.min_y, self.max_y = axisY.min(), axisY.max()
        # 坐标系中左上角顶点
        self.point_top = self._chart.mapToPosition(
            QPointF(self.min_x, self.max_y))

    def setCat(self, data):
        self.categories = data

    #初始化
    def initChart(self, xAxis):
        self._chart = QChart()
        # 调整边距
        self._chart.layout().setContentsMargins(0, 0, 0, 0)  # 外界
        self._chart.setMargins(QMargins(3, 0, 3, 0))  # 内界
        self._chart.setBackgroundRoundness(0)
        self._chart.setBackgroundVisible(False)
        # 设置主题
        self._chart.setTheme(QChart.ChartThemeBlueIcy)
        # 抗锯齿
        self.setRenderHint(QPainter.Antialiasing)
        # 开启动画效果
        self._chart.setAnimationOptions(QChart.SeriesAnimations)
        self.categories = xAxis
        self._series = QBarSeries(self._chart)
        self._chart.addSeries(self._series)
        self._chart.createDefaultAxes()  # 创建默认的轴
        self._axis_x = QBarCategoryAxis(self._chart)
        self._axis_x.append(self.categories)
        self._axis_y = QValueAxis(self._chart)
        self._axis_y.setTitleText("任务数")
        self._axis_y.setRange(0, 10)
        self._chart.setAxisX(self._axis_x, self._series)
        self._chart.setAxisY(self._axis_y, self._series)
        # chart的图例
        legend = self._chart.legend()
        legend.setVisible(True)

        self.setChart(self._chart)

    def mouseMoveEvent(self, event):
        super(barChartView, self).mouseMoveEvent(event)
        pos = event.pos()
        # 把鼠标位置所在点转换为对应的xy值
        x = self._chart.mapToValue(pos).x()
        y = self._chart.mapToValue(pos).y()
        index = round(x)
        # 得到在坐标系中的所有bar的类型和点
        serie = self._chart.series()[0]
        bars = [
            (bar, bar.at(index)) for bar in serie.barSets()
            if self.min_x <= x <= self.max_x and self.min_y <= y <= self.max_y
        ]
        # print(bars)
        if bars:
            right_top = self._chart.mapToPosition(
                QPointF(self.max_x, self.max_y))
            # 等分距离比例
            step_x = round(
                (right_top.x() - self.point_top.x()) / self.category_len)
            posx = self._chart.mapToPosition(QPointF(x, self.min_y))
            self.lineItem.setLine(posx.x(), self.point_top.y(), posx.x(),
                                  posx.y())
            self.lineItem.show()
            try:
                title = self.categories[index]
            except:
                title = ""
            t_width = self.toolTipWidget.width()
            t_height = self.toolTipWidget.height()
            # 如果鼠标位置离右侧的距离小于tip宽度
            x = pos.x() - t_width if self.width() - \
                pos.x() - 20 < t_width else pos.x()
            # 如果鼠标位置离底部的高度小于tip高度
            y = pos.y() - t_height if self.height() - \
                pos.y() - 20 < t_height else pos.y()
            self.toolTipWidget.show(title, bars, QPoint(x, y))
        else:
            self.toolTipWidget.hide()
            self.lineItem.hide()
示例#5
0
class QmyMainWindow(QMainWindow): 

   def __init__(self, parent=None):
      super().__init__(parent)   #调用父类构造函数,创建窗体
      self.ui=Ui_MainWindow()    #创建UI对象
      self.ui.setupUi(self)      #构造UI界面

      self.setWindowTitle("Demo12_2, QChart绘制折线图")
      self.setCentralWidget(self.ui.splitter)

      self.__chart=None       #图表
      self.__curSeries=None   #当前序列
      self.__curAxis=None     #当前坐标轴
      
      self.__createChart()
      self.__prepareData()
      self.__updateFromChart()

##  ==============自定义功能函数========================
   def __createChart(self):
      self.__chart = QChart()
      self.__chart.setTitle("简单函数曲线")
      self.ui.chartView.setChart(self.__chart)
      self.ui.chartView.setRenderHint(QPainter.Antialiasing)

      series0 =  QLineSeries()
      series0.setName("Sin曲线")
      series1 =  QLineSeries()
      series1.setName("Cos曲线")
      self.__curSeries=series0   #当前序列

      pen=QPen(Qt.red)
      pen.setStyle(Qt.DotLine)   #SolidLine, DashLine, DotLine, DashDotLine
      pen.setWidth(2)
      series0.setPen(pen)        #序列的线条设置

      pen.setStyle(Qt.SolidLine) #SolidLine, DashLine, DotLine, DashDotLine
      pen.setColor(Qt.blue)
      series1.setPen(pen)        #序列的线条设置

      self.__chart.addSeries(series0)
      self.__chart.addSeries(series1)

      axisX = QValueAxis()
      self.__curAxis=axisX       #当前坐标轴
      axisX.setRange(0, 10)      #设置坐标轴范围
      axisX.setLabelFormat("%.1f")  #标签格式
      axisX.setTickCount(11)        #主分隔个数
      axisX.setMinorTickCount(4)
      axisX.setTitleText("time(secs)")  #标题
      axisX.setGridLineVisible(True)
      axisX.setMinorGridLineVisible(False)

      axisY = QValueAxis()
      axisY.setRange(-2, 2)
      axisY.setLabelFormat("%.2f")     #标签格式
      axisY.setTickCount(5)
      axisY.setMinorTickCount(4)
      axisY.setTitleText("value")
      axisY.setGridLineVisible(True)
      axisY.setMinorGridLineVisible(False)

   ##      self.__chart.setAxisX(axisX, series0) #添加X坐标轴
   ##      self.__chart.setAxisX(axisX, series1) #添加X坐标轴
   ##      self.__chart.setAxisY(axisY, series0) #添加Y坐标轴
   ##      self.__chart.setAxisY(axisY, series1) #添加Y坐标轴

   ##另一种实现设置坐标轴的方法
      self.__chart.addAxis(axisX,Qt.AlignBottom) #坐标轴添加到图表,并指定方向
      self.__chart.addAxis(axisY,Qt.AlignLeft)

      series0.attachAxis(axisX)  #序列 series0 附加坐标轴
      series0.attachAxis(axisY)

      series1.attachAxis(axisX)  #序列 series1 附加坐标轴
      series1.attachAxis(axisY)

      

   def __prepareData(self):  ##为序列设置数据点
      chart=self.ui.chartView.chart()  #获取chartView中的QChart对象
      series0=chart.series()[0]        #获取第1个序列,QLineSeries
      series0.clear() 

      series1=chart.series()[1]   #获取第2个序列,QLineSeries
      series1.clear()
      
      t,y1,y2=0.0,0.0,0.0
      intv=0.1
      pointCount=100
      for i in range(pointCount):
         rd=random.randint(-5,5)     #随机数,-5~+5
         y1=math.sin(t)+rd/50.0
         series0.append(t,y1)        #序列添加数据点

         rd=random.randint(-5,5)     #随机数,-5~+5
         y2=math.cos(t)+rd/50.0
         series1.append(t,y2)        #序列添加数据点
         t=t+intv

   def __updateFromChart(self):
      self.ui.editTitle.setText(self.__chart.title())    #图表标题
      mg=self.__chart.margins()     #边距, QMargins
      self.ui.spinMarginLeft.setValue(mg.left())
      self.ui.spinMarginRight.setValue(mg.right())
      self.ui.spinMarginTop.setValue(mg.top())
      self.ui.spinMarginBottom.setValue(mg.bottom())
      
      
##  ==============event处理函数==========================


##  ==========由connectSlotsByName()自动连接的槽函数============        
      
##========工具栏上的几个按钮的Actions==============
   @pyqtSlot()  ## "刷新绘图" 工具栏按钮
   def on_actDraw_triggered(self):
      self.__prepareData()


##=====ToolBox 第1组:==="图表设置" 分组里的功能================
##=======1.1 标题========
   @pyqtSlot()   ##设置标题文字
   def on_btnTitleSetText_clicked(self):
      text=self.ui.editTitle.text()
      self.__chart.setTitle(text)
        
   @pyqtSlot()   ##设置标题文字颜色
   def on_btnTitleColor_clicked(self):
      color=self.__chart.titleBrush().color()
      color=QColorDialog.getColor(color)
      if color.isValid():
         self.__chart.setTitleBrush(QBrush(color))

   @pyqtSlot()   ##设置标题字体
   def on_btnTitleFont_clicked(self):
      iniFont=self.__chart.titleFont()  #QFont
      font,ok=QFontDialog.getFont(iniFont)
      if ok:
         self.__chart.setTitleFont(font)

##=======1.2 图例==========
   @pyqtSlot(bool)   ##图例是否可见
   def on_groupBox_Legend_clicked(self,checked):
      self.__chart.legend().setVisible(checked)

   @pyqtSlot()   ##图例的位置, 上
   def on_radioButton_clicked(self):
      self.__chart.legend().setAlignment(Qt.AlignTop)
         
   @pyqtSlot()   ##图例的位置,下
   def on_radioButton_2_clicked(self):
      self.__chart.legend().setAlignment(Qt.AlignBottom)
      
   @pyqtSlot()   ##图例的位置,左
   def on_radioButton_3_clicked(self):
      self.__chart.legend().setAlignment(Qt.AlignLeft)

   @pyqtSlot()   ##图例的位置,右
   def on_radioButton_4_clicked(self):
      self.__chart.legend().setAlignment(Qt.AlignRight)

   @pyqtSlot()   ##图例的文字颜色
   def on_btnLegendlabelColor_clicked(self):
      color=self.__chart.legend().labelColor()
      color=QColorDialog.getColor(color)
      if color.isValid():
         self.__chart.legend().setLabelColor(color)

   @pyqtSlot()   ##图例的字体
   def on_btnLegendFont_clicked(self):
      iniFont=self.__chart.legend().font()
      font,ok=QFontDialog.getFont(iniFont)
      if ok:
         self.__chart.legend().setFont(font)

##=======1.3 边距========
   @pyqtSlot()   ##设置图表的4个边距
   def on_btnSetMargin_clicked(self):
      mgs=QMargins()
      mgs.setLeft(self.ui.spinMarginLeft.value())
      mgs.setRight(self.ui.spinMarginRight.value())
      mgs.setTop(self.ui.spinMarginTop.value())
      mgs.setBottom(self.ui.spinMarginBottom.value())
      self.__chart.setMargins(mgs)

##=======1.4 动画效果========        
   @pyqtSlot(int)   ##动画效果
   def on_comboAnimation_currentIndexChanged(self,index):
      animation=QChart.AnimationOptions(index)
      self.__chart.setAnimationOptions(animation)

   @pyqtSlot(int)   ##图表的主题
   def on_comboTheme_currentIndexChanged(self,index):
      self.__chart.setTheme(QChart.ChartTheme(index))


##=====ToolBox 第2组:==="曲线设置" 分组里的功能================
##=======2.1 选择操作序列========
   @pyqtSlot()   ##获取当前数据序列,sin
   def on_radioSeries0_clicked(self):
      if self.ui.radioSeries0.isChecked():
         self.__curSeries=self.__chart.series()[0]
      else:
         self.__curSeries=self.__chart.series()[1]
      ##获取序列的属性值,并显示到界面上
      self.ui.editSeriesName.setText(self.__curSeries.name())
      self.ui.groupBox_Series.setChecked(self.__curSeries.isVisible())  #序列是否显示
      self.ui.chkBoxPointVisible.setChecked(self.__curSeries.pointsVisible())    #数据点是否显示
      self.ui.chkkBoxUseOpenGL.setChecked(self.__curSeries.useOpenGL()) #使用openGL

      self.ui.sliderOpacity.setValue(self.__curSeries.opacity()*10)  #透明度

      visible=self.__curSeries.pointLabelsVisible()   #数据点标签可见性
      self.ui.groupBox_PointLabel.setChecked(visible)

   @pyqtSlot()   ##获取当前数据序列,cos
   def on_radioSeries1_clicked(self):
      self.on_radioSeries0_clicked()

      
##======2.2 序列曲线 设置======== 
   @pyqtSlot(bool)   ##序列是否可见
   def on_groupBox_Series_clicked(self,checked):
      self.__curSeries.setVisible(checked)

   @pyqtSlot()   ##设置序列名称
   def on_btnSeriesName_clicked(self):
      seriesName=self.ui.editSeriesName.text()
      self.__curSeries.setName(seriesName)
      if self.ui.radioSeries0.isChecked():
         self.ui.radioSeries0.setText(seriesName)
      else:
         self.ui.radioSeries1.setText(seriesName)

   @pyqtSlot()   ##序列的曲线颜色
   def on_btnSeriesColor_clicked(self):
      color=self.__curSeries.color()
      color=QColorDialog.getColor(color)
      if color.isValid():
         self.__curSeries.setColor(color)

   @pyqtSlot()   ##序列曲线的Pen设置
   def on_btnSeriesPen_clicked(self):
      iniPen=self.__curSeries.pen()
      pen,ok=QmyDialogPen.staticGetPen(iniPen)
      if ok:
         self.__curSeries.setPen(pen)

   @pyqtSlot(bool)   ##序列的数据点是否可见,数据点形状是固定的
   def on_chkBoxPointVisible_clicked(self,checked):
      self.__curSeries.setPointsVisible(checked)

   @pyqtSlot(bool)   ##使用openGL加速后,不能设置线型,不能显示数据点
   def on_chkkBoxUseOpenGL_clicked(self,checked):
      self.__curSeries.setUseOpenGL(checked)

   @pyqtSlot(int)   ##序列的透明度
   def on_sliderOpacity_sliderMoved(self,position):
      self.__curSeries.setOpacity(position/10.0)

##=======2.3 数据点标签 ========     
   @pyqtSlot(bool)   ##数据点标签 groupBox
   def on_groupBox_PointLabel_clicked(self,checked):
      self.__curSeries.setPointLabelsVisible(checked)

   @pyqtSlot()   ##序列数据点标签颜色
   def on_btnSeriesLabColor_clicked(self):
      color=self.__curSeries.pointLabelsColor()
      color=QColorDialog.getColor(color)
      if color.isValid():
         self.__curSeries.setPointLabelsColor(color)

   @pyqtSlot()   ##序列数据点标签字体
   def on_btnSeriesLabFont_clicked(self):
      font=self.__curSeries.pointLabelsFont()    #QFont
      font,ok=QFontDialog.getFont(font)
      if ok:
         self.__curSeries.setPointLabelsFont(font)

   @pyqtSlot()   ##序列数据点标签的显示格式
   def on_radioSeriesLabFormat0_clicked(self):
      self.__curSeries.setPointLabelsFormat("@yPoint")
         
   @pyqtSlot()   ##序列数据点标签的显示格式
   def on_radioSeriesLabFormat1_clicked(self):
      self.__curSeries.setPointLabelsFormat("(@xPoint,@yPoint)")

##=====ToolBox 第3组:==="坐标轴设置" 分组里的功能================

##=======3.1 选择操作的坐标轴对象=======
   @pyqtSlot()   ##选择坐标轴X
   def on_radioAxisX_clicked(self):
      if (self.ui.radioAxisX.isChecked()):
         self.__curAxis=self.ui.chartView.chart().axisX()   #QValueAxis
      else:
         self.__curAxis=self.ui.chartView.chart().axisY()

      ##获取坐标轴的各种属性,显示到界面上
      self.ui.groupBox_Axis.setChecked(self.__curAxis.isVisible()) #坐标轴可见性

      self.ui.chkBoxAxisReverse.setChecked(self.__curAxis.isReverse())
         
      self.ui.spinAxisMin.setValue(self.__curAxis.min())
      self.ui.spinAxisMax.setValue(self.__curAxis.max())

      self.ui.editAxisTitle.setText(self.__curAxis.titleText())   #轴标题
      self.ui.groupBox_AxisTitle.setChecked(self.__curAxis.isTitleVisible())  #轴标题可见

      self.ui.editAxisLabelFormat.setText(self.__curAxis.labelFormat())       #标签格式
      self.ui.groupBox_AxisLabel.setChecked(self.__curAxis.labelsVisible())   #标签可见

      self.ui.groupBox_GridLine.setChecked(self.__curAxis.isGridLineVisible()) #网格线

      self.ui.groupBox_Ticks.setChecked(self.__curAxis.isLineVisible())    #主刻度线

      self.ui.spinTickCount.setValue(self.__curAxis.tickCount())           #主刻度个数

      self.ui.spinMinorTickCount.setValue(self.__curAxis.minorTickCount()) #次刻度个数
      self.ui.groupBox_MinorGrid.setChecked(self.__curAxis.isMinorGridLineVisible())   #次网格线可见


   @pyqtSlot()   ##选择坐标轴Y
   def on_radioAxisY_clicked(self):
      self.on_radioAxisX_clicked()

##======3.2 坐标轴可见性和范围=======
   @pyqtSlot(bool)   ##坐标轴可见性
   def on_groupBox_Axis_clicked(self,checked):
      self.__curAxis.setVisible(checked)

   @pyqtSlot(bool)   ##坐标反向
   def on_chkBoxAxisReverse_clicked(self,checked):
      self.__curAxis.setReverse(checked)
      
   @pyqtSlot()   ##设置坐标范围
   def on_btnSetAxisRange_clicked(self):
      minV=self.ui.spinAxisMin.value()
      maxV=self.ui.spinAxisMax.value()
      self.__curAxis.setRange(minV,maxV)
      
##======3.3 轴标题=======
   @pyqtSlot(bool)   ##坐标轴标题可见性
   def on_groupBox_AxisTitle_clicked(self,checked):
      self.__curAxis.setTitleVisible(checked)

   @pyqtSlot()   ##设置轴标题
   def on_btnAxisSetTitle_clicked(self):
      self.__curAxis.setTitleText(self.ui.editAxisTitle.text())

   @pyqtSlot()   ##设置轴标题的颜色
   def on_btnAxisTitleColor_clicked(self):
      color=self.__curAxis.titleBrush().color()
      color=QColorDialog.getColor(color)
      if color.isValid():
         self.__curAxis.setTitleBrush(QBrush(color))

   @pyqtSlot()   ##设置轴标题的字体
   def on_btnAxisTitleFont_clicked(self):
      iniFont=self.__curAxis.titleFont() #QFont
      font,ok=QFontDialog.getFont(iniFont)
      if ok:
         self.__curAxis.setTitleFont(font)

##======3.4 轴刻度标签=======
   @pyqtSlot(bool)   ##可见性
   def on_groupBox_AxisLabel_clicked(self,checked):
      self.__curAxis.setLabelsVisible(checked)
         
   @pyqtSlot()   ##设置标签格式
   def on_btnAxisLabelFormat_clicked(self):
      strFormat=self.ui.editAxisLabelFormat.text()
      self.__curAxis.setLabelFormat(strFormat)
      
   @pyqtSlot()   ##设置标签文字颜色
   def on_btnAxisLabelColor_clicked(self):
      color=self.__curAxis.labelsColor()
      color=QColorDialog.getColor(color)
      if color.isValid():
         self.__curAxis.setLabelsColor(color)

   @pyqtSlot()   ##设置标签字体
   def on_btnAxisLabelFont_clicked(self):
      iniFont=self.__curAxis.labelsFont() #QFont
      font,ok=QFontDialog.getFont(iniFont)
      if ok:
         self.__curAxis.setLabelsFont(font)

##======3.5 轴线和主刻度=========
   @pyqtSlot(bool)   ##可见性
   def on_groupBox_Ticks_clicked(self,checked):
      self.__curAxis.setLineVisible(checked)

   @pyqtSlot(int)   ##主刻度个数
   def on_spinTickCount_valueChanged(self,arg1):
      self.__curAxis.setTickCount(arg1)

   @pyqtSlot()   ##设置线条Pen
   def on_btnAxisLinePen_clicked(self):
      iniPen=self.__curAxis.linePen()
      pen,ok=QmyDialogPen.staticGetPen(iniPen)
      if ok:
         self.__curAxis.setLinePen(pen)
         
   @pyqtSlot()   ##设置线条颜色
   def on_btnAxisLinePenColor_clicked(self):
      color=self.__curAxis.linePenColor()
      color=QColorDialog.getColor(color)
      if color.isValid():
         self.__curAxis.setLinePenColor(color)
         
##======3.6 主网格线=========
   @pyqtSlot(bool)   ##可见性
   def on_groupBox_GridLine_clicked(self,checked):
      self.__curAxis.setGridLineVisible(checked)
         

   @pyqtSlot()   ##设置线条Pen
   def on_btnGridLinePen_clicked(self):
      iniPen=self.__curAxis.gridLinePen()
      pen,ok=QmyDialogPen.staticGetPen(iniPen)
      if ok:
         self.__curAxis.setGridLinePen(pen)

   @pyqtSlot()   ##设置线条颜色
   def on_btnGridLineColor_clicked(self):
      color=self.__curAxis.gridLineColor()
      color=QColorDialog.getColor(color)
      if color.isValid():
         self.__curAxis.setGridLineColor(color)

##======3.7 次网格线=======
   @pyqtSlot(bool)   ##可见性
   def on_groupBox_MinorGrid_clicked(self,checked):
      self.__curAxis.setMinorGridLineVisible(checked)

   @pyqtSlot(int)   ##次刻度个数
   def on_spinMinorTickCount_valueChanged(self,arg1):
      self.__curAxis.setMinorTickCount(arg1)
         
   @pyqtSlot()   ##设置线条Pen
   def on_btnMinorPen_clicked(self):
      iniPen=self.__curAxis.minorGridLinePen()
      pen,ok=QmyDialogPen.staticGetPen(iniPen)    #使用类函数调用方法
      if ok:
         self.__curAxis.setMinorGridLinePen(pen)
         
   @pyqtSlot()   ##设置线条颜色
   def on_btnMinorColor_clicked(self):
      color=self.__curAxis.minorGridLineColor()
      color=QColorDialog.getColor(color)
      if color.isValid():
         self.__curAxis.setMinorGridLineColor(color)
示例#6
0
class IMUChartView(QChartView, BaseGraph):
    def __init__(self, parent=None):
        super().__init__(parent=parent)

        # Render on OpenGL
        self.setViewport(QOpenGLWidget())

        self.xvalues = {}

        self.chart = QChart()

        self.setChart(self.chart)
        self.chart.legend().setVisible(True)
        self.chart.legend().setAlignment(Qt.AlignTop)
        self.ncurves = 0
        self.setRenderHint(QPainter.Antialiasing)

        # Cursor (with chart as parent)
        self.cursor = QGraphicsLineItem(self.chart)

        self.cursor.setZValue(100.0)
        # self.scene().addItem(self.cursor)

        # Selection features
        # self.setRubberBand(QChartView.HorizontalRubberBand)
        self.selectionBand = QRubberBand(QRubberBand.Rectangle, self)
        self.selecting = False
        self.initialClick = QPoint()

        # Track mouse
        self.setMouseTracking(True)

        self.labelValue = QLabel(self)
        self.labelValue.setStyleSheet(
            "background-color: rgba(255,255,255,75%); color: black;")
        self.labelValue.setAlignment(Qt.AlignCenter)
        self.labelValue.setMargin(5)
        self.labelValue.setVisible(False)

        self.build_style()

        self.selection_start_time = None
        self.selection_stop_time = None
        self.cursor_time = None

    def build_style(self):
        self.setStyleSheet("QLabel{color:blue;}")
        self.chart.setTheme(QChart.ChartThemeBlueCerulean)
        self.setBackgroundBrush(QBrush(Qt.darkGray))
        self.chart.setPlotAreaBackgroundBrush(QBrush(Qt.black))
        self.chart.setPlotAreaBackgroundVisible(True)

    def save_as_png(self, file_path):
        pixmap = self.grab()

        child = self.findChild(QOpenGLWidget)

        painter = QPainter(pixmap)
        if child is not None:
            d = child.mapToGlobal(QPoint()) - self.mapToGlobal(QPoint())
            painter.setCompositionMode(QPainter.CompositionMode_SourceAtop)
            painter.drawImage(d, child.grabFramebuffer())

        painter.end()
        pixmap.save(file_path, 'PNG')

    #  def closeEvent(self, event):
    #     self.aboutToClose.emit(self)

    @staticmethod
    def decimate(xdata, ydata):
        # assert(len(xdata) == len(ydata))

        # Decimate only if we have too much data
        decimate_factor = len(xdata) / 100000.0
        # decimate_factor = 1.0

        if decimate_factor > 1.0:
            decimate_factor = int(np.floor(decimate_factor))
            # print('decimate factor', decimate_factor)

            x = np.ndarray(int(len(xdata) / decimate_factor), dtype=np.float64)
            y = np.ndarray(int(len(ydata) / decimate_factor), dtype=np.float64)
            # for i in range(len(x)):
            for i, _ in enumerate(x):
                index = i * decimate_factor
                # assert(index < len(xdata))
                x[i] = xdata[index]
                y[i] = ydata[index]
                if x[i] < x[0]:
                    print('timestamp error', x[i], x[0])
            return x, y
        else:
            return xdata, ydata

    @pyqtSlot(float, float)
    def axis_range_changed(self, min_value, max_value):
        # print('axis_range_changed', min, max)
        for axis in self.chart.axes():
            axis.applyNiceNumbers()

    def update_axes(self):

        # Get and remove all axes
        for axis in self.chart.axes():
            self.chart.removeAxis(axis)

        # Create new axes
        # Create axis X
        axisx = QDateTimeAxis()
        axisx.setTickCount(5)
        axisx.setFormat("dd MMM yyyy hh:mm:ss")
        axisx.setTitleText("Date")
        self.chart.addAxis(axisx, Qt.AlignBottom)

        # Create axis Y
        axisY = QValueAxis()
        axisY.setTickCount(5)
        axisY.setLabelFormat("%.3f")
        axisY.setTitleText("Values")
        self.chart.addAxis(axisY, Qt.AlignLeft)
        # axisY.rangeChanged.connect(self.axis_range_changed)

        ymin = None
        ymax = None

        # Attach axes to series, find min-max
        for series in self.chart.series():
            series.attachAxis(axisx)
            series.attachAxis(axisY)
            vect = series.pointsVector()
            # for i in range(len(vect)):
            for i, _ in enumerate(vect):
                if ymin is None:
                    ymin = vect[i].y()
                    ymax = vect[i].y()
                else:
                    ymin = min(ymin, vect[i].y())
                    ymax = max(ymax, vect[i].y())

        # Update range
        # print('min max', ymin, ymax)
        if ymin is not None:
            axisY.setRange(ymin, ymax)

        # Make the X,Y axis more readable
        # axisx.applyNiceNumbers()
        # axisY.applyNiceNumbers()

    def add_data(self, xdata, ydata, color=None, legend_text=None):
        curve = QLineSeries()
        pen = curve.pen()
        if color is not None:
            pen.setColor(color)
        pen.setWidthF(1.5)

        curve.setPen(pen)
        # curve.setPointsVisible(True)

        # curve.setUseOpenGL(True)

        self.total_samples = max(self.total_samples, len(xdata))

        # Decimate
        xdecimated, ydecimated = self.decimate(xdata, ydata)

        # Data must be in ms since epoch
        # curve.append(self.series_to_polyline(xdecimated * 1000.0, ydecimated))
        # self.reftime = datetime.datetime.fromtimestamp(xdecimated[0])

        # if len(xdecimated) > 0:
        #    xdecimated = xdecimated - xdecimated[0]

        xdecimated *= 1000  # No decimal expected
        points = []
        for i, _ in enumerate(xdecimated):
            # TODO hack
            # curve.append(QPointF(xdecimated[i], ydecimated[i]))
            points.append(QPointF(xdecimated[i], ydecimated[i]))

        curve.replace(points)
        points.clear()

        if legend_text is not None:
            curve.setName(legend_text)

        # Needed for mouse events on series
        self.chart.setAcceptHoverEvents(True)
        self.xvalues[self.ncurves] = np.array(xdecimated)

        # connect signals / slots
        # curve.clicked.connect(self.lineseries_clicked)
        # curve.hovered.connect(self.lineseries_hovered)

        # Add series
        self.chart.addSeries(curve)
        self.ncurves += 1
        self.update_axes()

    def update_data(self, xdata, ydata, series_id):
        if series_id < len(self.chart.series()):
            # Find start time to replace data
            current_series = self.chart.series()[series_id]
            current_points = current_series.pointsVector()

            # Find start and end indexes
            start_index = -1
            try:
                start_index = current_points.index(
                    QPointF(xdata[0] * 1000, ydata[0]))  # Right on the value!
                # print("update_data: start_index found exact match.")
            except ValueError:
                # print("update_data: start_index no exact match - scanning deeper...")
                for i, value in enumerate(current_points):
                    # print(str(current_points[i].x()) + " == " + str(xdata[0]*1000))
                    if current_points[i].x() == xdata[0] * 1000 or (
                            i > 0 and current_points[i - 1].x() <
                            xdata[0] * 1000 < current_points[i].x()):
                        start_index = i
                        # print("update_data: start_index found approximative match.")
                        break

            end_index = -1
            try:
                end_index = current_points.index(
                    QPointF(xdata[len(xdata) - 1] * 1000,
                            ydata[len(ydata) - 1]))  # Right on!
                # print("update_data: start_index found exact match.")
            except ValueError:
                # print("update_data: start_index no exact match - scanning deeper...")
                for i, value in enumerate(current_points):
                    # print(str(current_points[i].x()) + " == " + str(xdata[0]*1000))
                    if current_points[i].x(
                    ) == xdata[len(xdata) - 1] * 1000 or (
                            i > 0 and
                            current_points[i - 1].x() < xdata[len(xdata) - 1] *
                            1000 < current_points[i].x()):
                        end_index = i
                        # print("update_data: start_index found approximative match.")
                        break

            if start_index < 0 or end_index < 0:
                return

            # Decimate, if needed
            xdata, ydata = self.decimate(xdata, ydata)

            # Check if we have the same number of points for that range. If not, remove and replace!
            target_points = current_points[start_index:end_index]
            if len(target_points) != len(xdata):
                points = []
                for i, value in enumerate(xdata):
                    # TODO improve
                    points.append(QPointF(value * 1000, ydata[i]))

                new_points = current_points[0:start_index] + points[0:len(points)-1] + \
                             current_points[end_index:len(current_points)-1]

                current_series.replace(new_points)
                new_points.clear()

                # self.xvalues[series_id] = np.array(xdata)

        return

    @classmethod
    def set_title(cls, title):
        # print('Setting title: ', title)
        # self.chart.setTitle(title)
        return

    @staticmethod
    def series_to_polyline(xdata, ydata):
        """Convert series data to QPolygon(F) polyline

        This code is derived from PythonQwt's function named
        `qwt.plot_curve.series_to_polyline`"""

        # print('series_to_polyline types:', type(xdata[0]), type(ydata[0]))
        size = len(xdata)
        polyline = QPolygonF(size)

        # for i in range(0, len(xdata)):
        #   polyline[i] = QPointF(xdata[i] - xdata[0], ydata[i])
        for i, data in enumerate(xdata):
            polyline[i] = QPointF(data - xdata[0], ydata[i])

        # pointer = polyline.data()
        # dtype, tinfo = np.float, np.finfo  # integers: = np.int, np.iinfo
        # pointer.setsize(2*polyline.size()*tinfo(dtype).dtype.itemsize)
        # memory = np.frombuffer(pointer, dtype)
        # memory[:(size-1)*2+1:2] = xdata
        # memory[1:(size-1)*2+2:2] = ydata
        return polyline

    def add_test_data(self):

        # 100Hz, one day accelerometer values
        npoints = 1000 * 60 * 24

        xdata = np.linspace(0., 10., npoints)
        self.add_data(xdata, np.sin(xdata), color=Qt.red, legend_text='Acc. X')
        # self.add_data(xdata, np.cos(xdata), color=Qt.green, legend_text='Acc. Y')
        # self.add_data(xdata, np.cos(2 * xdata), color=Qt.blue, legend_text='Acc. Z')
        self.set_title(
            "Simple example with %d curves of %d points (OpenGL Accelerated Series)"
            % (self.ncurves, npoints))

    def mouseMoveEvent(self, e: QMouseEvent):
        if self.selecting:

            clicked_x = max(
                self.chart.plotArea().x(),
                min(
                    self.mapToScene(e.pos()).x(),
                    self.chart.plotArea().x() + self.chart.plotArea().width()))

            clicked_y = max(
                self.chart.plotArea().y(),
                min(
                    self.mapToScene(e.pos()).y(),
                    self.chart.plotArea().y() +
                    self.chart.plotArea().height()))

            current_pos = QPoint(clicked_x, clicked_y)  # e.pos()

            self.selection_stop_time = self.chart.mapToValue(
                QPointF(clicked_x, 0)).x()

            self.setCursorPosition(clicked_x, True)

            if self.interaction_mode == GraphInteractionMode.SELECT:
                if current_pos.x() < self.initialClick.x():
                    start_x = current_pos.x()
                    width = self.initialClick.x() - start_x
                else:
                    start_x = self.initialClick.x()
                    width = current_pos.x() - self.initialClick.x()

                self.selectionBand.setGeometry(
                    QRect(start_x,
                          self.chart.plotArea().y(), width,
                          self.chart.plotArea().height()))
                if self.selection_rec:
                    self.selection_rec.setRect(
                        QRectF(start_x,
                               self.chart.plotArea().y(), width,
                               self.chart.plotArea().height()))

            if self.interaction_mode == GraphInteractionMode.MOVE:
                new_pos = current_pos - self.initialClick
                self.chart.scroll(-new_pos.x(), new_pos.y())
                self.initialClick = current_pos

    def mousePressEvent(self, e: QMouseEvent):
        # Handling rubberbands
        # super().mousePressEvent(e)
        # Verify if click is inside plot area
        # if self.chart.plotArea().contains(e.pos()):

        self.selecting = True
        clicked_x = max(
            self.chart.plotArea().x(),
            min(
                self.mapToScene(e.pos()).x(),
                self.chart.plotArea().x() + self.chart.plotArea().width()))

        clicked_y = max(
            self.chart.plotArea().y(),
            min(
                self.mapToScene(e.pos()).y(),
                self.chart.plotArea().y() + self.chart.plotArea().height()))

        self.initialClick = QPoint(clicked_x, clicked_y)  # e.pos()

        self.selection_start_time = self.chart.mapToValue(QPointF(
            clicked_x, 0)).x()

        self.setCursorPosition(clicked_x, True)

        if self.interaction_mode == GraphInteractionMode.SELECT:
            self.selectionBand.setGeometry(
                QRect(self.initialClick.x(),
                      self.chart.plotArea().y(), 1,
                      self.chart.plotArea().height()))
            self.selectionBand.show()

        if self.interaction_mode == GraphInteractionMode.MOVE:
            QGuiApplication.setOverrideCursor(Qt.ClosedHandCursor)
            self.labelValue.setVisible(False)

    def mouseReleaseEvent(self, e: QMouseEvent):

        # Assure if click is inside plot area
        # Handling rubberbands (with min / max x)
        clicked_x = max(
            self.chart.plotArea().x(),
            min(
                self.mapToScene(e.pos()).x(),
                self.chart.plotArea().x() + self.chart.plotArea().width()))

        if self.interaction_mode == GraphInteractionMode.SELECT:
            self.selectionBand.hide()
            if clicked_x != self.mapToScene(self.initialClick).x():
                mapped_x = self.mapToScene(self.initialClick).x()
                if self.initialClick.x() < clicked_x:
                    self.setSelectionArea(mapped_x, clicked_x, True)
                else:
                    self.setSelectionArea(clicked_x, mapped_x, True)

        if self.interaction_mode == GraphInteractionMode.MOVE:
            QGuiApplication.restoreOverrideCursor()

        self.selecting = False

    def clearSelectionArea(self, emit_signal=False):
        if self.selection_rec:
            self.scene().removeItem(self.selection_rec)
            self.selection_rec = None

        if emit_signal:
            self.clearedSelectionArea.emit()

    def setSelectionArea(self, start_pos, end_pos, emit_signal=False):
        selection_brush = QBrush(QColor(153, 204, 255, 128))
        selection_pen = QPen(Qt.transparent)

        if self.selection_rec:
            self.scene().removeItem(self.selection_rec)

        self.selection_rec = self.scene().addRect(
            start_pos,
            self.chart.plotArea().y(), end_pos - start_pos,
            self.chart.plotArea().height(), selection_pen, selection_brush)
        if emit_signal:
            self.selectedAreaChanged.emit(
                self.chart.mapToValue(QPointF(start_pos, 0)).x(),
                self.chart.mapToValue(QPointF(end_pos, 0)).x())

    def setSelectionAreaFromTime(self,
                                 start_time,
                                 end_time,
                                 emit_signal=False):
        # Convert times to x values
        if isinstance(start_time, datetime.datetime):
            start_time = start_time.timestamp() * 1000
        if isinstance(end_time, datetime.datetime):
            end_time = end_time.timestamp() * 1000

        start_pos = self.chart.mapToPosition(QPointF(start_time, 0)).x()
        end_pos = self.chart.mapToPosition(QPointF(end_time, 0)).x()

        self.setSelectionArea(start_pos, end_pos)

    def setCursorPosition(self, pos, emit_signal=False):

        self.cursor_time = self.chart.mapToValue(QPointF(pos, 0)).x()

        # print (pos)
        pen = self.cursor.pen()
        pen.setColor(Qt.cyan)
        pen.setWidthF(1.0)
        self.cursor.setPen(pen)
        # On Top
        self.cursor.setZValue(100.0)

        area = self.chart.plotArea()
        x = pos
        y1 = area.y()
        y2 = area.y() + area.height()

        # self.cursor.set
        self.cursor.setLine(x, y1, x, y2)
        self.cursor.show()

        xmap_initial = self.chart.mapToValue(QPointF(pos, 0)).x()
        display = ''
        # '<i>' + (datetime.datetime.fromtimestamp(xmap + self.reftime.timestamp())).strftime('%d-%m-%Y %H:%M:%S') +
        # '</i><br />'
        ypos = 10
        last_val = None
        for i in range(self.ncurves):
            # Find nearest point
            idx = (np.abs(self.xvalues[i] - xmap_initial)).argmin()
            ymap = self.chart.series()[i].at(idx).y()
            xmap = self.chart.series()[i].at(idx).x()
            if i == 0:
                display += "<i>" + (datetime.datetime.fromtimestamp(xmap_initial/1000)).strftime('%d-%m-%Y %H:%M:%S:%f') + \
                           "</i>"

            # Compute where to display label
            if last_val is None or ymap > last_val:
                last_val = ymap
                ypos = self.chart.mapToPosition(QPointF(xmap, ymap)).y()
            if display != '':
                display += '<br />'

            display += self.chart.series()[i].name(
            ) + ': <b>' + '%.3f' % ymap + '</b>'

        self.labelValue.setText(display)
        self.labelValue.setGeometry(pos, ypos, 100, 100)
        self.labelValue.adjustSize()
        self.labelValue.setVisible(True)

        if emit_signal:
            self.cursorMoved.emit(xmap_initial)

        self.update()

    def setCursorPositionFromTime(self, timestamp, emit_signal=False):
        # Find nearest point
        if isinstance(timestamp, datetime.datetime):
            timestamp = timestamp.timestamp()
        pos = self.get_pos_from_time(timestamp)
        self.setCursorPosition(pos, emit_signal)

    def get_pos_from_time(self, timestamp):
        px = timestamp
        idx1 = (np.abs(self.xvalues[0] - px)).argmin()
        x1 = self.chart.series()[0].at(idx1).x()
        pos1 = self.chart.mapToPosition(QPointF(x1, 0)).x()
        idx2 = idx1 + 1
        if idx2 < len(self.chart.series()[0]):
            x2 = self.chart.series()[0].at(idx2).x()
            if x2 != x1:
                pos2 = self.chart.mapToPosition(QPointF(x2, 0)).x()
                x2 /= 1000
                x1 /= 1000
                pos = (((px - x1) / (x2 - x1)) * (pos2 - pos1)) + pos1
            else:
                pos = pos1
        else:
            pos = pos1
        return pos

    def resizeEvent(self, e: QResizeEvent):
        super().resizeEvent(e)

        # oldSize = e.oldSize()
        # newSize = e.size()

        # Update cursor from time
        if self.cursor_time:
            self.setCursorPositionFromTime(self.cursor_time / 1000.0)

        # Update selection
        if self.selection_rec:
            self.setSelectionAreaFromTime(self.selection_start_time,
                                          self.selection_stop_time)

    def zoom_in(self):
        self.chart.zoomIn()
        self.update_axes()

    def zoom_out(self):
        self.chart.zoomOut()
        self.update_axes()

    def zoom_area(self):
        if self.selection_rec:
            zoom_rec = self.selection_rec.rect()
            zoom_rec.setY(0)
            zoom_rec.setHeight(self.chart.plotArea().height())
            self.chart.zoomIn(zoom_rec)
            self.clearSelectionArea(True)
            self.update_axes()

    def zoom_reset(self):
        self.chart.zoomReset()
        self.update_axes()

    def get_displayed_start_time(self):
        min_x = self.chart.mapToScene(self.chart.plotArea()).boundingRect().x()
        xmap = self.chart.mapToValue(QPointF(min_x, 0)).x()
        return datetime.datetime.fromtimestamp(xmap / 1000)

    def get_displayed_end_time(self):
        max_x = self.chart.mapToScene(self.chart.plotArea()).boundingRect().x()
        max_x += self.chart.mapToScene(
            self.chart.plotArea()).boundingRect().width()
        xmap = self.chart.mapToValue(QPointF(max_x, 0)).x()
        return datetime.datetime.fromtimestamp(xmap / 1000)

    @property
    def is_zoomed(self):
        return self.chart.isZoomed()
示例#7
0
class IMUChartView(QChartView):

    aboutToClose = pyqtSignal(QObject)
    cursorMoved = pyqtSignal(datetime.datetime)

    def __init__(self, parent=None):
        super(QChartView, self).__init__(parent=parent)

        #self.setFixedHeight(400)
        #self.setMinimumHeight(500)
        """self.setMaximumHeight(700)
        self.setFixedHeight(700)
        self.setMinimumWidth(1500)
        self.setSizePolicy(QSizePolicy.Fixed,QSizePolicy.Fixed)"""

        self.reftime = datetime.datetime.now()
        self.cursor = QGraphicsLineItem()
        self.scene().addItem(self.cursor)
        self.decim_factor = 1

        # self.setScene(QGraphicsScene())
        self.chart = QChart()
        # self.scene().addItem(self.chart)
        self.setChart(self.chart)
        self.chart.legend().setVisible(True)
        self.chart.legend().setAlignment(Qt.AlignTop)
        self.ncurves = 0
        self.setRenderHint(QPainter.Antialiasing)
        self.setRubberBand(QChartView.HorizontalRubberBand)

        # X, Y label on bottom
        # self.xTextItem = QGraphicsSimpleTextItem(self.chart)
        # self.xTextItem.setText('X: ')
        # self.yTextItem = QGraphicsSimpleTextItem(self.chart)
        # self.yTextItem.setText('Y: ')
        # self.update_x_y_coords()

        # Track mouse
        self.setMouseTracking(True)

        # Top Widgets
        newWidget = QWidget(self)
        newLayout = QHBoxLayout()
        newLayout.setContentsMargins(0, 0, 0, 0)
        newWidget.setLayout(newLayout)
        #labelx = QLabel(self)
        #labelx.setText('X:')
        #self.labelXValue = QLabel(self)
        #labely = QLabel(self)
        #labely.setText('Y:')
        #self.labelYValue = QLabel(self)

        # Test buttons
        #newLayout.addWidget(QToolButton(self))
        #newLayout.addWidget(QToolButton(self))
        #newLayout.addWidget(QToolButton(self))

        # Spacer
        #newLayout.addItem(QSpacerItem(10, 10, QSizePolicy.Expanding, QSizePolicy.Minimum))

        # Labels
        """newLayout.addWidget(labelx)
        newLayout.addWidget(self.labelXValue)
        self.labelXValue.setMinimumWidth(200)
        self.labelXValue.setMaximumWidth(200)
        newLayout.addWidget(labely)
        newLayout.addWidget(self.labelYValue)
        self.labelYValue.setMinimumWidth(200)
        self.labelYValue.setMaximumWidth(200)
        """
        """if parent is not None:
            parent.layout().setMenuBar(newWidget)
        """
        # self.layout()

        self.build_style()

    def build_style(self):
        self.setStyleSheet("QLabel{color:blue;}")

        self.setBackgroundBrush(QBrush(Qt.darkGray))
        self.chart.setPlotAreaBackgroundBrush(QBrush(Qt.black))
        self.chart.setPlotAreaBackgroundVisible(True)

    def save_as_png(self, file_path):
        pixmap = self.grab()

        child = self.findChild(QOpenGLWidget)

        painter = QPainter(pixmap)
        if child is not None:
            d = child.mapToGlobal(QPoint()) - self.mapToGlobal(QPoint())
            painter.setCompositionMode(QPainter.CompositionMode_SourceAtop)
            painter.drawImage(d, child.grabFramebuffer())

        painter.end()
        pixmap.save(file_path, 'PNG')

    def closeEvent(self, QCloseEvent):
        self.aboutToClose.emit(self)

    @pyqtSlot(QPointF)
    def lineseries_clicked(self, point):
        print('lineseries clicked', point)

    @pyqtSlot(QPointF)
    def lineseries_hovered(self, point):
        print('lineseries hovered', point)

    def update_x_y_coords(self):
        pass
        # self.xTextItem.setPos(self.chart.size().width() / 2 - 100, self.chart.size().height() - 40)
        # self.yTextItem.setPos(self.chart.size().width() / 2 + 100, self.chart.size().height() - 40)

    def decimate(self, xdata, ydata):
        assert (len(xdata) == len(ydata))

        # Decimate only if we have too much data
        decimate_factor = len(xdata) / 100000.0

        if decimate_factor > 1.0:
            decimate_factor = int(np.floor(decimate_factor))
            #print('decimate factor', decimate_factor)
            # x = decimate(xdata, decimate_factor)
            # y = decimate(ydata, decimate_factor)
            self.decim_factor = decimate_factor
            x = np.ndarray(int(len(xdata) / decimate_factor), dtype=np.float64)
            y = np.ndarray(int(len(ydata) / decimate_factor), dtype=np.float64)
            for i in range(len(x)):
                index = i * decimate_factor
                assert (index < len(xdata))
                x[i] = xdata[index]
                y[i] = ydata[index]
                if x[i] < x[0]:
                    print('timestamp error', x[i], x[0])

            #print('return size', len(x), len(y), 'timestamp', x[0])
            return x, y
        else:
            return xdata, ydata

    @pyqtSlot(float, float)
    def axis_range_changed(self, min, max):
        #print('axis_range_changed', min, max)
        for axis in self.chart.axes():
            axis.applyNiceNumbers()

    def update_axes(self):

        # Get and remove all axes
        for axis in self.chart.axes():
            self.chart.removeAxis(axis)

        # Create new axes
        # Create axis X
        # axisX = QDateTimeAxis()
        # axisX.setTickCount(5)
        # axisX.setFormat("dd MMM yyyy")
        # axisX.setTitleText("Date")
        # self.chart.addAxis(axisX, Qt.AlignBottom)
        # axisX.rangeChanged.connect(self.axis_range_changed)

        axisX = QValueAxis()
        axisX.setTickCount(10)
        axisX.setLabelFormat("%li")
        axisX.setTitleText("Seconds")
        self.chart.addAxis(axisX, Qt.AlignBottom)
        # axisX.rangeChanged.connect(self.axis_range_changed)

        # Create axis Y
        axisY = QValueAxis()
        axisY.setTickCount(5)
        axisY.setLabelFormat("%.3f")
        axisY.setTitleText("Values")
        self.chart.addAxis(axisY, Qt.AlignLeft)
        # axisY.rangeChanged.connect(self.axis_range_changed)

        ymin = None
        ymax = None

        # Attach axes to series, find min-max
        for series in self.chart.series():
            series.attachAxis(axisX)
            series.attachAxis(axisY)
            vect = series.pointsVector()
            for i in range(len(vect)):
                if ymin is None:
                    ymin = vect[i].y()
                    ymax = vect[i].y()
                else:
                    ymin = min(ymin, vect[i].y())
                    ymax = max(ymax, vect[i].y())

        # Update range
        # print('min max', ymin, ymax)
        if ymin is not None:
            axisY.setRange(ymin, ymax)

        # Make the X,Y axis more readable
        axisX.applyNiceNumbers()
        # axisY.applyNiceNumbers()

    def add_data(self, xdata, ydata, color=None, legend_text=None):
        curve = QLineSeries()
        pen = curve.pen()
        if color is not None:
            pen.setColor(color)
        pen.setWidthF(1.5)
        curve.setPen(pen)

        #curve.setUseOpenGL(True)

        # Decimate
        xdecimated, ydecimated = self.decimate(xdata, ydata)

        # Data must be in ms since epoch
        # curve.append(self.series_to_polyline(xdecimated * 1000.0, ydecimated))
        for i in range(len(xdecimated)):
            # TODO hack
            x = xdecimated[i] - xdecimated[0]
            curve.append(QPointF(x, ydecimated[i]))

        self.reftime = datetime.datetime.fromtimestamp(xdecimated[0])

        if legend_text is not None:
            curve.setName(legend_text)

        # Needed for mouse events on series
        self.chart.setAcceptHoverEvents(True)

        # connect signals / slots
        # curve.clicked.connect(self.lineseries_clicked)
        # curve.hovered.connect(self.lineseries_hovered)

        # Add series
        self.chart.addSeries(curve)
        self.ncurves += 1
        self.update_axes()

    def set_title(self, title):
        # print('Setting title: ', title)
        #self.chart.setTitle(title)
        pass

    def series_to_polyline(self, xdata, ydata):
        """Convert series data to QPolygon(F) polyline

        This code is derived from PythonQwt's function named
        `qwt.plot_curve.series_to_polyline`"""

        # print('series_to_polyline types:', type(xdata[0]), type(ydata[0]))
        size = len(xdata)
        polyline = QPolygonF(size)

        for i in range(0, len(xdata)):
            polyline[i] = QPointF(xdata[i] - xdata[0], ydata[i])

        # pointer = polyline.data()
        # dtype, tinfo = np.float, np.finfo  # integers: = np.int, np.iinfo
        # pointer.setsize(2*polyline.size()*tinfo(dtype).dtype.itemsize)
        # memory = np.frombuffer(pointer, dtype)
        # memory[:(size-1)*2+1:2] = xdata
        # memory[1:(size-1)*2+2:2] = ydata
        return polyline

    def add_test_data(self):

        # 100Hz, one day accelerometer values
        npoints = 1000 * 60 * 24

        xdata = np.linspace(0., 10., npoints)
        self.add_data(xdata, np.sin(xdata), color=Qt.red, legend_text='Acc. X')
        # self.add_data(xdata, np.cos(xdata), color=Qt.green, legend_text='Acc. Y')
        # self.add_data(xdata, np.cos(2 * xdata), color=Qt.blue, legend_text='Acc. Z')
        self.set_title("Simple example with %d curves of %d points " \
                          "(OpenGL Accelerated Series)" \
                          % (self.ncurves, npoints))

    def mouseMoveEvent(self, e: QMouseEvent):
        # Handling rubberbands
        super().mouseMoveEvent(e)

        # Go back to seconds (instead of ms)
        """xmap = self.chart.mapToValue(e.pos()).x()
        ymap = self.chart.mapToValue(e.pos()).y()

        self.labelXValue.setText(str(datetime.datetime.fromtimestamp(xmap + self.reftime.timestamp())))
        self.labelYValue.setText(str(ymap))"""

        # self.xTextItem.setText('X: ' + str(datetime.datetime.fromtimestamp(xmap + self.reftime.timestamp())))
        # self.yTextItem.setText('Y: ' + str(ymap))

    def mousePressEvent(self, e: QMouseEvent):
        # Handling rubberbands
        super().mousePressEvent(e)

        self.setCursorPosition(e.pos().x(), True)

        pass

    def setCursorPosition(self, pos, emit_signal=False):
        # print (pos)
        pen = self.cursor.pen()
        pen.setColor(Qt.cyan)
        pen.setWidthF(1.0)
        self.cursor.setPen(pen)
        # On Top
        self.cursor.setZValue(100.0)

        area = self.chart.plotArea()
        x = pos
        y1 = area.y()
        y2 = area.y() + area.height()

        # self.cursor.set
        self.cursor.setLine(x, y1, x, y2)
        self.cursor.show()

        xmap = self.chart.mapToValue(QPointF(pos, 0)).x()
        ymap = self.chart.mapToValue(QPointF(pos, 0)).y()

        #self.labelXValue.setText(str(datetime.datetime.fromtimestamp(xmap + self.reftime.timestamp())))
        #self.labelYValue.setText(str(ymap))

        if emit_signal:
            self.cursorMoved.emit(
                datetime.datetime.fromtimestamp(xmap +
                                                self.reftime.timestamp()))

        self.update()

    def setCursorPositionFromTime(self, timestamp, emit_signal=False):
        # Converts timestamp to x value
        pos = self.chart.mapToPosition(
            QPointF((timestamp - self.reftime).total_seconds(), 0)).x()
        self.setCursorPosition(pos, emit_signal)

    def mouseReleaseEvent(self, e: QMouseEvent):
        # Handling rubberbands
        super().mouseReleaseEvent(e)
        pass

    def resizeEvent(self, e: QResizeEvent):
        super().resizeEvent(e)

        # Update cursor height
        area = self.chart.plotArea()
        line = self.cursor.line()
        self.cursor.setLine(line.x1(), area.y(), line.x2(),
                            area.y() + area.height())

        # self.scene().setSceneRect(0, 0, e.size().width(), e.size().height())
        # Need to reposition X,Y labels
        self.update_x_y_coords()
示例#8
0
class QmyMainWindow(QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)  #调用父类构造函数,创建窗体
        self.ui = Ui_MainWindow()  #创建UI对象
        self.ui.setupUi(self)  #构造UI界面

        self.setWindowTitle("Demo12_4, 对数坐标轴和多坐标轴")
        self.__buildStatusBar()
        self.ui.frameSetup.setEnabled(False)  #禁用控制面板

        ## 创建QmyChartView对象,并添加到窗口上
        self.ui.chartView.setRenderHint(QPainter.Antialiasing)
        self.ui.chartView.setCursor(Qt.CrossCursor)  #设置鼠标指针为十字星
        self.__iniChart()  # 创建self.chart

##  ==============自定义功能函数========================

    def __buildStatusBar(self):  ##构建状态栏
        self.__labMagXY = QLabel("幅频曲线,")
        self.__labMagXY.setMinimumWidth(250)
        self.ui.statusBar.addWidget(self.__labMagXY)

        self.__labPhaseXY = QLabel("相频曲线,")
        self.__labPhaseXY.setMinimumWidth(250)
        self.ui.statusBar.addWidget(self.__labPhaseXY)

    def __iniChart(self):  ##图表初始化
        self.chart = QChart()  #创建 chart
        self.chart.setTitle("二阶系统频率特性")
        self.chart.legend().setVisible(True)
        self.ui.chartView.setChart(self.chart)  #chart添加到chartView

        ##2. 创建坐标轴
        ## bottom 轴是 QLogValueAxis
        self.__axisFreq = QLogValueAxis()
        self.__axisFreq.setLabelFormat("%.1f")  #标签格式
        self.__axisFreq.setTitleText("角频率(rad/sec)")
        self.__axisFreq.setRange(0.1, 100)
        self.__axisFreq.setMinorTickCount(9)
        self.chart.addAxis(self.__axisFreq, Qt.AlignBottom)

        ## left 轴是 QValueAxis
        self.__axisMag = QValueAxis()
        self.__axisMag.setTitleText("幅度(dB)")
        self.__axisMag.setRange(-40, 10)
        self.__axisMag.setTickCount(6)
        self.__axisMag.setLabelFormat("%.1f")  #标签格式
        self.chart.addAxis(self.__axisMag, Qt.AlignLeft)

        ## right 轴是 QValueAxis
        self.__axisPhase = QValueAxis()
        self.__axisPhase.setTitleText("相位(度)")
        self.__axisPhase.setRange(-200, 0)
        self.__axisPhase.setTickCount(6)
        self.__axisPhase.setLabelFormat("%.0f")  #标签格式
        self.chart.addAxis(self.__axisPhase, Qt.AlignRight)

    def __loadData(self, allLines):  ##从字符串列表读取数据
        rowCnt = len(allLines)  #文本行数
        self.__vectW = [0] * rowCnt
        self.__vectMag = [0] * rowCnt
        self.__VectPhase = [0] * rowCnt

        for i in range(rowCnt):
            lineText = allLines[i].strip()  #一行的文字,必须去掉末尾的\n
            strList = lineText.split()  #分割为字符串列表
            self.__vectW[i] = float(strList[0])  #频率
            self.__vectMag[i] = float(strList[1])  #幅度
            self.__VectPhase[i] = float(strList[2])  #相位

    def __drawBode(self):
        self.chart.removeAllSeries()  #删除所有序列
        ## 创建序列
        pen = QPen(Qt.red)
        pen.setWidth(2)

        seriesMag = QLineSeries()  #幅频曲线序列
        seriesMag.setName("幅频曲线")
        seriesMag.setPen(pen)
        seriesMag.setPointsVisible(False)
        seriesMag.hovered.connect(self.do_seriesMag_hovered)

        seriesPhase = QLineSeries()  #相频曲线序列
        pen.setColor(Qt.blue)
        seriesPhase.setName("相频曲线")
        seriesPhase.setPen(pen)
        seriesPhase.setPointsVisible(True)
        seriesPhase.hovered.connect(self.do_seriesPhase_hovered)

        ## 为序列添加数据点
        count = len(self.__vectW)  #数据点数
        for i in range(count):
            seriesMag.append(self.__vectW[i], self.__vectMag[i])
            seriesPhase.append(self.__vectW[i], self.__VectPhase[i])

    ##设置坐标轴范围
        minMag = min(self.__vectMag)
        maxMag = max(self.__vectMag)
        minPh = min(self.__VectPhase)
        maxPh = max(self.__VectPhase)
        self.__axisMag.setRange(minMag, maxMag)
        self.__axisPhase.setRange(minPh, maxPh)

        ##序列添加到chart,并指定坐标轴
        self.chart.addSeries(seriesMag)
        seriesMag.attachAxis(self.__axisFreq)
        seriesMag.attachAxis(self.__axisMag)

        self.chart.addSeries(seriesPhase)
        seriesPhase.attachAxis(self.__axisFreq)
        seriesPhase.attachAxis(self.__axisPhase)

        for marker in self.chart.legend().markers():  #QLegendMarker类型列表
            marker.clicked.connect(self.do_LegendMarkerClicked)

##  ==============event处理函数==========================

##  ==========由connectSlotsByName()自动连接的槽函数============

    @pyqtSlot()  ##打开数据
    def on_actOpen_triggered(self):
        curPath = QDir.currentPath()  #获取当前路径
        filename, flt = QFileDialog.getOpenFileName(
            self, "打开一个文件", curPath, "频率响应数据文件(*.txt);;所有文件(*.*)")
        if (filename == ""):
            return

        aFile = open(filename, 'r')
        allLines = aFile.readlines()  #读取所有行,list类型,每行末尾带有 \n
        aFile.close()
        fileInfo = QFileInfo(filename)
        QDir.setCurrent(fileInfo.absolutePath())

        self.__loadData(allLines)  #解析数据
        self.__drawBode()  #绘制幅频曲线和相频曲线
        self.ui.frameSetup.setEnabled(True)

    @pyqtSlot()  ##放大
    def on_actZoomIn_triggered(self):
        self.ui.chartView.chart().zoom(1.2)

    @pyqtSlot()  ##缩小
    def on_actZoomOut_triggered(self):
        self.ui.chartView.chart().zoom(0.8)

    @pyqtSlot()  ##原始大小
    def on_actZoomReset_triggered(self):
        self.ui.chartView.chart().zoomReset()

##图表外观控制

    @pyqtSlot(int)  ##主题
    def on_comboTheme_currentIndexChanged(self, index):
        self.ui.chartView.chart().setTheme(QChart.ChartTheme(index))

    @pyqtSlot(bool)  ##显示图例
    def on_chkBox_Legend_clicked(self, checked):
        self.ui.chartView.chart().legend().setVisible(checked)

## 幅频曲线设置

    @pyqtSlot()  ##设置坐标范围
    def on_btnMag_SetRange_clicked(self):
        self.__axisMag.setRange(self.ui.spinMag_Min.value(),
                                self.ui.spinMag_Max.value())

    @pyqtSlot(int)  ##分度数
    def on_spinMag_Ticks_valueChanged(self, arg1):
        self.__axisMag.setTickCount(arg1)

    @pyqtSlot(bool)  ##显示数据点
    def on_chkBoxMag_Point_clicked(self, checked):
        seriesMag = self.chart.series()[0]
        seriesMag.setPointsVisible(checked)

## 相频曲线设置

    @pyqtSlot()  ##设置坐标范围
    def on_btnPh_SetRange_clicked(self):
        self.__axisPhase.setRange(self.ui.spinPh_Min.value(),
                                  self.ui.spinPh_Max.value())

    @pyqtSlot(int)  ##分度数
    def on_spinPh_Ticks_valueChanged(self, arg1):
        self.__axisPhase.setTickCount(arg1)

    @pyqtSlot(bool)  ##显示数据点
    def on_chkBoxPh_Point_clicked(self, checked):
        seriesPhase = self.chart.series()[1]
        seriesPhase.setPointsVisible(checked)

## 频率坐标轴

    @pyqtSlot()  ##设置坐标范围
    def on_btnX_SetRange_clicked(self):
        self.__axisFreq.setRange(self.ui.spinX_Min.value(),
                                 self.ui.spinX_Max.value())

    @pyqtSlot(int)  ##次分度数
    def on_spinX_MinorTicks_valueChanged(self, arg1):
        self.__axisFreq.setMinorTickCount(arg1)

##  =============自定义槽函数===============================

    def do_LegendMarkerClicked(self):
        marker = self.sender()  #QLegendMarker
        if (marker.type() != QLegendMarker.LegendMarkerTypeXY):
            return

        marker.series().setVisible(not marker.series().isVisible())
        marker.setVisible(True)
        alpha = 1.0
        if not marker.series().isVisible():
            alpha = 0.5

        brush = marker.labelBrush()  #QBrush
        color = brush.color()  #QColor
        color.setAlphaF(alpha)
        brush.setColor(color)
        marker.setLabelBrush(brush)

        brush = marker.brush()
        color = brush.color()
        color.setAlphaF(alpha)
        brush.setColor(color)
        marker.setBrush(brush)

        pen = marker.pen()  #QPen
        color = pen.color()
        color.setAlphaF(alpha)
        pen.setColor(color)
        marker.setPen(pen)

    def do_seriesMag_hovered(self, point, state):
        if state:
            hint = "幅频曲线:频率=%.1f, 幅度=%.1f dB" % (point.x(), point.y())
            self.__labMagXY.setText(hint)
##      else:
##         self.__labMagXY.setText("幅频曲线")

    def do_seriesPhase_hovered(self, point, state):
        if state:
            hint = "相频曲线:频率=%.1f, 相位=%.1f 度" % (point.x(), point.y())
            self.__labPhaseXY.setText(hint)
示例#9
0
class RTTView(QWidget):
    def __init__(self, parent=None):
        super(RTTView, self).__init__(parent)

        uic.loadUi('RTTView.ui', self)

        self.initSetting()

        self.initQwtPlot()

        self.rcvbuff = ''

        self.daplink = None

        self.tmrRTT = QtCore.QTimer()
        self.tmrRTT.setInterval(10)
        self.tmrRTT.timeout.connect(self.on_tmrRTT_timeout)
        self.tmrRTT.start()

        self.tmrRTT_Cnt = 0

    def initSetting(self):
        if not os.path.exists('setting.ini'):
            open('setting.ini', 'w', encoding='utf-8')

        self.conf = configparser.ConfigParser()
        self.conf.read('setting.ini', encoding='utf-8')

        if not self.conf.has_section('Memory'):
            self.conf.add_section('Memory')
            self.conf.set('Memory', 'StartAddr', '0x20000000')

    def initQwtPlot(self):
        self.PlotData = [[0] * 1000 for i in range(N_CURVES)]
        self.PlotPoint = [[QtCore.QPointF(j, 0) for j in range(1000)]
                          for i in range(N_CURVES)]

        self.PlotChart = QChart()

        self.ChartView = QChartView(self.PlotChart)
        self.ChartView.setVisible(False)
        self.vLayout.insertWidget(0, self.ChartView)

        self.PlotCurve = [QLineSeries() for i in range(N_CURVES)]

    @pyqtSlot()
    def on_btnOpen_clicked(self):
        if self.btnOpen.text() == u'打开连接':
            try:
                self.daplink = self.daplinks[self.cmbDAP.currentText()]
                self.daplink.open()

                _dp = dap.DebugPort(self.daplink, None)
                _dp.init()
                _dp.power_up_debug()

                _ap = ap.AHB_AP(_dp, 0)
                _ap.init()

                self.dap = cortex_m.CortexM(None, _ap)

                Addr = int(self.conf.get('Memory', 'StartAddr'), 16)
                for i in range(256):
                    buff = self.dap.read_memory_block8(Addr + 1024 * i, 1024)
                    buff = ''.join([chr(x) for x in buff])
                    index = buff.find('SEGGER RTT')
                    if index != -1:
                        self.RTTAddr = Addr + 1024 * i + index

                        buff = self.dap.read_memory_block8(
                            self.RTTAddr, ctypes.sizeof(SEGGER_RTT_CB))

                        rtt_cb = SEGGER_RTT_CB.from_buffer(bytearray(buff))
                        self.aUpAddr = self.RTTAddr + 16 + 4 + 4
                        self.aDownAddr = self.aUpAddr + ctypes.sizeof(
                            RingBuffer) * rtt_cb.MaxNumUpBuffers

                        self.txtMain.append(
                            '\n_SEGGER_RTT @ 0x%08X with %d aUp and %d aDown\n'
                            % (self.RTTAddr, rtt_cb.MaxNumUpBuffers,
                               rtt_cb.MaxNumDownBuffers))
                        break
                else:
                    raise Exception('Can not find _SEGGER_RTT')
            except Exception as e:
                self.txtMain.append(f'\n{str(e)}\n')
            else:
                self.cmbDAP.setEnabled(False)
                self.btnOpen.setText(u'关闭连接')
        else:
            self.daplink.close()
            self.cmbDAP.setEnabled(True)
            self.btnOpen.setText(u'打开连接')

    def aUpRead(self):
        buf = self.dap.read_memory_block8(self.aUpAddr,
                                          ctypes.sizeof(RingBuffer))
        aUp = RingBuffer.from_buffer(bytearray(buf))

        if aUp.RdOff == aUp.WrOff:
            buf = []

        elif aUp.RdOff < aUp.WrOff:
            cnt = aUp.WrOff - aUp.RdOff
            buf = self.dap.read_memory_block8(
                ctypes.cast(aUp.pBuffer, ctypes.c_void_p).value + aUp.RdOff,
                cnt)

            aUp.RdOff += cnt

            self.dap.write32(self.aUpAddr + 4 * 4, aUp.RdOff)

        else:
            cnt = aUp.SizeOfBuffer - aUp.RdOff
            buf = self.dap.read_memory_block8(
                ctypes.cast(aUp.pBuffer, ctypes.c_void_p).value + aUp.RdOff,
                cnt)

            aUp.RdOff = 0  #这样下次再读就会进入执行上个条件

            self.dap.write32(self.aUpAddr + 4 * 4, aUp.RdOff)

        return ''.join([chr(x) for x in buf])

    def on_tmrRTT_timeout(self):
        if self.btnOpen.text() == u'关闭连接':
            try:
                self.rcvbuff += self.aUpRead()

                if self.txtMain.isVisible():
                    if self.chkHEXShow.isChecked():
                        text = ''.join(f'{ord(x):02X} ' for x in self.rcvbuff)
                    else:
                        text = self.rcvbuff

                    if len(self.txtMain.toPlainText()) > 25000:
                        self.txtMain.clear()
                    self.txtMain.moveCursor(QtGui.QTextCursor.End)
                    self.txtMain.insertPlainText(text)

                    self.rcvbuff = ''

                else:
                    if self.rcvbuff.rfind(',') == -1: return

                    d = self.rcvbuff[0:self.rcvbuff.rfind(',')].split(
                        ',')  # [b'12', b'34'] or [b'12 34', b'56 78']
                    d = [[float(x) for x in X.strip().split()]
                         for X in d]  # [[12], [34]]   or [[12, 34], [56, 78]]
                    for arr in d:
                        for i, x in enumerate(arr):
                            if i == N_CURVES: break

                            self.PlotData[i].pop(0)
                            self.PlotData[i].append(x)
                            self.PlotPoint[i].pop(0)
                            self.PlotPoint[i].append(QtCore.QPointF(999, x))

                    self.rcvbuff = self.rcvbuff[self.rcvbuff.rfind(',') + 1:]

                    self.tmrRTT_Cnt += 1
                    if self.tmrRTT_Cnt % 4 == 0:
                        if len(d[-1]) != len(self.PlotChart.series()):
                            for series in self.PlotChart.series():
                                self.PlotChart.removeSeries(series)
                            for i in range(min(len(d[-1]), N_CURVES)):
                                self.PlotCurve[i].setName(f'Curve {i+1}')
                                self.PlotChart.addSeries(self.PlotCurve[i])
                            self.PlotChart.createDefaultAxes()

                        for i in range(len(self.PlotChart.series())):
                            for j, point in enumerate(self.PlotPoint[i]):
                                point.setX(j)

                            self.PlotCurve[i].replace(self.PlotPoint[i])

                        miny = min([
                            min(d) for d in
                            self.PlotData[:len(self.PlotChart.series())]
                        ])
                        maxy = max([
                            max(d) for d in
                            self.PlotData[:len(self.PlotChart.series())]
                        ])
                        self.PlotChart.axisY().setRange(miny, maxy)
                        self.PlotChart.axisX().setRange(0000, 1000)

            except Exception as e:
                self.rcvbuff = ''
                print(str(e))  # 波形显示模式下 txtMain 不可见,因此错误信息不能显示在其上

        else:
            self.tmrRTT_Cnt += 1
            if self.tmrRTT_Cnt % 20 == 0:
                self.detect_daplink()  # 自动检测 DAPLink 的热插拔

    def aDownWrite(self, text):
        buf = self.dap.read_memory_block8(self.aDownAddr,
                                          ctypes.sizeof(RingBuffer))
        aDown = RingBuffer.from_buffer(bytearray(buf))

        if aDown.WrOff >= aDown.RdOff:
            if aDown.RdOff != 0:
                cnt = min(aDown.SizeOfBuffer - aDown.WrOff, len(text))
            else:
                cnt = min(
                    aDown.SizeOfBuffer - 1 - aDown.WrOff,
                    len(text))  # 写入操作不能使得 aDown.WrOff == aDown.RdOff,以区分满和空
            self.dap.write_memory_block8(
                ctypes.cast(aDown.pBuffer, ctypes.c_void_p).value +
                aDown.WrOff, [ord(x) for x in text[:cnt]])

            aDown.WrOff += cnt
            if aDown.WrOff == aDown.SizeOfBuffer: aDown.WrOff = 0

            text = text[cnt:]

        if text and aDown.RdOff != 0 and aDown.RdOff != 1:  # != 0 确保 aDown.WrOff 折返回 0,!= 1 确保有空间可写入
            cnt = min(aDown.RdOff - 1 - aDown.WrOff,
                      len(text))  # - 1 确保写入操作不导致WrOff与RdOff指向同一位置
            self.dap.write_memory_block8(
                ctypes.cast(aDown.pBuffer, ctypes.c_void_p).value +
                aDown.WrOff, [ord(x) for x in text[:cnt]])

            aDown.WrOff += cnt

        self.dap.write32(self.aDownAddr + 4 * 3, aDown.WrOff)

    @pyqtSlot()
    def on_btnSend_clicked(self):
        if self.btnOpen.text() == u'关闭连接':
            text = self.txtSend.toPlainText()

            try:
                if self.chkHEXSend.isChecked():
                    text = ''.join([chr(int(x, 16)) for x in text.split()])

                self.aDownWrite(text)

            except Exception as e:
                self.txtMain.append(f'\n{str(e)}\n')

    def detect_daplink(self):
        daplinks = aggregator.DebugProbeAggregator.get_all_connected_probes()

        if len(daplinks) != self.cmbDAP.count():
            self.cmbDAP.clear()
            for daplink in daplinks:
                self.cmbDAP.addItem(daplink.product_name)

            self.daplinks = collections.OrderedDict([
                (daplink.product_name, daplink) for daplink in daplinks
            ])

            if self.daplink and self.daplink.product_name in self.daplinks:
                self.cmbDAP.setCurrentIndex(self.daplinks.keys().index(
                    self.daplink.product_name))
            else:  # daplink被拔掉
                self.btnOpen.setText(u'打开连接')

    @pyqtSlot(int)
    def on_chkWavShow_stateChanged(self, state):
        self.ChartView.setVisible(state == Qt.Checked)
        self.txtMain.setVisible(state == Qt.Unchecked)

    @pyqtSlot()
    def on_btnClear_clicked(self):
        self.txtMain.clear()

    def closeEvent(self, evt):
        self.conf.write(open('setting.ini', 'w', encoding='utf-8'))
示例#10
0
class ChartView(QChartView):
    def __init__(self, file, parent=None):
        super(ChartView, self).__init__(parent)
        self._chart = QChart()
        self._chart.setAcceptHoverEvents(True)
        self.setChart(self._chart)
        self.initUi(file)

    def initUi(self, file):
        if isinstance(file, dict):
            return self.__analysis(file)
        if isinstance(file, str):
            if not os.path.isfile(file):
                return self.__analysis(json.loads(file))
            with open(file, "rb") as fp:
                data = fp.read()
                encoding = chardet.detect(data) or {}
                data = data.decode(encoding.get("encoding") or "utf-8")
            self.__analysis(json.loads(data))

    #     def onSeriesHoverd(self, point, state):
    #         print(point, state)

    def mouseMoveEvent(self, event):
        super(ChartView, self).mouseMoveEvent(event)
        # 获取x和y轴的最小最大值
        axisX, axisY = self._chart.axisX(), self._chart.axisY()
        min_x, max_x = axisX.min(), axisX.max()
        min_y, max_y = axisY.min(), axisY.max()
        # 把鼠标位置所在点转换为对应的xy值
        x = self._chart.mapToValue(event.pos()).x()
        y = self._chart.mapToValue(event.pos()).y()
        index = round(x)  # 四舍五入
        print(x, y, index)
        # 得到在坐标系中的所有series的类型和点
        points = [(s.type(), s.at(index)) for s in self._chart.series()
                  if min_x <= x <= max_x and min_y <= y <= max_y]
        print(points)

    def __getColor(self, color=None, default=Qt.white):
        '''
        :param color: int|str|[r,g,b]|[r,g,b,a]
        '''
        if not color:
            return QColor(default)
        if isinstance(color, QBrush):
            return color
        # 比如[r,g,b]或[r,g,b,a]
        if isinstance(color, list) and 3 <= len(color) <= 4:
            return QColor(*color)
        else:
            return QColor(color)

    def __getPen(self,
                 pen=None,
                 default=QPen(Qt.white, 1, Qt.SolidLine, Qt.SquareCap,
                              Qt.BevelJoin)):
        '''
        :param pen: pen json
        '''
        if not pen or not isinstance(pen, dict):
            return default
        return QPen(self.__getColor(pen.get("color", None) or default.color()),
                    pen.get("width", 1) or 1,
                    pen.get("style", 0) or 0,
                    pen.get("capStyle", 16) or 16,
                    pen.get("joinStyle", 64) or 64)

    def __getAlignment(self, alignment):
        '''
        :param alignment: left|top|right|bottom
        '''
        try:
            return getattr(Qt, "Align" + alignment.capitalize())
        except:
            return Qt.AlignTop

    #         if alignment == "left":
    #             return Qt.AlignLeft
    #         if alignment == "right":
    #             return Qt.AlignRight
    #         if alignment == "bottom":
    #             return Qt.AlignBottom
    #         return Qt.AlignTop

    def __setTitle(self, title=None):
        '''
        :param title: title json
        '''
        if not title or not isinstance(title, dict):
            return
        # 设置标题
        self._chart.setTitle(title.get("text", "") or "")
        # 设置标题颜色
        self._chart.setTitleBrush(
            self.__getColor(
                title.get("color", self._chart.titleBrush())
                or self._chart.titleBrush()))
        # 设置标题字体
        font = QFont(title.get("font", "") or self._chart.titleFont())
        pointSize = title.get("pointSize", -1) or -1
        if pointSize > 0:
            font.setPointSize(pointSize)
        font.setWeight(title.get("weight", -1) or -1)
        font.setItalic(title.get("italic", False) or False)
        self._chart.setTitleFont(font)

    def __setAnimation(self, animation=None):
        '''
        :param value: animation json
        '''
        if not animation or not isinstance(animation, dict):
            return
        # 动画持续时间
        self._chart.setAnimationDuration(
            animation.get("duration", 1000) or 1000)
        # 设置动画曲线
        self._chart.setAnimationEasingCurve(
            EasingCurve.get(animation.get("curve", 10) or 10, None)
            or QEasingCurve.OutQuart)
        # 设置开启何种动画
        self._chart.setAnimationOptions(
            AnimationOptions.get(animation.get("options", 0) or 0, None)
            or QChart.NoAnimation)

    def __setBackground(self, background=None):
        '''
        :param background:background json
        '''
        if not background or not isinstance(background, dict):
            return
        # 设置是否背景可用
        self._chart.setBackgroundVisible(
            background.get("visible", True) or True)
        # 设置背景矩形的圆角
        self._chart.setBackgroundRoundness(background.get("radius", 0) or 0)
        # 设置下拉阴影
        self._chart.setDropShadowEnabled(
            background.get("dropShadow", True) or True)
        # 设置pen
        self._chart.setBackgroundPen(
            self.__getPen(background.get("pen", None),
                          self._chart.backgroundPen()))
        # 设置背景
        image = background.get("image", None)
        color = background.get("color", None)
        if image:
            self._chart.setBackgroundBrush(QBrush(QPixmap(image)))
        elif color:
            self._chart.setBackgroundBrush(
                self.__getColor(color, self._chart.backgroundBrush()))

    def __setMargins(self, margins=None):
        '''
        :param margins: margins json
        '''
        if not margins or not isinstance(margins, dict):
            return
        left = margins.get("left", 20) or 20
        top = margins.get("top", 20) or 20
        right = margins.get("right", 20) or 20
        bottom = margins.get("bottom", 20) or 20
        self._chart.setMargins(QMargins(left, top, right, bottom))

    def __setLegend(self, legend=None):
        '''
        :param legend: legend json
        '''
        if not legend or not isinstance(legend, dict):
            return
        _legend = self._chart.legend()
        _legend.setAlignment(self.__getAlignment(legend.get("alignment",
                                                            None)))
        _legend.setShowToolTips(legend.get("showToolTips", True) or True)

    def __getSerie(self, serie=None):
        if not serie or not isinstance(serie, dict):
            return None
        types = serie.get("type", "") or ""
        data = serie.get("data", []) or []
        if not data or not isinstance(data, list):
            return None
        if types == "line":
            _series = QLineSeries(self._chart)
        else:
            return None
        # 设置series名字
        _series.setName(serie.get("name", "") or "")
        # 添加数据到series中
        for index, value in enumerate(data):
            # 保证vlaue必须是数字
            _series.append(index, value if type(value) in (int, float) else 0)
        return _series

    def __setSeries(self, series=None):
        if not series or not isinstance(series, list):
            return
        for serie in series:
            _serie = self.__getSerie(serie)
            if _serie:
                #                 _serie.hovered.connect(self.onSeriesHoverd)
                self._chart.addSeries(_serie)
        # 创建默认的xy轴
        self._chart.createDefaultAxes()

    def __setAxisX(self, axisx=None):
        if not axisx or not isinstance(axisx, dict):
            return
        series = self._chart.series()
        if not series:
            return
        types = axisx.get("type", None)
        data = axisx.get("data", []) or []
        if not data or not isinstance(data, list):
            return None
        minx = self._chart.axisX().min()
        maxx = self._chart.axisX().max()
        if types == "category":
            xaxis = QCategoryAxis(
                self._chart,
                labelsPosition=QCategoryAxis.AxisLabelsPositionOnValue)
            # 隐藏网格
            xaxis.setGridLineVisible(False)
            # 刻度条数
            tickc_d = len(data)
            tickc = tickc_d if tickc_d > 1 else self._chart.axisX().tickCount()
            xaxis.setTickCount(tickc)
            # 强制x轴刻度与新刻度条数一致
            self._chart.axisX().setTickCount(tickc)
            step = (maxx - minx) / (tickc - 1)
            for i in range(min(tickc_d, tickc)):
                xaxis.append(data[i], minx + i * step)
            self._chart.setAxisX(xaxis, series[-1])

    def __analysis(self, datas):
        '''
        analysis json data
        :param datas: json data
        '''
        # 标题
        self.__setTitle(datas.get("title", None))
        # 抗锯齿
        if (datas.get("antialiasing", False) or False):
            self.setRenderHint(QPainter.Antialiasing)
        # 主题
        self._chart.setTheme(datas.get("theme", 0) or 0)
        # 动画
        self.__setAnimation(datas.get("animation", None))
        # 背景设置
        self.__setBackground(datas.get("background", None))
        # 边距设置
        self.__setMargins(datas.get("margins", None))
        # 设置图例
        self.__setLegend(datas.get("legend", None))
        # 设置series
        self.__setSeries(datas.get("series", None))
        # 自定义的x轴
        self.__setAxisX(datas.get("axisx", None))
示例#11
0
class SproutUI(QtWidgets.QMainWindow):
    def __init__(self):
        super(SproutUI, self).__init__()
        uic.loadUi('Sprout.ui', self)
        self.setWindowTitle("Sprout")
        self.setWindowIcon(QIcon('./Images/SproutIcon.ico'))

        self.get_default_path()

        self.save_window_ui = SaveWindow(self)

        self.chartView = None

        self.myThread = None
        # columns: wedges, rows: rings (r1(w1,w2,...,w7,wAvg),r2(...),r3(...),rAvg(...))
        self.densities = []
        self.measurement_data = []

        self.error_message = ""

        self.loading_image = QPixmap("./Images/LoadingImage")

    def ui(self):
        """
        Sets Sprout user interface and displays the user interface
        :return: None
        """
        self.tabWidget_1.setCurrentIndex(0)
        self.tabWidget_2.setCurrentIndex(0)
        self.lineEdit_numMeasurements.setFocus(0)

        # Main Screen and save button
        self.browse_button_1.clicked.connect(self.browse_file)
        self.browse_button_2.clicked.connect(self.browse_folder)
        self.lineEdit_intermediateStepPath.setText(in_data['intermediate_path'])

        self.lineEdit_numMeasurements.editingFinished.connect(self.update_num_diameter_measurements)
        self.lineEdit_numWedges.editingFinished.connect(self.update_num_wedges)
        self.lineEdit_numRings.editingFinished.connect(self.update_num_rings)
        self.lineEdit_imageDPI.editingFinished.connect(self.update_image_dpi)

        self.start_button.clicked.connect(self.start_button_func)
        self.stop_button.clicked.connect(self.stop_button_func)
        self.save_button.clicked.connect(self.show_save_files)

        # Graphs View
        self.comboBox_rings.currentIndexChanged.connect(self.filter_rings_graph)
        self.comboBox_wedges.currentIndexChanged.connect(self.filter_wedges_graph)

        self.progressBar.setValue(0)
        self.progressBar.hide()
        self.label_progressBar.hide()

        self.disable_dashboard()

        self.progressBar.valueChanged.connect(self.progress_change)

        self.tabWidget_1.tabBar().setCursor(QtCore.Qt.PointingHandCursor)
        self.tabWidget_2.tabBar().setCursor(QtCore.Qt.PointingHandCursor)

        self.show()

    def get_default_path(self):
        """
        Gets the "Documents" directory of the user of the local computer.
        :return: None
        """
        temp = str(os.path.expanduser("~"))
        split = temp.split(os.path.sep)
        temp = str(split[0] + "/" + split[1] + "/" + split[2] + "/" + "Documents/Sprout/Run")
        in_data['intermediate_path'] = temp

    def browse_file(self):
        """
        Function mapped to the browse button used to select the file path of the image that will be used to
        calculate the fiber density of a bamboo cross-section.
        :return: None
        """
        url = QFileDialog.getOpenFileName(self, "Open a file", "", "*jpg; *jpeg;; *bmp;; *tif")

        if url[0] is not '':
            try:
                self.label_bamboo.setPixmap(self.loading_image)
                self.label_bamboo.repaint()
            except Exception:
                self.warning_message_box("Missing loading image. ")

            try:
                assert (url[0])[-4:] in ('.bmp', '.jpg', 'jpeg', '.tif'), "Image format is not supported."
            except Exception as e:
                self.label_bamboo.clear()
                self.warning_message_box(str(e))
                return

            try:
                cross_section = QPixmap(url[0])
                cross_section = cross_section.scaled(500, 500)
                if cross_section.isNull():
                    self.label_bamboo.clear()
                    self.warning_message_box("Unable to open input file.  \n\n")
                else:
                    self.label_bamboo.clear()
                    self.label_bamboo.setPixmap(cross_section)
                    self.lineEdit_imagePath.setText(url[0])
                    in_data['img_path'] = url[0]
            except Exception as e:
                self.label_bamboo.clear()
                self.warning_message_box("Unable to open input file, verify  \n file path or image file type.\n\n")

    def browse_folder(self):
        """
        Function mapped to the browse button used to select the folder path that will be used to
        save the intermediate step for the fiber density calculation.
        :return: None
        """
        url = QFileDialog.getExistingDirectory(self, "Open a directory", "", QFileDialog.ShowDirsOnly)
        if url is not '':
            self.lineEdit_intermediateStepPath.setText(url)
            in_data['intermediate_path'] = url

    def disable_dashboard(self):
        """
        Disables the dashboard and hides the graphs.
        :return: None
        """
        self.dashboard_tab.setEnabled(False)
        self.tabWidget_2.setEnabled(False)
        self.graphs_tab.setEnabled(False)
        self.region_density_tab.setEnabled(False)
        self.measurement_data_tab.setEnabled(False)

        self.tabWidget_2.setCurrentIndex(0)

        self.widget_rings.hide()
        self.widget_wedges.hide()
        self.comboBox_rings.hide()
        self.comboBox_wedges.hide()

    def update_num_diameter_measurements(self):
        """
        Update the value for the total number of measurements in the user interface main screen that serves as feedback.
        :return: None
        """
        global in_data

        if self.is_int_inbound(self.lineEdit_numMeasurements.text(), 3, 100):
            temp = int(self.lineEdit_numMeasurements.text()) * 4
            self.label_numMeasurementsFeedback.setText("Total Diameter Measurements: " + str(temp))
            in_data['num_measurement'] = temp
        else:
            self.lineEdit_numMeasurements.clear()

    def update_num_wedges(self):
        """
        Update the value for number of wedges in the user interface main screen that serves as feedback.
        :return: None
        """
        global in_data, wedge_degree

        if self.is_int_inbound(self.lineEdit_numWedges.text(), 3, 100):
            temp = int(self.lineEdit_numWedges.text()) * 4
            wedge_degree = 360 / temp
            self.label_numWedgesFeedback.setText("Num. Wedges: " + str(temp) + " @ {:.1f}º".format(wedge_degree))
            self.label_numRegionsFeedback.setText("Num. Regions: " + str(temp * int(in_data['num_rings'])))
            in_data['num_wedges'] = temp
        else:
            self.lineEdit_numWedges.clear()

    def update_num_rings(self):
        """
        Update the value for number of rings in the user interface main screen that serves as feedback.
        :return: None
        """
        global in_data

        temp = self.lineEdit_numRings.text()
        if self.is_int_inbound(temp, 1, 25):
            self.label_numRingsFeedback.setText("Num. Rings: " + str(temp))
            self.label_numRegionsFeedback.setText("Num. Regions: " + str(int(temp) * int(in_data['num_wedges'])))
            in_data['num_rings'] = int(temp)
        else:
            self.lineEdit_numRings.clear()

    def update_image_dpi(self):
        """
        Update the value for the image dpi (dot per inch) in the user interface main screen that serves as feedback.
        :return: None
        """
        global in_data

        if self.is_int_inbound(self.lineEdit_imageDPI.text(), 1200, 4800):
            temp = int(self.lineEdit_imageDPI.text())
            self.label_imageDPIFeedback.setText("Image DPI: " + str(temp))
            in_data['img_dpi'] = temp
        else:
            self.lineEdit_imageDPI.clear()

    def start_button_func(self):
        """
        Sets what the start button will do when it is pressed. It starts the fiber density calculation and
        disables user input. The button is replaced by the stop button.
        :return: None
        """
        global in_data, debounce
        # if program has not started
        if debounce is not 0:
            if (time.time() - debounce) < .30:
                return
            debounce = 0

        # Test input data for being empty
        if(self.lineEdit_imagePath.text() is "" or self.lineEdit_intermediateStepPath.text() is "" or
                self.lineEdit_numWedges.text() is "" or self.lineEdit_numRings.text() is "" or
                self.lineEdit_numMeasurements.text() is "" or self.lineEdit_imageDPI.text() is ""):
            self.warning_message_box("Make sure all inputs are filled in.")
            return

        # Test numeric input
        if not self.is_int_inbound(self.lineEdit_numMeasurements.text(), 3, 100, self.label_numMeasurements.text()):
            return
        if not self.is_int_inbound(self.lineEdit_numWedges.text(), 3, 100, self.label_numWedges.text()):
            return
        if not self.is_int_inbound(self.lineEdit_numRings.text(), 1, 25, self.label_numRings.text()):
            return
        if not self.is_int_inbound(self.lineEdit_imageDPI.text(), 1200, 4800, self.label_imageDPI.text()):
            return

        # After all inputs have been validated
        self.disable_dashboard()

        # Save input data in in_data dictionary
        in_data['units'] = self.comboBox_units.currentText()
        in_data['num_measurement'] = int(self.lineEdit_numMeasurements.text())*4
        in_data['img_dpi'] = int(self.lineEdit_imageDPI.text())
        in_data['enhance'] = bool(self.checkBox_imageEnhancement.isChecked())
        in_data['pixelMap'] = bool(self.checkBox_pixelMap.isChecked())

        self.inputs_set_enabled(False)

        self.progressBar.show()
        self.label_progressBar.show()
        self.progressBar.setValue(1)

        self.stop_button.setEnabled(True)
        self.start_button.hide()

        # Start Sprout Controller for fiber density calculation
        self.myThread = Sprout.SproutController(self, in_data)
        try:
            self.myThread.start()
            self.myThread.progress.connect(self.progressBar.setValue)
        except:
            self.warning_message_box("Error while starting process.")

    def stop_button_func(self):
        """
        Sets what the stop button will do when it is pressed or is called when the fiber density calculation
        is completed. It cancels the running session and enables user input. If running session is completed it
        enables the user input, and proceeds to display the dashboard (graphs, region density table, and measurement
         data). The button is replaced by the start button.
        :return: None
        """
        global debounce
        # if program is currently in progress

        self.stop_button.setEnabled(False)
        self.stop_button.repaint()

        if not self.myThread.isFinished():
            self.myThread.requestInterruption()
            self.myThread.wait()

        if self.progressBar.value() == 100:
            # if finished successfully

            # self.progressBar.setValue(100)
            self.dashboard_tab.setEnabled(True)
            self.tabWidget_2.setEnabled(True)
            self.graphs_tab.setEnabled(True)
            self.region_density_tab.setEnabled(True)
            self.measurement_data_tab.setEnabled(True)

            # create graphs
            self.create_graphs()

            # create table
            self.create_table()

            # set measurement data
            self.display_measurement_data()

            self.tabWidget_1.setCurrentIndex(1)
            self.tabWidget_2.setCurrentIndex(0)

        self.inputs_set_enabled(True)

        self.progressBar.hide()
        self.label_progressBar.hide()
        self.progressBar.setValue(0)

        self.start_button.show()
        debounce = time.time()

    def inputs_set_enabled(self, val: bool):
        """
        Enable or disable the options presented in the home screen depending on input parameter: val.
        :param val: True to anabel all the options in the home screen and False to disable.
        :return: None
        """
        self.browse_button_1.setEnabled(val)
        self.browse_button_2.setEnabled(val)
        self.comboBox_units.setEnabled(val)
        self.lineEdit_numMeasurements.setEnabled(val)
        self.lineEdit_numWedges.setEnabled(val)
        self.lineEdit_numRings.setEnabled(val)
        self.lineEdit_imageDPI.setEnabled(val)
        self.checkBox_imageEnhancement.setEnabled(val)
        self.checkBox_pixelMap.setEnabled(val)

        self.label_imagePath.setEnabled(val)
        self.label_intermediateStepPath.setEnabled(val)
        self.label_numMeasurements.setEnabled(val)
        self.label_numWedges.setEnabled(val)
        self.label_numRings.setEnabled(val)
        self.label_imageDPI.setEnabled(val)
        self.label_units.setEnabled(val)

    def create_graphs(self):
        """
        Creates the graphs that will be displayed int the dashboard's Graphs tab.
        :return: None
        """
        global default_comboBox_graph_item_count

        # Set Graphs ComboBox
        for x in range(self.comboBox_rings.count()):
            self.comboBox_rings.removeItem(default_comboBox_graph_item_count)

        for x in range(self.comboBox_wedges.count()):
            self.comboBox_wedges.removeItem(default_comboBox_graph_item_count)

        self.comboBox_rings.setCurrentIndex(0)
        self.comboBox_wedges.setCurrentIndex(0)

        # Ring Graph
        self.ring_chart = QChart()

        for x in range(len(self.densities)):
            ring_series = QLineSeries()
            for y in range(len(self.densities[x])-1):
                ring_series.append(y+1, self.densities[x][y])
            self.ring_chart.addSeries(ring_series)
            if x < len(self.densities)-1:
                self.comboBox_rings.addItem("Ring " + str(x+1))

        self.ring_chart.setTitle('Fiber Density VS Wedges')
        self.ring_chart.legend().hide()
        self.ring_chart.createDefaultAxes()
        self.ring_chart.axes(Qt.Horizontal)[0].setRange(1, len(self.densities[0])-1)
        self.ring_chart.axes(Qt.Vertical)[0].setRange(0, 1)
        self.ring_chart.axes(Qt.Horizontal)[0].setTitleText("Wedge Number")
        self.ring_chart.axes(Qt.Vertical)[0].setTitleText("Fiber Density")

        self.chartView = QChartView(self.ring_chart, self.widget_rings)
        self.chartView.resize(self.widget_rings.size())

        # Wedges Graph
        self.wedge_chart = QChart()

        for y in range(len(self.densities[0])):
            if in_data['num_rings'] == 1:
                ring_series = QScatterSeries()
            else:
                ring_series = QLineSeries()
            for x in range(len(self.densities)-1):
                ring_series.append(x+1, self.densities[x][y])
            self.wedge_chart.addSeries(ring_series)
            if y < len(self.densities[0])-1:
                self.comboBox_wedges.addItem("Wedge " + str(y+1))

        self.wedge_chart.setTitle('Fiber Density VS Rings')
        self.wedge_chart.legend().hide()
        self.wedge_chart.createDefaultAxes()
        if (len(self.densities)) == 2:
            self.wedge_chart.axes(Qt.Horizontal)[0].setRange(0, 2)
        else:
            self.wedge_chart.axes(Qt.Horizontal)[0].setRange(1, len(self.densities)-1)
        self.wedge_chart.axes(Qt.Vertical)[0].setRange(0, 1)
        self.wedge_chart.axes(Qt.Horizontal)[0].setTitleText("Ring Number")
        self.wedge_chart.axes(Qt.Vertical)[0].setTitleText("Fiber Density")

        self.chartView = QChartView(self.wedge_chart, self.widget_wedges)
        self.chartView.resize(self.widget_wedges.size())

        self.widget_rings.show()
        self.widget_wedges.show()

        if in_data['num_rings'] == 1:
            self.comboBox_rings.hide()
        else:
            self.comboBox_rings.show()
        self.comboBox_wedges.show()

    def filter_rings_graph(self):
        """
        Filters the rings graph by: All(includes average), All Wedges(only rings are shown),
        Average(only average is shown), and individual rings(varies depending on number of rings).
        :return: None
        """
        global default_comboBox_graph_item_count

        for x in range(len(self.ring_chart.series())):
            self.ring_chart.series()[x].show()

        if self.comboBox_rings.currentText() == "All Rings":
            for x in range(len(self.ring_chart.series()) - 1):
                self.ring_chart.series()[x].show()
            self.ring_chart.series()[len(self.ring_chart.series()) - 1].hide()
        elif self.comboBox_rings.currentText() == "Average":
            for x in range(len(self.ring_chart.series()) - 1):
                self.ring_chart.series()[x].hide()
            self.ring_chart.series()[len(self.ring_chart.series()) - 1].show()
        elif "Ring" in self.comboBox_rings.currentText():
            for x in range(len(self.ring_chart.series())):
                self.ring_chart.series()[x].hide()
            self.ring_chart.series()[self.comboBox_rings.currentIndex() - default_comboBox_graph_item_count].show()

    def filter_wedges_graph(self):
        """
        Filters the wedges graph by: All(includes average), All Wedges(only wedges are shown),
        Average(only average is shown), and individual wedges(varies depending on number of wedges).
        :return: None
        """
        global default_comboBox_graph_item_count

        for x in range(len(self.wedge_chart.series())):
            self.wedge_chart.series()[x].show()

        if self.comboBox_wedges.currentText() == "All Wedges":
            for x in range(len(self.wedge_chart.series()) - 1):
                self.wedge_chart.series()[x].show()
            self.wedge_chart.series()[len(self.wedge_chart.series()) - 1].hide()
        elif self.comboBox_wedges.currentText() == "Average":
            for x in range(len(self.wedge_chart.series()) - 1):
                self.wedge_chart.series()[x].hide()
            self.wedge_chart.series()[len(self.wedge_chart.series()) - 1].show()
        elif "Wedge" in self.comboBox_wedges.currentText():
            for x in range(len(self.wedge_chart.series())):
                self.wedge_chart.series()[x].hide()
            self.wedge_chart.series()[self.comboBox_wedges.currentIndex() - default_comboBox_graph_item_count].show()

    def create_table(self):
        """
        Creates the table that will be presented in the dashboard's Region Density tab
        based on fiber density calculations.
        :return: None
        """
        i = 0
        j = 0
        column_name = []
        row_name = []

        self.tableWidget.setRowCount(len(self.densities))
        self.tableWidget.setColumnCount(len(self.densities[0]))

        for ring in self.densities:
            for wedge in ring:
                item = QTableWidgetItem("{:.4f}".format(wedge))
                item.setTextAlignment(Qt.AlignCenter)

                # if average of average
                if j == (len(self.densities[0])-1) and i == (len(self.densities)-1):
                    font = QFont()
                    font.setBold(True)
                    item.setFont(font)

                self.tableWidget.setItem(i, j, item)
                j += 1
            j = 0
            i += 1

        for x in range(len(self.densities[0])):
            if x == len(self.densities[0]) - 1:
                column_name.append("Average")
            else:
                column_name.append("Wedge " + str(x + 1))
        for y in range(len(self.densities)):
            if y == len(self.densities) - 1:
                row_name.append("Average")
            else:
                row_name.append("Ring " + str(y + 1))

        self.tableWidget.setHorizontalHeaderLabels(column_name)
        self.tableWidget.setVerticalHeaderLabels(row_name)
        self.tableWidget.setEditTriggers(QtWidgets.QTableWidget.NoEditTriggers)

    def display_measurement_data(self):
        """
        Manages all output data related to the measurement data that is presented in the dashboard Measurement Data.
        :return: None
        """
        self.lineEdit_area.setText(str("{:.4f}".format(self.measurement_data[0])) + " " + in_data['units'] + "^2")
        self.lineEdit_avgOuterDiameter.setText(str("{:.4f}".format(self.measurement_data[1])) + " " + in_data['units'])
        self.lineEdit_avgInnerDiameter.setText(str("{:.4f}".format(self.measurement_data[2])) + " " + in_data['units'])
        self.lineEdit_AverageT.setText(str("{:.4f}".format(self.measurement_data[3])) + " " + in_data['units'])
        self.lineEdit_centroid_x.setText(str("{:.4f}".format(self.measurement_data[4])) + " " + in_data['units'])
        self.lineEdit_centroid_y.setText(str("{:.4f}".format(self.measurement_data[5])) + " " + in_data['units'])
        self.lineEdit_momentOfInertia_x.setText(str("{:.4f}".format(self.measurement_data[6])) + " " +
                                                in_data['units'] + "^4")
        self.lineEdit_momentOfInertia_y.setText(str("{:.4f}".format(self.measurement_data[7])) + " " +
                                                in_data['units'] + "^4")
        self.lineEdit_productOfInertia.setText(str("{:.4f}".format(self.measurement_data[8])) + " " +
                                               (in_data['units']) + "^4")

    def is_int_inbound(self, ui_in: str, lower: int, upper: int, ui_in_name: str = None):
        """
        Test if user input is in specified upper and lower bound, including upper and lower bound value.
        :param ui_in: user input for value that will be tested
        :param lower: lowest value that ui_in can have to return True
        :param upper: highest value that ui_in can hava to return True
        :param ui_in_name: user input label name that is used in popup messages for users to relate error
        :return: True if ui_in is in upper and lower bound (lower <= ui_in <= upper)
                    otherwise False
        """
        if not (str.isdigit(ui_in)) or int(ui_in) > upper or int(ui_in) < lower:
            if ui_in_name is not None:
                self.warning_message_box(str(ui_in_name) + "\nPlease input a number from "
                                         + str(lower) + " to " + str(upper))
            return False
        else:
            return True

    def warning_message_box(self, message):
        """
        Display a popup message box to inform users of error.
        :param message: Message to be displayed in the popup message
        :return:
        """
        mbox = QMessageBox.critical(self, "Warning!!", message)
        if mbox == QMessageBox.Ok:
            self.lineEdit_numMeasurements.setFocus(0)

    def show_save_files(self):
        """
        Call to displays the popup for the Save Popup Window.
        :return: None
        """
        self.windowModality()
        self.save_window_ui.lineEdit_filePath.setText(os.getcwd())
        self.save_window_ui.show()
        self.save_window_ui.raise_()

    def progress_change(self):
        """
        Signals the user interface when process is completed or wen error occurs.
        :return: None
        """
        if self.progressBar.value() == 2:
            self.stop_button_func()
            self.warning_message_box(str(self.error_message))
        elif self.progressBar.value() == 100:
            self.densities = DM.get_fiber_density_average()
            self.measurement_data = DM.get_dimensional_measurements()
            self.stop_button_func()
示例#12
0
class ChartView(QChartView):
    def __init__(self, *args, **kwargs):
        super(ChartView, self).__init__(*args, **kwargs)
        self.setRenderHint(QPainter.Antialiasing)  # 抗锯齿
        self._chart = QChart(title="词频图")
        # 提示widget
        self.toolTipWidget = GraphicsProxyWidget(self._chart)
        # line 宽度需要调整
        self.lineItem = QGraphicsLineItem(self._chart)
        pen = QPen(Qt.gray)
        self.lineItem.setPen(pen)
        self.lineItem.setZValue(998)
        self.lineItem.hide()

        self._chart.setAcceptHoverEvents(True)
        # Series动画
        self._chart.setAnimationOptions(QChart.SeriesAnimations)

    def mouseMoveEvent(self, event):
        super(ChartView, self).mouseMoveEvent(event)
        pos = event.pos()
        # 把鼠标位置所在点转换为对应的xy值
        x = self._chart.mapToValue(pos).x()
        y = self._chart.mapToValue(pos).y()
        index = round(x)
        # 得到在坐标系中的所有bar的类型和点
        try:
            serie = self._chart.series()[0]
        except:
            return
        bars = [
            (bar, bar.at(index)) for bar in serie.barSets()
            if self.min_x <= x <= self.max_x and self.min_y <= y <= self.max_y
        ]
        if bars:
            right_top = self._chart.mapToPosition(
                QPointF(self.max_x, self.max_y))
            # 等分距离比例
            step_x = round(
                (right_top.x() - self.point_top.x()) / self.category_len)
            posx = self._chart.mapToPosition(QPointF(x, self.min_y))
            self.lineItem.setLine(posx.x(), self.point_top.y(), posx.x(),
                                  posx.y())
            self.lineItem.show()
            try:
                title = self.categories[index]
            except:
                title = ""
            t_width = self.toolTipWidget.width()
            t_height = self.toolTipWidget.height()
            # 如果鼠标位置离右侧的距离小于tip宽度
            x = pos.x() - t_width if self.width() - \
                pos.x() - 20 < t_width else pos.x()
            # 如果鼠标位置离底部的高度小于tip高度
            y = pos.y() - t_height if self.height() - \
                pos.y() - 20 < t_height else pos.y()
            self.toolTipWidget.show(title, bars, QPoint(x, y))
        else:
            self.toolTipWidget.hide()
            self.lineItem.hide()

    def handleMarkerClicked(self):
        marker = self.sender()  # 信号发送者
        if not marker:
            return
        bar = marker.barset()
        if not bar:
            return
        # bar透明度
        brush = bar.brush()
        color = brush.color()
        alpha = 0.0 if color.alphaF() == 1.0 else 1.0
        color.setAlphaF(alpha)
        brush.setColor(color)
        bar.setBrush(brush)
        # marker
        brush = marker.labelBrush()
        color = brush.color()
        alpha = 0.4 if color.alphaF() == 1.0 else 1.0
        # 设置label的透明度
        color.setAlphaF(alpha)
        brush.setColor(color)
        marker.setLabelBrush(brush)
        # 设置marker的透明度
        brush = marker.brush()
        color = brush.color()
        color.setAlphaF(alpha)
        brush.setColor(color)
        marker.setBrush(brush)

    def handleMarkerHovered(self, status):
        # 设置bar的画笔宽度
        marker = self.sender()  # 信号发送者
        if not marker:
            return
        bar = marker.barset()
        if not bar:
            return
        pen = bar.pen()
        if not pen:
            return
        pen.setWidth(pen.width() + (1 if status else -1))
        bar.setPen(pen)

    def handleBarHoverd(self, status, index):
        # 设置bar的画笔宽度
        bar = self.sender()  # 信号发送者
        pen = bar.pen()
        if not pen:
            return
        pen.setWidth(pen.width() + (1 if status else -1))
        bar.setPen(pen)

    def initChart(self, common):
        self.categories = [item[0] for item in common]
        series = QBarSeries(self._chart)
        bar = QBarSet("")
        # 随机数据
        for item in common:
            bar.append(item[1])
        series.append(bar)
        bar.hovered.connect(self.handleBarHoverd)  # 鼠标悬停
        self._chart.addSeries(series)
        self._chart.createDefaultAxes()  # 创建默认的轴
        # x轴
        axis_x = QBarCategoryAxis(self._chart)
        axis_x.append(self.categories)
        self._chart.setAxisX(axis_x, series)
        # chart的图例
        legend = self._chart.legend()
        legend.setVisible(True)
        # 遍历图例上的标记并绑定信号
        for marker in legend.markers():
            # 点击事件
            marker.clicked.connect(self.handleMarkerClicked)
            # 鼠标悬停事件
            marker.hovered.connect(self.handleMarkerHovered)
        # 一些固定计算,减少mouseMoveEvent中的计算量
        # 获取x和y轴的最小最大值
        axisX, axisY = self._chart.axisX(), self._chart.axisY()
        self.category_len = len(axisX.categories())
        self.min_x, self.max_x = -0.5, self.category_len - 0.5
        self.min_y, self.max_y = axisY.min(), axisY.max()
        # 坐标系中左上角顶点
        self.point_top = self._chart.mapToPosition(
            QPointF(self.min_x, self.max_y))
        self.setChart(self._chart)
示例#13
0
class LineChartView(QChartView):
    def __init__(self):
        super(LineChartView, self).__init__()
        self.resize(800, 600)
        self.setRenderHint(QPainter.Antialiasing)  # 抗锯齿
        # 自定义x轴label

    def CreatePlot(self, NumSection, Alltime, PlotData, type, name='列车调度'):
        if type == 1:
            self.initChart(NumSection, Alltime, PlotData, name)
        else:
            self.initProgressChart(NumSection, Alltime, PlotData, name)

        # 提示widget
        self.toolTipWidget = GraphicsProxyWidget(self._chart)

        # line
        self.lineItem = QGraphicsLineItem(self._chart)
        pen = QPen(Qt.gray)
        pen.setWidth(1)
        self.lineItem.setPen(pen)
        self.lineItem.setZValue(998)
        #self.lineItem.hide()
        self.lineItem.show()
        # 一些固定计算,减少mouseMoveEvent中的计算量
        # 获取x和y轴的最小最大值
        axisX, axisY = self._chart.axisX(), self._chart.axisY()
        self.min_x, self.max_x = axisX.min(), axisX.max()
        self.min_y, self.max_y = axisY.min(), axisY.max()

    def resizeEvent(self, event):
        super(LineChartView, self).resizeEvent(event)
        # 当窗口大小改变时需要重新计算
        # 坐标系中左上角顶点
        self.point_top = self._chart.mapToPosition(
            QPointF(self.min_x, self.max_y))
        # 坐标原点坐标
        self.point_bottom = self._chart.mapToPosition(
            QPointF(self.min_x, self.min_y))
        self.step_x = (self.max_x -
                       self.min_x) / (self._chart.axisX().tickCount() - 1)

    def mouseMoveEvent(self, event):
        super(LineChartView, self).mouseMoveEvent(event)
        pos = event.pos()
        # 把鼠标位置所在点转换为对应的xy值
        x = self._chart.mapToValue(pos).x()
        y = self._chart.mapToValue(pos).y()
        index = round(int(x / self.step_x) * self.step_x) + round(
            x % self.step_x) - 1
        #index = round((x - self.min_x) / self.step_x)
        # 得到在坐标系中的所有正常显示的series的类型和点
        points = [
            (serie, serie.at(index)) for serie in self._chart.series()
            if self.min_x <= x <= self.max_x and self.min_y <= y <= self.max_y
        ]
        if points:
            pos_x = self._chart.mapToPosition(
                QPointF(index * self.step_x + self.min_x, self.min_y))
            self.lineItem.setLine(pos_x.x(), self.point_top.y(), pos_x.x(),
                                  self.point_bottom.y())
            self.lineItem.show()
            try:
                title = ""
            except:
                title = ""
            t_width = self.toolTipWidget.width()
            t_height = self.toolTipWidget.height()
            # 如果鼠标位置离右侧的距离小于tip宽度
            x = pos.x() - t_width if self.width() - \
                                     pos.x() - 20 < t_width else pos.x()
            # 如果鼠标位置离底部的高度小于tip高度
            y = pos.y() - t_height if self.height() - \
                                      pos.y() - 20 < t_height else pos.y()
            self.toolTipWidget.show(title, points, QPoint(x, y))
        else:
            self.toolTipWidget.hide()
            self.lineItem.hide()

    def handleMarkerClicked(self):
        marker = self.sender()  # 信号发送者
        if not marker:
            return
        visible = not marker.series().isVisible()
        #         # 隐藏或显示series
        marker.series().setVisible(visible)
        marker.setVisible(True)  # 要保证marker一直显示
        # 透明度
        alpha = 1.0 if visible else 0.4
        # 设置label的透明度
        brush = marker.labelBrush()
        color = brush.color()
        color.setAlphaF(alpha)
        brush.setColor(color)
        marker.setLabelBrush(brush)
        # 设置marker的透明度
        brush = marker.brush()
        color = brush.color()
        color.setAlphaF(alpha)
        brush.setColor(color)
        marker.setBrush(brush)
        # 设置画笔透明度
        pen = marker.pen()
        color = pen.color()
        color.setAlphaF(alpha)
        pen.setColor(color)
        marker.setPen(pen)

    def handleMarkerHovered(self, status):
        # 设置series的画笔宽度
        marker = self.sender()  # 信号发送者
        if not marker:
            return
        series = marker.series()
        if not series:
            return
        pen = series.pen()
        if not pen:
            return
        pen.setWidth(pen.width() + (1 if status else -1))
        series.setPen(pen)

    def handleSeriesHoverd(self, point, state):
        # 设置series的画笔宽度
        series = self.sender()  # 信号发送者
        pen = series.pen()
        if not pen:
            return
        pen.setWidth(pen.width() + (1 if state else -1))
        series.setPen(pen)

    def initProgressChart(self, MaxTime, Allgen, PlotData, name):
        MinTime = 100000
        for k in PlotData:
            if min(k[1]) < MinTime:
                MinTime = min(k[1])
        self._chart = QChart(title=name)
        self._chart.setAcceptHoverEvents(True)
        # Series动画

        self._chart.setAnimationOptions(QChart.SeriesAnimations)
        # dataTable = [
        #     ["邮件营销", [120, 132, 101, 134, 90, 230]],
        #     ["联盟广告", [220, 182, 191, 234, 290, 330, 310]],
        #     ["视频广告", [150, 232, 201, 154, 190, 330, 410]],
        #     ["直接访问", [320, 332, 301, 334, 390, 330, 320]],
        #     ["搜索引擎", [820, 932, 901, 934, 1290, 1330, 1320]]
        # ]
        for series_name, data_list, label_list in PlotData:
            series = QLineSeries(self._chart)
            for j, v in zip(label_list, data_list):
                series.append(j, v)
            series.setName(series_name)
            series.setPointsVisible(True)  # 显示圆点
            series.hovered.connect(self.handleSeriesHoverd)  # 鼠标悬停
            self._chart.addSeries(series)
        self._chart.createDefaultAxes()  # 创建默认的轴
        axisX = self._chart.axisX()  # x轴
        axisX.setTickCount(11)  # x轴设置7个刻度
        axisX.setGridLineVisible(False)  # 隐藏从x轴往上的线条
        axisY = self._chart.axisY()
        axisY.setTickCount(10)  # y轴设置7个刻度
        #axisY.setRange(0, MaxTime)  # 设置y轴范围
        # 自定义x轴
        # axis_x = QCategoryAxis(
        #     self._chart, labelsPosition=QCategoryAxis.AxisLabelsPositionOnValue)
        # axis_x.setTickCount(Allgen)
        # axis_x.setGridLineVisible(False)
        # min_x = axisX.min()
        # for i in range(0, Allgen + 1):
        #     axis_x.append(str(i), min_x + i)
        # self._chart.setAxisX(axis_x, self._chart.series()[-1])

        # 自定义y轴
        # axis_y = QCategoryAxis(
        #     self._chart, labelsPosition=QCategoryAxis.AxisLabelsPositionOnValue)
        # axis_y.setTickCount(round(MaxTime)-max(round(MinTime)-10,0))
        # axis_y.setRange(max(round(MinTime)-10,0), round(MaxTime))
        # for i in range(max(round(MinTime)-10,0), round(MaxTime) + 1):
        #     axis_y.append('%i' % i, i)
        # self._chart.setAxisY(axis_y, self._chart.series()[-1])
        # chart的图例
        legend = self._chart.legend()
        # 设置图例由Series来决定样式
        legend.setMarkerShape(QLegend.MarkerShapeFromSeries)
        # 遍历图例上的标记并绑定信号
        for marker in legend.markers():
            # 点击事件
            marker.clicked.connect(self.handleMarkerClicked)
            # 鼠标悬停事件
            marker.hovered.connect(self.handleMarkerHovered)
        self.setChart(self._chart)

    def initChart(self, NumSection, Alltime, PlotData, name):
        self._chart = QChart(title=name)
        self._chart.setAcceptHoverEvents(True)
        # Series动画
        self._chart.setAnimationOptions(QChart.SeriesAnimations)
        # dataTable = [
        #     ["邮件营销", [120, 132, 101, 134, 90, 230]],
        #     ["联盟广告", [220, 182, 191, 234, 290, 330, 310]],
        #     ["视频广告", [150, 232, 201, 154, 190, 330, 410]],
        #     ["直接访问", [320, 332, 301, 334, 390, 330, 320]],
        #     ["搜索引擎", [820, 932, 901, 934, 1290, 1330, 1320]]
        # ]
        for series_name, data_list, label_list in PlotData:
            series = QLineSeries(self._chart)
            for j, v in zip(label_list, data_list):
                series.append(j, v)
            series.setName(series_name)
            series.setPointsVisible(True)  # 显示圆点
            series.hovered.connect(self.handleSeriesHoverd)  # 鼠标悬停
            self._chart.addSeries(series)
        self._chart.createDefaultAxes()  # 创建默认的轴
        axisX = self._chart.axisX()  # x轴
        axisX.setTickCount(Alltime)  # x轴设置7个刻度
        axisX.setGridLineVisible(False)  # 隐藏从x轴往上的线条
        axisY = self._chart.axisY()
        axisY.setTickCount(NumSection)  # y轴设置7个刻度
        axisY.setRange(0, NumSection)  # 设置y轴范围
        # 自定义x轴
        axis_x = QCategoryAxis(
            self._chart,
            labelsPosition=QCategoryAxis.AxisLabelsPositionOnValue)
        axis_x.setTickCount(Alltime + 1)
        axis_x.setGridLineVisible(False)
        min_x = axisX.min()
        for i in range(0, Alltime + 1):
            axis_x.append(str(i), min_x + i)
        self._chart.setAxisX(axis_x, self._chart.series()[-1])

        # 自定义y轴
        axis_y = QCategoryAxis(
            self._chart, labelsPosition=QCategoryAxis.AxisLabelsPositionCenter)
        axis_y.setTickCount(NumSection)
        axis_y.setRange(0, NumSection)
        for i in range(0, NumSection + 1):
            axis_y.append('section%i' % (NumSection - i + 1), i)
        self._chart.setAxisY(axis_y, self._chart.series()[-1])
        # chart的图例
        legend = self._chart.legend()
        # 设置图例由Series来决定样式
        legend.setMarkerShape(QLegend.MarkerShapeFromSeries)
        # 遍历图例上的标记并绑定信号
        for marker in legend.markers():
            # 点击事件
            marker.clicked.connect(self.handleMarkerClicked)
            # 鼠标悬停事件
            marker.hovered.connect(self.handleMarkerHovered)
        self.setChart(self._chart)
示例#14
0
class QtGraph(QChartView):
    def __init__(self, spec):
        super().__init__(None)
        self.spec = spec
        self.chart = QChart()
        # self.chart.setTitle(str(self.spec.variables))
        # self.chart.legend().hide()

        for i in range(len(self.spec.variables)):
            series = QLineSeries()
            series.setColor(
                QColor(
                    self.spec.colors[i][0],
                    self.spec.colors[i][1],
                    self.spec.colors[i][2],
                ))
            series.setName(self.spec.variables[i])
            self.chart.addSeries(series)

        self.setMinimumWidth(400)
        self.setMinimumHeight(230)

        self.setChart(self.chart)
        self.setRenderHint(QPainter.Antialiasing)
        self.chart.createDefaultAxes()
        self.autoscale_y_axis = True
        if self.spec.min_y and self.spec.max_y:
            self.autoscale_y_axis = False
            self.chart.axes()[1].setRange(self.spec.min_y, self.spec.max_y)

        self._updates_per_second = 60
        self._data = []
        self._min = 0
        self._max = 0

    def clear(self):
        for chart in self.chart.series():
            chart.clear()
        self._data = []

    def add_data(self, data):
        self._data.append(data)

    def redraw(self):
        if len(self._data) > 0:
            for i in range(len(self.spec.variables)):
                data = [datapoint[i] for datapoint in self._data]
                datapoint = sum(data) / len(data)
                self.chart.series()[i].append(
                    QPointF(
                        self.chart.series()[i].count() /
                        self._updates_per_second,
                        datapoint,
                    ))
                self._min = min(self._min, datapoint)
                self._max = max(self._max, datapoint)
        self.chart.axes()[0].setRange(0, (self.chart.series()[0].count() - 1) /
                                      self._updates_per_second)
        diff = self._max - self._min
        if self.autoscale_y_axis:
            if diff > 0:
                self.chart.axes()[1].setRange(self._min, self._max)
            else:
                self.chart.axes()[1].setRange(self._min - 0.5, self._max + 0.5)
        self._data = []
示例#15
0
class SERCOM(QWidget):
    def __init__(self, parent=None):
        super(SERCOM, self).__init__(parent)
        
        uic.loadUi('SERCOM.ui', self)

        for port, desc, hwid in comports():
            self.cmbPort.addItem(f'{port} ({desc[:desc.index("(")]})')

        self.ser = Serial()

        self.initSetting()

        self.initQwtPlot()

        self.rcvbuff = b''

        self.tmrSer = QtCore.QTimer()
        self.tmrSer.setInterval(10)
        self.tmrSer.timeout.connect(self.on_tmrSer_timeout)
        self.tmrSer.start()

        self.tmrSer_Cnt = 0
        self.AutoInterval = 0   # 自动发送时间间隔,单位 10ms
    
    def initSetting(self):
        if not os.path.exists('setting.ini'):
            open('setting.ini', 'w', encoding='utf-8')
        
        self.conf = configparser.ConfigParser()
        self.conf.read('setting.ini', encoding='utf-8')

        if not self.conf.has_section('serial'):
            self.conf.add_section('serial')
            self.conf.set('serial', 'port', 'COM0')
            self.conf.set('serial', 'baud', '57600')

            self.conf.add_section('encode')
            self.conf.set('encode', 'input', 'ASCII')
            self.conf.set('encode', 'output', 'ASCII')
            self.conf.set('encode', 'oenter', r'\r\n')  # output enter (line feed)

            self.conf.add_section('others')
            self.conf.set('others', 'history', '11 22 33 AA BB CC')

        self.txtSend.setPlainText(self.conf.get('others', 'history'))

        self.cmbICode.setCurrentIndex(self.cmbICode.findText(self.conf.get('encode', 'input')))
        self.cmbOCode.setCurrentIndex(self.cmbOCode.findText(self.conf.get('encode', 'output')))
        self.cmbEnter.setCurrentIndex(self.cmbEnter.findText(self.conf.get('encode', 'oenter')))

        index = self.cmbPort.findText(self.conf.get('serial', 'port'))
        self.cmbPort.setCurrentIndex(index if index != -1 else 0)
        self.cmbBaud.setCurrentIndex(self.cmbBaud.findText(self.conf.get('serial', 'baud')))
    
    def initQwtPlot(self):
        self.PlotData  = [[0]*1000 for i in range(N_CURVES)]
        self.PlotPoint = [[QtCore.QPointF(j, 0) for j in range(1000)] for i in range(N_CURVES)]

        self.PlotChart = QChart()

        self.ChartView = QChartView(self.PlotChart)
        self.ChartView.setVisible(False)
        self.vLayout0.insertWidget(0, self.ChartView)
        
        self.PlotCurve = [QLineSeries() for i in range(N_CURVES)]

    @pyqtSlot()
    def on_btnOpen_clicked(self):
        if not self.ser.is_open:
            try:
                self.ser.timeout = 1
                self.ser.xonxoff = 0
                self.ser.port = self.cmbPort.currentText().split()[0]
                self.ser.parity = self.cmbChek.currentText()[0]
                self.ser.baudrate = int(self.cmbBaud.currentText())
                self.ser.bytesize = int(self.cmbData.currentText())
                self.ser.stopbits = int(self.cmbStop.currentText())
                self.ser.open()
            except Exception as e:
                self.txtMain.clear()
                self.txtMain.insertPlainText(str(e))
            else:
                self.cmbPort.setEnabled(False)
                self.btnOpen.setText('关闭串口')
        else:
            self.ser.close()

            self.cmbPort.setEnabled(True)
            self.btnOpen.setText('打开串口')
    
    @pyqtSlot()
    def on_btnSend_clicked(self):
        if self.ser.is_open:
            text = self.txtSend.toPlainText()

            if self.cmbOCode.currentText() == 'HEX':
                try:
                    self.ser.write(bytes([int(x, 16) for x in text.split()]))
                except Exception as e:
                    print(e)

            else:
                if self.cmbEnter.currentText() == r'\r\n':
                    text = text.replace('\n', '\r\n')
                
                try:
                    self.ser.write(text.encode(self.cmbOCode.currentText()))
                except Exception as e:
                    print(e)
    
    def on_tmrSer_timeout(self):
        self.tmrSer_Cnt += 1

        if self.ser.is_open:
            if self.ser.in_waiting > 0:
                self.rcvbuff += self.ser.read(self.ser.in_waiting)
                
                if self.chkWave.isChecked():
                    if self.rcvbuff.rfind(b',') == -1:
                        return

                    try:
                        d = self.rcvbuff[0:self.rcvbuff.rfind(b',')].split(b',')    # [b'12', b'34'] or [b'12 34', b'56 78']
                        d = [[float(x) for x in X.strip().split()] for X in d]      # [[12], [34]]   or [[12, 34], [56, 78]]
                        for arr in d:
                            for i, x in enumerate(arr):
                                if i == N_CURVES: break

                                self.PlotData[i].pop(0)
                                self.PlotData[i].append(x)
                                self.PlotPoint[i].pop(0)
                                self.PlotPoint[i].append(QtCore.QPointF(999, x))
                        
                        self.rcvbuff = self.rcvbuff[self.rcvbuff.rfind(b',')+1:]

                        if self.tmrSer_Cnt % 4 == 0:
                            if len(d[-1]) != len(self.PlotChart.series()):
                                for series in self.PlotChart.series():
                                    self.PlotChart.removeSeries(series)
                                for i in range(min(len(d[-1]), N_CURVES)):
                                    self.PlotCurve[i].setName(f'Curve {i+1}')
                                    self.PlotChart.addSeries(self.PlotCurve[i])
                                self.PlotChart.createDefaultAxes()

                            for i in range(len(self.PlotChart.series())):
                                for j, point in enumerate(self.PlotPoint[i]):
                                    point.setX(j)
                            
                                self.PlotCurve[i].replace(self.PlotPoint[i])
                        
                            miny = min([min(d) for d in self.PlotData[:len(self.PlotChart.series())]])
                            maxy = max([max(d) for d in self.PlotData[:len(self.PlotChart.series())]])
                            self.PlotChart.axisY().setRange(miny, maxy)
                            self.PlotChart.axisX().setRange(0000, 1000)
                    
                    except Exception as e:
                        self.rcvbuff = b''
                        print(e)

                else:
                    text = ''
                    if self.cmbICode.currentText() == 'ASCII':
                        text = ''.join([chr(x) for x in self.rcvbuff])
                        self.rcvbuff = b''

                    elif self.cmbICode.currentText() == 'HEX':
                        text = ' '.join([f'{x:02X}' for x in self.rcvbuff]) + ' '
                        self.rcvbuff = b''

                    elif self.cmbICode.currentText() == 'GBK':
                        while len(self.rcvbuff):
                            if self.rcvbuff[0:1].decode('GBK', 'ignore'):
                                text += self.rcvbuff[0:1].decode('GBK')
                                self.rcvbuff = self.rcvbuff[1:]

                            elif len(self.rcvbuff) > 1 and self.rcvbuff[0:2].decode('GBK', 'ignore'):
                                text += self.rcvbuff[0:2].decode('GBK')
                                self.rcvbuff = self.rcvbuff[2:]

                            elif len(self.rcvbuff) > 1:
                                text += chr(self.rcvbuff[0])
                                self.rcvbuff = self.rcvbuff[1:]

                            else:
                                break

                    elif self.cmbICode.currentText() == 'UTF-8':
                        while len(self.rcvbuff):
                            if self.rcvbuff[0:1].decode('UTF-8', 'ignore'):
                                text += self.rcvbuff[0:1].decode('UTF-8')
                                self.rcvbuff = self.rcvbuff[1:]

                            elif len(self.rcvbuff) > 1 and self.rcvbuff[0:2].decode('UTF-8', 'ignore'):
                                text += self.rcvbuff[0:2].decode('UTF-8')
                                self.rcvbuff = self.rcvbuff[2:]

                            elif len(self.rcvbuff) > 2 and self.rcvbuff[0:3].decode('UTF-8', 'ignore'):
                                text += self.rcvbuff[0:3].decode('UTF-8')
                                self.rcvbuff = self.rcvbuff[3:]

                            elif len(self.rcvbuff) > 3 and self.rcvbuff[0:4].decode('UTF-8', 'ignore'):
                                text += self.rcvbuff[0:4].decode('UTF-8')
                                self.rcvbuff = self.rcvbuff[4:]

                            elif len(self.rcvbuff) > 3:
                                text += chr(self.rcvbuff[0])
                                self.rcvbuff = self.rcvbuff[1:]

                            else:
                                break
                    
                    if len(self.txtMain.toPlainText()) > 25000: self.txtMain.clear()
                    self.txtMain.moveCursor(QtGui.QTextCursor.End)
                    self.txtMain.insertPlainText(text)

            if self.AutoInterval and self.tmrSer_Cnt % self.AutoInterval == 0:
                self.on_btnSend_clicked()

        else:
            if self.tmrSer_Cnt % 100 == 0:
                if len(comports()) != self.cmbPort.count():
                    self.cmbPort.clear()
                    for port, desc, hwid in comports():
                        self.cmbPort.addItem(f'{port} ({desc[:desc.index("(")]})')

    @pyqtSlot(str)
    def on_cmbAuto_currentIndexChanged(self, text):
        if self.cmbAuto.currentText() == 'NO Auto':
            self.AutoInterval = 0

        elif self.cmbAuto.currentText().endswith('s'):
            self.AutoInterval = float(self.cmbAuto.currentText()[:-1]) * 100

        elif self.cmbAuto.currentText().endswith('m'):
            self.AutoInterval = float(self.cmbAuto.currentText()[:-1]) * 100 * 60
    
    @pyqtSlot(int)
    def on_chkWave_stateChanged(self, state):
        self.ChartView.setVisible(state == Qt.Checked)
        self.txtMain.setVisible(state == Qt.Unchecked)
    
    @pyqtSlot(str)
    def on_cmbBaud_currentIndexChanged(self, text):
        self.ser.baudrate = int(text)
    
    @pyqtSlot(str)
    def on_cmbData_currentIndexChanged(self, text):
        self.ser.bytesize = int(text)
    
    @pyqtSlot(str)
    def on_cmbChek_currentIndexChanged(self, text):
        self.ser.parity = text[0]
    
    @pyqtSlot(str)
    def on_cmbStop_currentIndexChanged(self, text):
        self.ser.stopbits = int(text)
    
    @pyqtSlot()
    def on_btnClear_clicked(self):
        self.txtMain.clear()
    
    def closeEvent(self, evt):
        self.ser.close()

        self.conf.set('serial', 'port', self.cmbPort.currentText())
        self.conf.set('serial', 'baud', self.cmbBaud.currentText())
        self.conf.set('encode', 'input', self.cmbICode.currentText())
        self.conf.set('encode', 'output', self.cmbOCode.currentText())
        self.conf.set('encode', 'oenter', self.cmbEnter.currentText())
        self.conf.set('others', 'history', self.txtSend.toPlainText())
        self.conf.write(open('setting.ini', 'w', encoding='utf-8'))
示例#16
0
class UDPBitrate(QWidget):
    def __init__(self):
        super().__init__()

        self.mainLayout = QVBoxLayout()
        self.headerLayout = QGridLayout()
        self.portLayout = QHBoxLayout()
        self.axisxLayout = QHBoxLayout()
        self.axisyLayout = QHBoxLayout()
        self.smoothLayout = QHBoxLayout()
        self.btnsLayout = QHBoxLayout()
        self.lblPort = QLabel("Port: ")
        self.lblAxisX = QLabel("X-Axis(Time): ")
        self.lblAxisY = QLabel("Y-Axis(Bitrate): ")
        self.lblSmooth = QLabel("Smooth: ")
        self.txtPort = QLineEdit()
        self.txtAxisX = QLineEdit()
        self.txtAxisY = QLineEdit()
        self.txtSmooth = QLineEdit()
        self.chkMode = QCheckBox("Stack Mode")
        self.btnStart = QPushButton("Start")
        self.btnExport = QPushButton("Export")
        self.btnStart.clicked.connect(self.onStartClick)
        self.btnExport.clicked.connect(self.onExportClick)

        self.portLayout.addWidget(self.lblPort)
        self.portLayout.addWidget(self.txtPort)

        self.axisxLayout.addWidget(self.lblAxisX)
        self.axisxLayout.addWidget(self.txtAxisX)

        self.axisyLayout.addWidget(self.lblAxisY)
        self.axisyLayout.addWidget(self.txtAxisY)

        self.smoothLayout.addWidget(self.lblSmooth)
        self.smoothLayout.addWidget(self.txtSmooth)

        self.btnsLayout.addWidget(self.chkMode)
        self.btnsLayout.addWidget(self.btnStart)
        self.btnsLayout.addWidget(self.btnExport)

        self.headerLayout.addLayout(self.portLayout, 0, 0, 1, 3)
        self.headerLayout.addLayout(self.axisxLayout, 0, 3, 1, 2)
        self.headerLayout.addLayout(self.axisyLayout, 0, 5, 1, 2)
        self.headerLayout.addLayout(self.smoothLayout, 0, 7, 1, 2)
        self.headerLayout.addLayout(self.btnsLayout, 0, 9, 1, 2)

        self.chart = QChart()
        self.chartview = QChartView(self.chart)
        self.chartview.setRenderHint(QPainter.HighQualityAntialiasing)
        self.chart.legend().setAlignment(Qt.AlignBottom)
        self.chart.setTitle("UDP Bitrate")

        self.mainLayout.addLayout(self.headerLayout)
        self.mainLayout.addWidget(self.chartview)

        self.timer = QTimer()
        self.timer.timeout.connect(self.onTimeOut)

        self.axis_x_unit_str = ["Sec", "Min", "Hour", "Day"]
        self.axis_y_unit_str = ["bps", "Kbps", "Mbps", "Gbps"]
        self.axis_x_unit_scale = [1, 60, 3600, 3600 * 24]
        self.axis_y_unit_scale = [1, 1e3, 1e6, 1e9]
        self.axis_x_default_resolution = 1000

        self.ports = []
        self.udpthreads = []
        self.smooth_alpha = 0.0

        if os.path.exists("udpbitrate.conf"):
            with open("udpbitrate.conf", "r") as fp:
                try:
                    json_data = json.load(fp)
                    self.txtPort.setText(json_data.get("port", ""))
                    self.txtAxisX.setText(json_data.get("x-axis", "1000s"))
                    self.txtAxisY.setText(json_data.get("y-axis", "10m"))
                    self.txtSmooth.setText(json_data.get("smooth", "0"))
                    self.chkMode.setChecked(json_data.get("mode", False))
                finally:
                    pass

        self.setWindowTitle("UDP Bitrate")
        self.setLayout(self.mainLayout)
        self.resize(960, 600)
        self.show()

    def onStartClick(self):
        if self.btnStart.text() == "Start":
            self.btnStart.setText("Stop")
            self.chkMode.setEnabled(False)
            self.txtPort.setEnabled(False)
            self.txtAxisX.setEnabled(False)
            self.txtAxisY.setEnabled(False)
            self.txtSmooth.setEnabled(False)
            self.parse_port()
            self.parse_axis_x()
            self.parse_axis_y()
            self.parse_smooth()
            self.start_record()
            json_data = {
                "port": self.txtPort.text(),
                "x-axis": self.txtAxisX.text(),
                "y-axis": self.txtAxisY.text(),
                "smooth": self.txtSmooth.text(),
                "mode": self.chkMode.isChecked()
            }
            with open("udpbitrate.conf", "w") as fp:
                json.dump(json_data, fp)
        else:
            self.btnStart.setText("Start")
            self.chkMode.setEnabled(True)
            self.txtPort.setEnabled(True)
            self.txtAxisX.setEnabled(True)
            self.txtAxisY.setEnabled(True)
            self.txtSmooth.setEnabled(True)
            self.stop_record()

    def onExportClick(self):
        image = QApplication.primaryScreen().grabWindow(
            self.chartview.winId()).toImage()
        filename, filetype = QFileDialog.getSaveFileName(
            self, "Export Image", "./udpbitrate.png",
            "PNG Files (*.png);;JEPG Files (*.jpg);;All Files (*)")
        if filename:
            ext = ".jpg" if ".jpg" in filetype else ".png"
            filename = filename + ext if ext not in filename else filename
            image.save(filename)

    def onTimeOut(self):
        if self.chkMode.isChecked():
            bitrate = 0
            for i in range(len(self.udpthreads)):
                length = float(self.udpthreads[i].get_recv_length())
                bitrate += length * 8.0 / self.axis_x_step / self.axis_y_unit_scale[
                    self.axis_y_unit]
                self.add_point(bitrate, self.chart.series()[i])
        else:
            total_bitrate = 0
            for i in range(len(self.udpthreads)):
                length = float(self.udpthreads[i].get_recv_length())
                bitrate = length * 8.0 / self.axis_x_step / self.axis_y_unit_scale[
                    self.axis_y_unit]
                total_bitrate += bitrate
                self.add_point(bitrate, self.chart.series()[i])
            self.add_point(total_bitrate, self.chart.series()[-1])

    def add_point(self, val, series):
        points = series.pointsVector()
        if len(points) == 0:
            points.append(
                QPointF(
                    len(points) * self.axis_x_step /
                    self.axis_x_unit_scale[self.axis_x_unit], val))
        elif len(points) <= self.axis_x_resolution:
            val = self.smooth_alpha * points[-1].y() + (
                1 - self.smooth_alpha) * val
            points.append(
                QPointF(
                    len(points) * self.axis_x_step /
                    self.axis_x_unit_scale[self.axis_x_unit], val))
        else:
            val = self.smooth_alpha * points[-1].y() + (
                1 - self.smooth_alpha) * val
            for i in range(len(points) - 1):
                points[i] = QPointF(
                    i * self.axis_x_step /
                    self.axis_x_unit_scale[self.axis_x_unit],
                    points[i + 1].y())
            points[-1] = QPointF(
                len(points) * self.axis_x_step /
                self.axis_x_unit_scale[self.axis_x_unit], val)
        series.replace(points)

    def parse_port(self):
        self.ports = []
        s = self.txtPort.text()
        sl = map(str.strip, s.split(','))
        p = re.compile('^(\d+)\s?-\s?(\d+)$|^(\d+)$')
        for ss in sl:
            m = p.match(ss)
            if m:
                if m.group(3):
                    cur = int(m.group(3))
                    if len(self.ports) > 16:
                        break
                    if cur > 0:
                        self.ports.append(cur)
                else:
                    start, end = int(m.group(1)), int(m.group(2))
                    for cur in range(start, end + 1):
                        if len(self.ports) > 16:
                            break
                        if cur > 0:
                            self.ports.append(cur)

    def parse_axis_x(self):
        self.axis_x_val = 0
        self.axis_x_unit = 0
        s = self.txtAxisX.text().lower()
        p = re.compile(
            '^(\d+(?:\.\d+)?)\s?(s(?:ec)?|m(?:in)?|h(?:our)?|d(?:ay)?)?$')
        m = p.match(s)
        if m:
            self.axis_x_val = float(m.group(1))
            if m.group(2):
                if m.group(2) == "s" or m.group(2) == "sec":
                    self.axis_x_unit = 0
                elif m.group(2) == "m" or m.group(2) == "min":
                    self.axis_x_unit = 1
                elif m.group(2) == "h" or m.group(2) == "hour":
                    self.axis_x_unit = 2
                elif m.group(2) == "d" or m.group(2) == "day":
                    self.axis_x_unit = 3
        self.axis_x_step = self.axis_x_val * self.axis_x_unit_scale[
            self.axis_x_unit] / self.axis_x_default_resolution
        if self.axis_x_step < 0.1:
            self.axis_x_resolution = int(
                10 * self.axis_x_val *
                self.axis_x_unit_scale[self.axis_x_unit])
            self.axis_x_step = self.axis_x_val * self.axis_x_unit_scale[
                self.axis_x_unit] / self.axis_x_resolution
        else:
            self.axis_x_resolution = self.axis_x_default_resolution

    def parse_axis_y(self):
        self.axis_y_val = 0
        self.axis_y_unit = 0
        s = self.txtAxisY.text().lower()
        p = re.compile('^(\d+(?:\.\d+)?)\s?(k|m|g)?$')
        m = p.match(s)
        if m:
            self.axis_y_val = float(m.group(1))
            if m.group(2):
                if m.group(2) == "k":
                    self.axis_y_unit = 1
                elif m.group(2) == "m":
                    self.axis_y_unit = 2
                elif m.group(2) == "g":
                    self.axis_y_unit = 3

    def parse_smooth(self):
        self.smooth_alpha = 0.0
        s = self.txtSmooth.text()
        p = re.compile('^(\d+(?:\.\d+)?)\s?$')
        m = p.match(s)
        if m:
            self.smooth_alpha = float(m.group(1))
        self.smooth_alpha = self.smooth_alpha if self.smooth_alpha < 0.9999 else 0.9999

    def start_record(self):
        self.chart.removeAllSeries()
        self.series = []

        try:
            for port in self.ports:
                udp = UdpServer(port)
                udp.start()
                self.udpthreads.append(udp)
                series = QLineSeries()
                series.setName(str(port))
                self.chart.addSeries(series)
        finally:
            pass

        if not self.chkMode.isChecked():
            series = QLineSeries()
            series.setName("Total")
            self.chart.addSeries(series)

        self.chart.createDefaultAxes()

        axis_x = self.chart.axisX()
        axis_x.setRange(0, self.axis_x_val)
        axis_x.setLabelFormat("%g")
        axis_x.setTitleText("Time / " + self.axis_x_unit_str[self.axis_x_unit])

        axis_y = self.chart.axisY()
        axis_y.setRange(0, self.axis_y_val)
        axis_y.setLabelFormat("%g")
        axis_y.setTitleText("Bitrate / " +
                            self.axis_y_unit_str[self.axis_y_unit])

        self.timer.start(self.axis_x_step * 1000)

    def stop_record(self):
        if self.timer.isActive():
            self.timer.stop()
        for udp in self.udpthreads:
            udp.stop()
        self.udpthreads = []
示例#17
0
class QmyMainWindow(QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)  #调用父类构造函数,创建窗体
        self.ui = Ui_MainWindow()  #创建UI对象
        self.ui.setupUi(self)  #构造UI界面

        self.setWindowTitle("Demo12_6, 蜡烛图、日期时间坐标轴")
        self.__buildStatusBar()

        self.ui.chartView.setRenderHint(QPainter.Antialiasing)
        self.ui.chartView.setCursor(Qt.CrossCursor)  #设置鼠标指针为十字星

        ## 初始化Model/View结构
        self.itemModel = QStandardItemModel(self)
        self.ui.tableView.setModel(self.itemModel)
        self.ui.tableView.setAlternatingRowColors(True)
        self.ui.tableView.horizontalHeader().setDefaultSectionSize(80)
        self.ui.tableView.verticalHeader().setDefaultSectionSize(24)

        self.__iniChart()  # 初始化图表
        self.ui.chartView.mouseMove.connect(self.do_chartView_mouseMove)

##  ==============自定义功能函数========================

    def __buildStatusBar(self):
        self.__labChartXY = QLabel("Chart Y= ")  ##状态栏显示鼠标点的坐标
        self.__labChartXY.setMinimumWidth(200)
        self.ui.statusBar.addWidget(self.__labChartXY)

        self.__labHoverXY = QLabel("Hovered candle")
        self.__labHoverXY.setMinimumWidth(200)
        self.ui.statusBar.addWidget(self.__labHoverXY)

        self.__labClickXY = QLabel("Clicked candle")
        ##      self.__labClickXY.setMinimumWidth(200)
        self.ui.statusBar.addPermanentWidget(self.__labClickXY)

    def __iniChart(self):
        self.chart = QChart()  #创建 Chart
        self.chart.setTitle("股票日线图")
        self.chart.setTheme(
            QChart.ChartThemeBlueCerulean
        )  #ChartThemeBlueCerulean, ChartThemeQt,ChartThemeBlueNcs,ChartThemeDark
        self.ui.chartView.setChart(self.chart)  #Chart添加到ChartView

        ## X 轴是QDateTimeAxis
        self.__axisX = QDateTimeAxis()
        dateFormat = self.ui.comboDateFormat.currentText()  #如"MM-dd"
        self.__axisX.setFormat(dateFormat)  #标签格式
        self.__axisX.setTickCount(10)  #主分隔个数
        self.__axisX.setTitleText("日期")  #标题

        dateMin = QDateTime.fromString("2018-01-01", "yyyy-MM-dd")
        self.__axisX.setMin(dateMin)
        dateMax = dateMin.addDays(150)
        self.__axisX.setMax(dateMax)
        self.chart.addAxis(self.__axisX, Qt.AlignBottom)

        ## Y 轴是 QValueAxis
        self.__axisY = QValueAxis()
        self.__axisY.setTitleText("Value")
        self.__axisY.setRange(0, 20)
        self.__axisY.setTickCount(5)
        self.__axisY.setLabelFormat("%.2f")  #标签格式
        self.chart.addAxis(self.__axisY, Qt.AlignLeft)

    def __loadData(self, allLines):  ##从字符串列表读取数据构建数据模型
        rowCount = len(allLines)  #文本行数,第1行是标题
        self.itemModel.setRowCount(rowCount - 1)  #实际数据行数
        ## 设置表头
        header = allLines[0].strip()  #第1行是表头
        headerList = header.split()
        self.itemModel.setHorizontalHeaderLabels(headerList)  #设置表头文字

        colCount = len(headerList)  #列数
        self.itemModel.setColumnCount(colCount)  #数据列数

        ## 设置模型数据
        for i in range(rowCount - 1):
            lineText = allLines[i + 1].strip()  #获取 数据区 的一行
            tmpList = lineText.split()
            for j in range(colCount):
                item = QStandardItem(tmpList[j])  #创建item
                item.setTextAlignment(Qt.AlignHCenter)
                self.itemModel.setItem(i, j, item)  #为模型的某个行列位置设置Item

    def __drawChart(self):  ##绘制图表
        self.chart.removeAllSeries()  #删除所有序列
        self.chart.setTitle("股票日线图--" + self.ui.tabWidget.tabText(0))

        ## 1. 创建蜡烛图
        seriesCandle = QCandlestickSeries()
        seriesCandle.setName("蜡烛图")
        seriesCandle.setIncreasingColor(Qt.red)  #暴涨
        seriesCandle.setDecreasingColor(Qt.darkGreen)  #暴跌

        visible = self.ui.chkBox_Outline.isChecked()
        seriesCandle.setBodyOutlineVisible(visible)
        seriesCandle.setCapsVisible(self.ui.chkBox_Caps.isChecked())

        self.chart.addSeries(seriesCandle)
        seriesCandle.attachAxis(self.__axisX)
        seriesCandle.attachAxis(self.__axisY)

        seriesCandle.clicked.connect(self.do_candleClicked)
        seriesCandle.hovered.connect(self.do_candleHovered)

        ## 2. 创建MA曲线
        pen = QPen()
        pen.setWidth(2)

        seriesMA1 = QLineSeries()  #不能使用QSplineSeries
        seriesMA1.setName("MA5")
        pen.setColor(Qt.magenta)
        seriesMA1.setPen(pen)
        self.chart.addSeries(seriesMA1)
        seriesMA1.attachAxis(self.__axisX)
        seriesMA1.attachAxis(self.__axisY)

        seriesMA2 = QLineSeries()
        seriesMA2.setName("MA10")
        pen.setColor(Qt.yellow)
        seriesMA2.setPen(pen)
        self.chart.addSeries(seriesMA2)
        seriesMA2.attachAxis(self.__axisX)
        seriesMA2.attachAxis(self.__axisY)

        seriesMA3 = QLineSeries()
        seriesMA3.setName("MA20")
        pen.setColor(Qt.cyan)
        seriesMA3.setPen(pen)
        self.chart.addSeries(seriesMA3)
        seriesMA3.attachAxis(self.__axisX)
        seriesMA3.attachAxis(self.__axisY)

        seriesMA4 = QLineSeries()
        seriesMA4.setName("MA60")
        pen.setColor(Qt.green)  #green
        seriesMA4.setPen(pen)
        self.chart.addSeries(seriesMA4)
        seriesMA4.attachAxis(self.__axisX)
        seriesMA4.attachAxis(self.__axisY)

        ## 3. 填充数据到序列
        dataRowCount = self.itemModel.rowCount()  #数据点个数
        for i in range(dataRowCount):
            dateStr = self.itemModel.item(i, 0).text()  #日期字符串,如"2017/02/03"
            dateValue = QDate.fromString(dateStr, "yyyy/MM/dd")  #QDate
            dtValue = QDateTime(dateValue)  #日期时间 QDateTime
            timeStamp = dtValue.toMSecsSinceEpoch()  #毫秒数

            oneCandle = QCandlestickSet()  #QCandlestickSet
            oneCandle.setOpen(float(self.itemModel.item(i, 1).text()))  #开盘
            oneCandle.setHigh(float(self.itemModel.item(i, 2).text()))  #最高
            oneCandle.setLow(float(self.itemModel.item(i, 3).text()))  #最低
            oneCandle.setClose(float(self.itemModel.item(i, 4).text()))  #收盘
            oneCandle.setTimestamp(timeStamp)  #时间戳
            seriesCandle.append(oneCandle)  #添加到序列

            M1 = float(self.itemModel.item(i, 5).text())
            M2 = float(self.itemModel.item(i, 6).text())
            M3 = float(self.itemModel.item(i, 7).text())
            M4 = float(self.itemModel.item(i, 8).text())

            seriesMA1.append(timeStamp, M1)
            seriesMA2.append(timeStamp, M2)
            seriesMA3.append(timeStamp, M3)
            seriesMA4.append(timeStamp, M4)

    ## 4. 设置坐标轴范围
        minDateStr = self.itemModel.item(0, 0).text()  #日期字符串,如"2017/02/03"
        minDate = QDate.fromString(minDateStr, "yyyy/MM/dd")  #QDate
        minDateTime = QDateTime(minDate)  #最小日期时间,QDateTime

        maxDateStr = self.itemModel.item(dataRowCount - 1,
                                         0).text()  #日期字符串,如"2017/05/03"
        maxDate = QDate.fromString(maxDateStr, "yyyy/MM/dd")
        maxDateTime = QDateTime(maxDate)  #最大日期时间

        self.__axisX.setRange(minDateTime, maxDateTime)  #日期时间范围
        dateFormat = self.ui.comboDateFormat.currentText()  #格式,如"MM-dd"
        self.__axisX.setFormat(dateFormat)  #标签格式

        self.__axisY.applyNiceNumbers()  #自动

        for marker in self.chart.legend().markers():  #QLegendMarker类型列表
            marker.clicked.connect(self.do_LegendMarkerClicked)

##  ==============event处理函数==========================

##  ==========由connectSlotsByName()自动连接的槽函数============

    @pyqtSlot()  ##“打开文件”按钮
    def on_actOpen_triggered(self):
        curPath = QDir.currentPath()
        filename, flt = QFileDialog.getOpenFileName(
            self, "打开一个文件", curPath, "股票数据文件(*.txt);;所有文件(*.*)")
        if (filename == ""):
            return

        aFile = open(filename, 'r')
        allLines = aFile.readlines()  #读取所有行,list类型,每行末尾带有 \n
        aFile.close()
        fileInfo = QFileInfo(filename)
        QDir.setCurrent(fileInfo.absolutePath())
        self.ui.tabWidget.setTabText(0, fileInfo.baseName())

        self.__loadData(allLines)  # 载入数据到数据模型
        self.__drawChart()  # 绘制图表
        self.ui.tab_Setup.setEnabled(True)

    @pyqtSlot()
    def on_actZoomIn_triggered(self):
        self.ui.chartView.chart().zoom(1.2)

    @pyqtSlot()
    def on_actZoomOut_triggered(self):
        self.ui.chartView.chart().zoom(0.8)

    @pyqtSlot()
    def on_actZoomReset_triggered(self):
        self.ui.chartView.chart().zoomReset()

##图表外观控制

    @pyqtSlot(int)  ##主题
    def on_comboTheme_currentIndexChanged(self, index):
        self.ui.chartView.chart().setTheme(QChart.ChartTheme(index))

    @pyqtSlot(bool)  ##显示图例
    def on_chkBox_Legend_clicked(self, checked):
        self.ui.chartView.chart().legend().setVisible(checked)

    ## 蜡烛图
    @pyqtSlot(bool)  ##capsVisible
    def on_chkBox_Caps_clicked(self, checked):
        seriesCandle = self.chart.series()[0]
        seriesCandle.setCapsVisible(checked)

    @pyqtSlot(bool)  ##bodyOutlineVisible
    def on_chkBox_Outline_clicked(self, checked):
        seriesCandle = self.chart.series()[0]
        seriesCandle.setBodyOutlineVisible(checked)

## Y轴--QValueAxis

    @pyqtSlot()  ##设置坐标范围
    def on_btnY_SetRange_clicked(self):
        self.__axisY.setRange(self.ui.spinY_Min.value(),
                              self.ui.spinY_Max.value())

    @pyqtSlot(int)  ##分度数
    def on_spinY_Ticks_valueChanged(self, arg1):
        self.__axisY.setTickCount(arg1)

    ## X轴--QDateTimeAxis
    @pyqtSlot(str)  ##标签格式
    def on_comboDateFormat_currentIndexChanged(self, arg1):
        self.__axisX.setFormat(arg1)

    @pyqtSlot(int)  ##分度数
    def on_btnX_Ticks_valueChanged(self, arg1):
        self.__axisX.setTickCount(arg1)

##  =============自定义槽函数===============================

    def do_LegendMarkerClicked(self):
        marker = self.sender()  #QLegendMarker

        marker.series().setVisible(not marker.series().isVisible())
        marker.setVisible(True)
        alpha = 1.0
        if not marker.series().isVisible():
            alpha = 0.5

        brush = marker.labelBrush()  #QBrush
        color = brush.color()  #QColor
        color.setAlphaF(alpha)
        brush.setColor(color)
        marker.setLabelBrush(brush)

        brush = marker.brush()
        color = brush.color()
        color.setAlphaF(alpha)
        brush.setColor(color)
        marker.setBrush(brush)

        pen = marker.pen()  #QPen
        color = pen.color()
        color.setAlphaF(alpha)
        pen.setColor(color)
        marker.setPen(pen)

        if marker.type() == QLegendMarker.LegendMarkerTypeCandlestick:
            QMessageBox.information(self, "提示", "蜡烛图序列无法隐藏")

    def do_chartView_mouseMove(self, point):
        pt = self.chart.mapToValue(point)  #QPointF 转换为图表的数值
        self.__labChartXY.setText("Chart Y=%.2f" % (pt.y()))  #状态栏显示

    def do_candleClicked(self, dataSet):
        valOpen = dataSet.open()
        valClose = dataSet.close()
        valHigh = dataSet.high()
        valLow = dataSet.low()
        price = "开盘%.2f, 收盘%.2f, 最高%.2f, 最低%.2f" % (valOpen, valClose, valHigh,
                                                    valLow)

        timeStamp = dataSet.timestamp()  #时间戳数据
        dt = QDateTime.fromMSecsSinceEpoch(timeStamp)
        dateStr = dt.toString("yyyy-MM-dd, ")

        self.__labClickXY.setText(dateStr + price)

    def do_candleHovered(self, status, dataSet):
        if status == False:
            self.__labHoverXY.setText("Hovered candle")
            return

        valOpen = dataSet.open()
        valClose = dataSet.close()
        valHigh = dataSet.high()
        valLow = dataSet.low()
        price = "开盘%.2f, 收盘%.2f, 最高%.2f, 最低%.2f" % (valOpen, valClose, valHigh,
                                                    valLow)

        timeStamp = dataSet.timestamp()  #时间戳数据
        dt = QDateTime.fromMSecsSinceEpoch(timeStamp)
        dateStr = dt.toString("yyyy-MM-dd, ")

        self.__labHoverXY.setText(dateStr + price)
示例#18
0
class RTTView(QWidget):
    def __init__(self, parent=None):
        super(RTTView, self).__init__(parent)

        uic.loadUi('RTTView.ui', self)

        self.initSetting()

        self.initQwtPlot()

        self.rcvbuff = b''

        self.tmrRTT = QtCore.QTimer()
        self.tmrRTT.setInterval(10)
        self.tmrRTT.timeout.connect(self.on_tmrRTT_timeout)
        self.tmrRTT.start()

        self.tmrRTT_Cnt = 0
        self.tmrDAP_Cnt = 0

    def initSetting(self):
        if not os.path.exists('setting.ini'):
            open('setting.ini', 'w', encoding='utf-8')

        self.conf = configparser.ConfigParser()
        self.conf.read('setting.ini', encoding='utf-8')

        if not self.conf.has_section('J-Link'):
            self.conf.add_section('J-Link')
            self.conf.set('J-Link', 'dllpath', '')
            self.conf.add_section('Memory')
            self.conf.set('Memory', 'rttaddr', '0x20000000')

        self.cmbDLL.addItem(self.conf.get('J-Link', 'dllpath'))
        self.linRTT.setText(self.conf.get('Memory', 'rttaddr'))

    def initQwtPlot(self):
        self.PlotData = [[0] * 1000 for i in range(N_CURVES)]
        self.PlotPoint = [[QtCore.QPointF(j, 0) for j in range(1000)]
                          for i in range(N_CURVES)]

        self.PlotChart = QChart()

        self.ChartView = QChartView(self.PlotChart)
        self.ChartView.setVisible(False)
        self.vLayout.insertWidget(0, self.ChartView)

        self.PlotCurve = [QLineSeries() for i in range(N_CURVES)]

    @pyqtSlot()
    def on_btnOpen_clicked(self):
        if self.btnOpen.text() == '打开连接':
            try:
                if self.cmbDLL.currentIndex() == 0:
                    self.xlk = xlink.XLink(
                        jlink.JLink(self.cmbDLL.currentText(), 'Cortex-M0'))

                else:
                    from pyocd.coresight import dap, ap, cortex_m
                    daplink = self.daplinks[self.cmbDLL.currentIndex() - 1]
                    daplink.open()

                    _dp = dap.DebugPort(daplink, None)
                    _dp.init()
                    _dp.power_up_debug()

                    _ap = ap.AHB_AP(_dp, 0)
                    _ap.init()

                    self.xlk = xlink.XLink(cortex_m.CortexM(None, _ap))

                addr = int(self.linRTT.text(), 16)
                for i in range(128):
                    data = self.xlk.read_mem_U8(addr + 1024 * i,
                                                1024 + 32)  # 多读32字节,防止搜索内容在边界处
                    index = bytes(data).find(b'SEGGER RTT')
                    if index != -1:
                        self.RTTAddr = addr + 1024 * i + index

                        data = self.xlk.read_mem_U8(
                            self.RTTAddr, ctypes.sizeof(SEGGER_RTT_CB))

                        rtt_cb = SEGGER_RTT_CB.from_buffer(bytearray(data))
                        self.aUpAddr = self.RTTAddr + 16 + 4 + 4
                        self.aDownAddr = self.aUpAddr + ctypes.sizeof(
                            RingBuffer) * rtt_cb.MaxNumUpBuffers

                        self.txtMain.append(
                            f'\n_SEGGER_RTT @ 0x{self.RTTAddr:08X} with {rtt_cb.MaxNumUpBuffers} aUp and {rtt_cb.MaxNumDownBuffers} aDown\n'
                        )
                        break

                else:
                    raise Exception('Can not find _SEGGER_RTT')

            except Exception as e:
                self.txtMain.append(f'\n{str(e)}\n')

            else:
                self.cmbDLL.setEnabled(False)
                self.btnDLL.setEnabled(False)
                self.linRTT.setEnabled(False)
                self.btnOpen.setText('关闭连接')

        else:
            self.xlk.close()
            self.cmbDLL.setEnabled(True)
            self.btnDLL.setEnabled(True)
            self.linRTT.setEnabled(True)
            self.btnOpen.setText('打开连接')

    def aUpRead(self):
        data = self.xlk.read_mem_U8(self.aUpAddr, ctypes.sizeof(RingBuffer))

        aUp = RingBuffer.from_buffer(bytearray(data))

        if aUp.RdOff <= aUp.WrOff:
            cnt = aUp.WrOff - aUp.RdOff

        else:
            cnt = aUp.SizeOfBuffer - aUp.RdOff

        if 0 < cnt < 1024 * 1024:
            data = self.xlk.read_mem_U8(
                ctypes.cast(aUp.pBuffer, ctypes.c_void_p).value + aUp.RdOff,
                cnt)

            aUp.RdOff = (aUp.RdOff + cnt) % aUp.SizeOfBuffer

            self.xlk.write_U32(self.aUpAddr + 4 * 4, aUp.RdOff)

        else:
            data = []

        return bytes(data)

    def aDownWrite(self, bytes):
        data = self.xlk.read_mem_U8(self.aDownAddr, ctypes.sizeof(RingBuffer))

        aDown = RingBuffer.from_buffer(bytearray(data))

        if aDown.WrOff >= aDown.RdOff:
            if aDown.RdOff != 0:
                cnt = min(aDown.SizeOfBuffer - aDown.WrOff, len(bytes))
            else:
                cnt = min(
                    aDown.SizeOfBuffer - 1 - aDown.WrOff,
                    len(bytes))  # 写入操作不能使得 aDown.WrOff == aDown.RdOff,以区分满和空
            self.xlk.write_mem(
                ctypes.cast(aDown.pBuffer, ctypes.c_void_p).value +
                aDown.WrOff, bytes[:cnt])

            aDown.WrOff += cnt
            if aDown.WrOff == aDown.SizeOfBuffer: aDown.WrOff = 0

            bytes = bytes[cnt:]

        if bytes and aDown.RdOff != 0 and aDown.RdOff != 1:  # != 0 确保 aDown.WrOff 折返回 0,!= 1 确保有空间可写入
            cnt = min(aDown.RdOff - 1 - aDown.WrOff,
                      len(bytes))  # - 1 确保写入操作不导致WrOff与RdOff指向同一位置
            self.xlk.write_mem(
                ctypes.cast(aDown.pBuffer, ctypes.c_void_p).value +
                aDown.WrOff, bytes[:cnt])

            aDown.WrOff += cnt

        self.xlk.write_U32(self.aDownAddr + 4 * 3, aDown.WrOff)

    def on_tmrRTT_timeout(self):
        if self.btnOpen.text() == '关闭连接':
            try:
                self.rcvbuff += self.aUpRead()

                if self.txtMain.isVisible():
                    if self.chkHEXShow.isChecked():
                        text = ''.join(f'{x:02X} ' for x in self.rcvbuff)

                    else:
                        text = self.rcvbuff.decode('latin')

                    if len(self.txtMain.toPlainText()) > 25000:
                        self.txtMain.clear()
                    self.txtMain.moveCursor(QtGui.QTextCursor.End)
                    self.txtMain.insertPlainText(text)

                    self.rcvbuff = b''

                else:
                    if self.rcvbuff.rfind(b',') == -1: return

                    d = self.rcvbuff[0:self.rcvbuff.rfind(b',')].split(
                        b',')  # [b'12', b'34'] or [b'12 34', b'56 78']
                    d = [[float(x) for x in X.strip().split()]
                         for X in d]  # [[12], [34]]   or [[12, 34], [56, 78]]
                    for arr in d:
                        for i, x in enumerate(arr):
                            if i == N_CURVES: break

                            self.PlotData[i].pop(0)
                            self.PlotData[i].append(x)
                            self.PlotPoint[i].pop(0)
                            self.PlotPoint[i].append(QtCore.QPointF(999, x))

                    self.rcvbuff = self.rcvbuff[self.rcvbuff.rfind(b',') + 1:]

                    self.tmrRTT_Cnt += 1
                    if self.tmrRTT_Cnt % 4 == 0:
                        if len(d[-1]) != len(self.PlotChart.series()):
                            for series in self.PlotChart.series():
                                self.PlotChart.removeSeries(series)
                            for i in range(min(len(d[-1]), N_CURVES)):
                                self.PlotCurve[i].setName(f'Curve {i+1}')
                                self.PlotChart.addSeries(self.PlotCurve[i])
                            self.PlotChart.createDefaultAxes()

                        for i in range(len(self.PlotChart.series())):
                            for j, point in enumerate(self.PlotPoint[i]):
                                point.setX(j)

                            self.PlotCurve[i].replace(self.PlotPoint[i])

                        miny = min([
                            min(d) for d in
                            self.PlotData[:len(self.PlotChart.series())]
                        ])
                        maxy = max([
                            max(d) for d in
                            self.PlotData[:len(self.PlotChart.series())]
                        ])
                        self.PlotChart.axisY().setRange(miny, maxy)
                        self.PlotChart.axisX().setRange(0000, 1000)

            except Exception as e:
                self.rcvbuff = b''
                print(str(e))  # 波形显示模式下 txtMain 不可见,因此错误信息不能显示在其上

        else:
            self.tmrDAP_Cnt += 1
            if self.tmrDAP_Cnt % 100 == 0:
                try:
                    from pyocd.probe import aggregator
                    self.daplinks = aggregator.DebugProbeAggregator.get_all_connected_probes(
                    )
                    if len(self.daplinks) != self.cmbDLL.count() - 1:
                        for i in range(1, self.cmbDLL.count()):
                            self.cmbDLL.removeItem(i)
                        for i, daplink in enumerate(self.daplinks):
                            self.cmbDLL.addItem(daplink.product_name)
                except Exception as e:
                    pass

    @pyqtSlot()
    def on_btnSend_clicked(self):
        if self.btnOpen.text() == '关闭连接':
            text = self.txtSend.toPlainText()

            try:
                if self.chkHEXSend.isChecked():
                    text = ''.join([chr(int(x, 16)) for x in text.split()])

                self.aDownWrite(text.encode('latin'))

            except Exception as e:
                self.txtMain.append(f'\n{str(e)}\n')

    @pyqtSlot()
    def on_btnDLL_clicked(self):
        dllpath, filter = QFileDialog.getOpenFileName(
            caption='JLink_x64.dll路径',
            filter='动态链接库文件 (*.dll)',
            directory=self.cmbDLL.itemText(0))
        if dllpath != '':
            self.cmbDLL.setItemText(0, dllpath)

    @pyqtSlot(int)
    def on_chkWavShow_stateChanged(self, state):
        self.ChartView.setVisible(state == Qt.Checked)
        self.txtMain.setVisible(state == Qt.Unchecked)

    @pyqtSlot()
    def on_btnClear_clicked(self):
        self.txtMain.clear()

    def closeEvent(self, evt):
        self.conf.set('J-Link', 'dllpath', self.cmbDLL.itemText(0))
        self.conf.set('Memory', 'rttaddr', self.linRTT.text())
        self.conf.write(open('setting.ini', 'w', encoding='utf-8'))
示例#19
0
class LeftBarPieChart(QWidget):
    def __init__(self, parent):
        super(LeftBarPieChart, self).__init__()
        self.parent = parent
        self.setParent(parent)

        self.chart = None
        self.inputs = []
        self.layout = None
        self.layout_inp = None
        self.btn_add = None
        self.w_inp = None
        self.header = None

        self.init_ui()

    def init_ui(self):

        self.chart = QChart()
        self.chart.setTheme(QChart.ChartThemeBlueIcy)
        self.chart.setAnimationDuration(1000)
        self.chart.setAnimationOptions(QChart.AllAnimations)
        self.serie = QPieSeries()

        self.layout = QVBoxLayout()
        self.layout_inp = QVBoxLayout()
        self.w_inp = QWidget()
        self.w_inp.setStyleSheet("""
            .QWidget{
                background-color: transparent;
            }
        """)

        self.w_inp.setLayout(self.layout_inp)

        self.setLayout(self.layout)
        self.inputs.append(InputPieChart(self))

        self.btn_add = QPushButton("+")
        self.btn_remove = QPushButton("-")
        self.btn_add.clicked.connect(self.add_input)
        self.btn_remove.clicked.connect(self.remove_input)
        self.btn_add.setStyleSheet("""
            border: 0px;
            background: green;
            color: white;
        """)
        self.btn_remove.setStyleSheet("""
            border: 0px;
            background: red;
            color: white;
        """)
        self.header = HeaderPieChartInput()
        self.layout.addWidget(self.header)
        self.layout.addWidget(self.w_inp)
        self.layout.addStretch(-1)
        self.layout.addWidget(self.btn_remove)
        self.layout.addWidget(self.btn_add)
        self.layout_inp.addWidget(self.inputs[0])

        self.serie.append(self.inputs[0].name.text(), 2)

        # self.slice.setExploded()
        # self.slice.setLabelVisible()
        # self.slice.setPen(QPen(Qt.darkGreen,2))
        # self.slice.setBrush(Qt.green)

        self.chart.addSeries(self.serie)
        self.chart.setTitle("Pie Chart")

    def add_input(self):
        if len(self.inputs) < 10:
            self.inputs.append(InputPieChart(self))
            self.layout_inp.addWidget(self.inputs[-1])
            self.serie.append(self.inputs[-1].name.text(),
                              int(self.inputs[-1].value.text()))
            # for slice in self.serie.slices():
            #     slice.setBrush(Qt.green)

    def remove_input(self):
        if len(self.inputs) >= 1:
            self.layout_inp.removeWidget(self.inputs[-1])
            self.inputs[-1].hide()
            self.inputs.remove(self.inputs[-1])
            self.slc = self.serie.slices()[-1]
            self.serie.remove(self.slc)

            # for slice in self.serie.slices():
            #     slice.setBrush(Qt.green)

    def input_changed(self, txt):
        if self.serie in self.chart.series():
            self.chart.removeSeries(self.serie)
            self.serie = QPieSeries()
            for inp in self.inputs:
                self.serie.append(inp.name.text(), int(inp.value.text()))
            self.chart.addSeries(self.serie)
示例#20
0
class QtAgentGraph(QChartView):
    def __init__(self, spec):
        super().__init__(None)
        self.spec = spec
        self.chart = QChart()
        self.chart.setTitle(str(self.spec.variable))
        self.chart.legend().hide()

        self.setMinimumWidth(400)
        self.setMinimumHeight(230)

        self.setChart(self.chart)
        self.setRenderHint(QPainter.Antialiasing)
        self.chart.createDefaultAxes()
        self.autoscale_y_axis = True
        if self.spec.min_y and self.spec.max_y:
            self.autoscale_y_axis = False
            self.chart.axes()[1].setRange(self.spec.min_y, self.spec.max_y)

        self.axis_x = QValueAxis()
        self.axis_y = QValueAxis()
        self.chart.addAxis(self.axis_x, Qt.AlignBottom)
        self.chart.addAxis(self.axis_y, Qt.AlignLeft)

        self._updates_per_second = 60
        self._data = []
        self._min = 0
        self._max = 0

    def clear(self):
        for chart in self.chart.series():
            chart.clear()
        self._data = []

    def update_data(self):
        for a in self.spec.agents:
            if not hasattr(a, "_agent_series"):
                a._agent_series = QLineSeries()
                a._agent_series.setColor(
                    QColor(a.color[0], a.color[1], a.color[2]))
                a._agent_series_data = [getattr(a, self.spec.variable)]
                self.chart.addSeries(a._agent_series)
                a._agent_series.attachAxis(self.chart.axisX())
                a._agent_series.attachAxis(self.chart.axisY())
            else:
                a._agent_series_data.append(getattr(a, self.spec.variable))

    def redraw(self):
        for a in self.spec.agents:
            if hasattr(a, "_agent_series") and len(a._agent_series_data) > 0:
                datapoint = sum(a._agent_series_data) / len(
                    a._agent_series_data)
                a._agent_series.append(
                    QPointF(
                        a._agent_series.count() / self._updates_per_second,
                        datapoint,
                    ))
                self._min = min(self._min, datapoint)
                self._max = max(self._max, datapoint)
                a._agent_series.setColor(
                    QColor(a.color[0], a.color[1], a.color[2]))
            a._agent_series_data = []
        if len(self.spec.agents) > 0:
            first_agent = self.spec.agents[0]
            if hasattr(first_agent, "_agent_series"):
                first_series = first_agent._agent_series
                self.chart.axes()[0].setRange(0, (first_series.count() - 1) /
                                              self._updates_per_second)
                diff = self._max - self._min
                if self.autoscale_y_axis:
                    if diff > 0:
                        self.chart.axes()[1].setRange(self._min, self._max)
                    else:
                        self.chart.axes()[1].setRange(self._min - 0.5,
                                                      self._max + 0.5)
示例#21
0
class RTTView(QWidget):
    def __init__(self, parent=None):
        super(RTTView, self).__init__(parent)

        uic.loadUi('RTTView.ui', self)

        self.initSetting()

        self.initQwtPlot()

        self.rcvbuff = b''

        self.tmrRTT = QtCore.QTimer()
        self.tmrRTT.setInterval(10)
        self.tmrRTT.timeout.connect(self.on_tmrRTT_timeout)
        self.tmrRTT.start()

        self.tmrRTT_Cnt = 0

    def initSetting(self):
        if not os.path.exists('setting.ini'):
            open('setting.ini', 'w', encoding='utf-8')

        self.conf = configparser.ConfigParser()
        self.conf.read('setting.ini', encoding='utf-8')

        if not self.conf.has_section('J-Link'):
            self.conf.add_section('J-Link')
            self.conf.set('J-Link', 'dllpath', '')
            self.conf.set('J-Link', 'mcucore', 'Cortex-M0')
            self.conf.add_section('Memory')
            self.conf.set('Memory', 'rttaddr', '0x20000000')

        self.linDLL.setText(self.conf.get('J-Link', 'dllpath'))
        self.linRTT.setText(self.conf.get('Memory', 'rttaddr'))
        self.cmbCore.setCurrentIndex(
            self.cmbCore.findText(self.conf.get('J-Link', 'mcucore')))

    def initQwtPlot(self):
        self.PlotData = [[0] * 1000 for i in range(N_CURVES)]
        self.PlotPoint = [[QtCore.QPointF(j, 0) for j in range(1000)]
                          for i in range(N_CURVES)]

        self.PlotChart = QChart()

        self.ChartView = QChartView(self.PlotChart)
        self.ChartView.setVisible(False)
        self.vLayout.insertWidget(0, self.ChartView)

        self.PlotCurve = [QLineSeries() for i in range(N_CURVES)]

    @pyqtSlot()
    def on_btnOpen_clicked(self):
        if self.btnOpen.text() == '打开连接':
            try:
                self.jlink = ctypes.cdll.LoadLibrary(self.linDLL.text())

                err_buf = (ctypes.c_char * 64)()
                self.jlink.JLINKARM_ExecCommand(
                    f'Device = {self.cmbCore.currentText()}'.encode('latin-1'),
                    err_buf, 64)

                self.jlink.JLINKARM_TIF_Select(1)
                self.jlink.JLINKARM_SetSpeed(4000)

                buff = ctypes.create_string_buffer(1024)
                Addr = int(self.linRTT.text(), 16)
                for i in range(128):
                    self.jlink.JLINKARM_ReadMem(Addr + 1024 * i, 1024, buff)
                    index = buff.raw.find(b'SEGGER RTT')
                    if index != -1:
                        self.RTTAddr = Addr + 1024 * i + index

                        buff = ctypes.create_string_buffer(
                            ctypes.sizeof(SEGGER_RTT_CB))
                        self.jlink.JLINKARM_ReadMem(
                            self.RTTAddr, ctypes.sizeof(SEGGER_RTT_CB), buff)

                        rtt_cb = SEGGER_RTT_CB.from_buffer(buff)
                        self.aUpAddr = self.RTTAddr + 16 + 4 + 4
                        self.aDownAddr = self.aUpAddr + ctypes.sizeof(
                            RingBuffer) * rtt_cb.MaxNumUpBuffers

                        self.txtMain.append(
                            f'\n_SEGGER_RTT @ 0x{self.RTTAddr:08X} with {rtt_cb.MaxNumUpBuffers} aUp and {rtt_cb.MaxNumDownBuffers} aDown\n'
                        )
                        break
                else:
                    raise Exception('Can not find _SEGGER_RTT')
            except Exception as e:
                self.txtMain.append(f'\n{str(e)}\n')
            else:
                self.linDLL.setEnabled(False)
                self.btnDLL.setEnabled(False)
                self.linRTT.setEnabled(False)
                self.cmbCore.setEnabled(False)
                self.btnOpen.setText('关闭连接')
        else:
            self.linDLL.setEnabled(True)
            self.btnDLL.setEnabled(True)
            self.linRTT.setEnabled(True)
            self.cmbCore.setEnabled(True)
            self.btnOpen.setText('打开连接')

    def aUpRead(self):
        buf = ctypes.create_string_buffer(ctypes.sizeof(RingBuffer))
        self.jlink.JLINKARM_ReadMem(self.aUpAddr, ctypes.sizeof(RingBuffer),
                                    buf)

        aUp = RingBuffer.from_buffer(buf)

        if aUp.RdOff == aUp.WrOff:
            str = ctypes.create_string_buffer(0)

        elif aUp.RdOff < aUp.WrOff:
            cnt = aUp.WrOff - aUp.RdOff
            str = ctypes.create_string_buffer(cnt)
            self.jlink.JLINKARM_ReadMem(
                ctypes.cast(aUp.pBuffer, ctypes.c_void_p).value + aUp.RdOff,
                cnt, str)

            aUp.RdOff += cnt

            self.jlink.JLINKARM_WriteU32(self.aUpAddr + 4 * 4, aUp.RdOff)

        else:
            cnt = aUp.SizeOfBuffer - aUp.RdOff
            str = ctypes.create_string_buffer(cnt)
            self.jlink.JLINKARM_ReadMem(
                ctypes.cast(aUp.pBuffer, ctypes.c_void_p).value + aUp.RdOff,
                cnt, str)

            aUp.RdOff = 0  #这样下次再读就会进入执行上个条件

            self.jlink.JLINKARM_WriteU32(self.aUpAddr + 4 * 4, aUp.RdOff)

        return str.raw

    def on_tmrRTT_timeout(self):
        if self.btnOpen.text() == '关闭连接':
            try:
                self.rcvbuff += self.aUpRead()

                if self.txtMain.isVisible():
                    if self.chkHEXShow.isChecked():
                        text = ''.join(f'{x:02X} ' for x in self.rcvbuff)

                    else:
                        text = self.rcvbuff.decode('latin')

                    if len(self.txtMain.toPlainText()) > 25000:
                        self.txtMain.clear()
                    self.txtMain.moveCursor(QtGui.QTextCursor.End)
                    self.txtMain.insertPlainText(text)

                    self.rcvbuff = b''

                else:
                    if self.rcvbuff.rfind(b',') == -1: return

                    d = self.rcvbuff[0:self.rcvbuff.rfind(b',')].split(
                        b',')  # [b'12', b'34'] or [b'12 34', b'56 78']
                    d = [[float(x) for x in X.strip().split()]
                         for X in d]  # [[12], [34]]   or [[12, 34], [56, 78]]
                    for arr in d:
                        for i, x in enumerate(arr):
                            if i == N_CURVES: break

                            self.PlotData[i].pop(0)
                            self.PlotData[i].append(x)
                            self.PlotPoint[i].pop(0)
                            self.PlotPoint[i].append(QtCore.QPointF(999, x))

                    self.rcvbuff = self.rcvbuff[self.rcvbuff.rfind(b',') + 1:]

                    self.tmrRTT_Cnt += 1
                    if self.tmrRTT_Cnt % 4 == 0:
                        if len(d[-1]) != len(self.PlotChart.series()):
                            for series in self.PlotChart.series():
                                self.PlotChart.removeSeries(series)
                            for i in range(min(len(d[-1]), N_CURVES)):
                                self.PlotCurve[i].setName(f'Curve {i+1}')
                                self.PlotChart.addSeries(self.PlotCurve[i])
                            self.PlotChart.createDefaultAxes()

                        for i in range(len(self.PlotChart.series())):
                            for j, point in enumerate(self.PlotPoint[i]):
                                point.setX(j)

                            self.PlotCurve[i].replace(self.PlotPoint[i])

                        miny = min([
                            min(d) for d in
                            self.PlotData[:len(self.PlotChart.series())]
                        ])
                        maxy = max([
                            max(d) for d in
                            self.PlotData[:len(self.PlotChart.series())]
                        ])
                        self.PlotChart.axisY().setRange(miny, maxy)
                        self.PlotChart.axisX().setRange(0000, 1000)

            except Exception as e:
                self.rcvbuff = b''
                print(str(e))  # 波形显示模式下 txtMain 不可见,因此错误信息不能显示在其上

    def aDownWrite(self, bytes):
        buf = ctypes.create_string_buffer(ctypes.sizeof(RingBuffer))
        self.jlink.JLINKARM_ReadMem(self.aDownAddr, ctypes.sizeof(RingBuffer),
                                    buf)

        aDown = RingBuffer.from_buffer(buf)

        if aDown.WrOff >= aDown.RdOff:
            if aDown.RdOff != 0:
                cnt = min(aDown.SizeOfBuffer - aDown.WrOff, len(bytes))
            else:
                cnt = min(
                    aDown.SizeOfBuffer - 1 - aDown.WrOff,
                    len(bytes))  # 写入操作不能使得 aDown.WrOff == aDown.RdOff,以区分满和空
            str = ctypes.create_string_buffer(bytes[:cnt])
            self.jlink.JLINKARM_WriteMem(
                ctypes.cast(aDown.pBuffer, ctypes.c_void_p).value +
                aDown.WrOff, cnt, str)

            aDown.WrOff += cnt
            if aDown.WrOff == aDown.SizeOfBuffer: aDown.WrOff = 0

            bytes = bytes[cnt:]

        if bytes and aDown.RdOff != 0 and aDown.RdOff != 1:  # != 0 确保 aDown.WrOff 折返回 0,!= 1 确保有空间可写入
            cnt = min(aDown.RdOff - 1 - aDown.WrOff,
                      len(bytes))  # - 1 确保写入操作不导致WrOff与RdOff指向同一位置
            str = ctypes.create_string_buffer(bytes[:cnt])
            self.jlink.JLINKARM_WriteMem(
                ctypes.cast(aDown.pBuffer, ctypes.c_void_p).value +
                aDown.WrOff, cnt, str)

            aDown.WrOff += cnt

        self.jlink.JLINKARM_WriteU32(self.aDownAddr + 4 * 3, aDown.WrOff)

    @pyqtSlot()
    def on_btnSend_clicked(self):
        if self.btnOpen.text() == '关闭连接':
            text = self.txtSend.toPlainText()

            try:
                if self.chkHEXSend.isChecked():
                    text = ''.join([chr(int(x, 16)) for x in text.split()])

                self.aDownWrite(text.encode('latin'))

            except Exception as e:
                self.txtMain.append(f'\n{str(e)}\n')

    @pyqtSlot()
    def on_btnDLL_clicked(self):
        dllpath, filter = QFileDialog.getOpenFileName(
            caption='JLink_x64.dll路径',
            filter='动态链接库文件 (*.dll)',
            directory=self.linDLL.text())
        if dllpath != '':
            self.linDLL.setText(dllpath)

    @pyqtSlot(int)
    def on_chkWavShow_stateChanged(self, state):
        self.ChartView.setVisible(state == Qt.Checked)
        self.txtMain.setVisible(state == Qt.Unchecked)

    @pyqtSlot()
    def on_btnClear_clicked(self):
        self.txtMain.clear()

    def closeEvent(self, evt):
        self.conf.set('J-Link', 'dllpath', self.linDLL.text())
        self.conf.set('J-Link', 'mcucore', self.cmbCore.currentText())
        self.conf.set('Memory', 'rttaddr', self.linRTT.text())
        self.conf.write(open('setting.ini', 'w', encoding='utf-8'))
示例#22
0
class ChartView(QChartView):
    def __init__(self, *args, **kwargs):
        super(ChartView, self).__init__(*args, **kwargs)
        self.resize(800, 600)
        self.setRenderHint(QPainter.Antialiasing)  # 抗锯齿
        # 自定义x轴label
        self.category = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"]
        self.initChart()

        # 提示widget
        self.toolTipWidget = GraphicsProxyWidget(self._chart)

        # line
        self.lineItem = QGraphicsLineItem(self._chart)
        pen = QPen(Qt.gray)
        pen.setWidth(1)

        self.lineItem.setPen(pen)
        self.lineItem.setZValue(996)
        self.lineItem.hide()

        self.lineItemX = QGraphicsLineItem(self._chart)
        penx = QPen(Qt.gray)
        penx.setWidth(1)
        self.lineItemX.setPen(penx)
        self.lineItemX.setZValue(997)
        self.lineItemX.hide()

        # 一些固定计算,减少mouseMoveEvent中的计算量
        # 获取x和y轴的最小最大值
        axisX, axisY = self._chart.axisX(), self._chart.axisY()
        self.min_x, self.max_x = axisX.min(), axisX.max()
        self.min_y, self.max_y = axisY.min(), axisY.max()

    def resizeEvent(self, event):
        super(ChartView, self).resizeEvent(event)
        # 当窗口大小改变时需要重新计算
        # 坐标系中左上角顶点
        self.point_top = self._chart.mapToPosition(
            QPointF(self.min_x, self.max_y))
        # 坐标右上点
        self.point_right_top = self._chart.mapToPosition(
            QPointF(self.max_x, self.max_y))
        # 坐标右下点
        self.point_right_bottom = self._chart.mapToPosition(
            QPointF(self.max_x, self.min_y))
        # 坐标原点坐标
        self.point_bottom = self._chart.mapToPosition(
            QPointF(self.min_x, self.min_y))
        self.step_x = (self.max_x - self.min_x) / \
            (self._chart.axisX().tickCount() - 1)

    def mouseMoveEvent(self, event):
        super(ChartView, self).mouseMoveEvent(event)
        pos = event.pos()
        # 把鼠标位置所在点转换为对应的xy值
        x = self._chart.mapToValue(pos).x()
        y = self._chart.mapToValue(pos).y()

        # 根据间隔来确定鼠标当前所在的索引
        index = round((x - self.min_x) / self.step_x)
        # 得到在坐标系中的所有正常显示的series的类型和点
        points = [
            (serie, serie.at(index)) for serie in self._chart.series()
            if self.min_x <= x <= self.max_x and self.min_y <= y <= self.max_y
        ]
        if points:
            # 根据鼠标的点获取对应曲线的坐标点,
            # pos_x = self._chart.mapToPosition(
            #     QPointF(index * self.step_x + self.min_x, self.min_y))
            # 设置鼠标的垂直线
            self.lineItem.setLine(pos.x(), self.point_top.y(), pos.x(),
                                  self.point_bottom.y())
            self.lineItem.show()

            # 设置鼠标的水平线
            self.lineItemX.setLine(self.point_top.x(), pos.y(),
                                   self.point_right_top.x(), pos.y())
            self.lineItemX.show()

            try:
                title = self.category[index]
            except:
                title = ""
            t_width = self.toolTipWidget.width()
            t_height = self.toolTipWidget.height()
            # 如果鼠标位置离右侧的距离小于tip宽度
            x = pos.x() - t_width if self.width() - \
                pos.x() - 20 < t_width else pos.x()
            # 如果鼠标位置离底部的高度小于tip高度
            y = pos.y() - t_height if self.height() - \
                pos.y() - 20 < t_height else pos.y()
            self.toolTipWidget.show(title, points, QPoint(x, y))
        else:
            self.toolTipWidget.hide()
            self.lineItem.hide()
            self.lineItemX.hide()

    def handleMarkerClicked(self):
        marker = self.sender()  # 信号发送者
        if not marker:
            return
        visible = not marker.series().isVisible()
        # 隐藏或显示series
        marker.series().setVisible(visible)
        marker.setVisible(True)  # 要保证marker一直显示
        # 透明度
        alpha = 1.0 if visible else 0.4
        # 设置label的透明度
        brush = marker.labelBrush()
        color = brush.color()
        color.setAlphaF(alpha)
        brush.setColor(color)
        marker.setLabelBrush(brush)
        # 设置marker的透明度
        brush = marker.brush()
        color = brush.color()
        color.setAlphaF(alpha)
        brush.setColor(color)
        marker.setBrush(brush)
        # 设置画笔透明度
        pen = marker.pen()
        color = pen.color()
        color.setAlphaF(alpha)
        pen.setColor(color)
        marker.setPen(pen)

    def handleMarkerHovered(self, status):
        # 设置series的画笔宽度
        marker = self.sender()  # 信号发送者
        if not marker:
            return
        series = marker.series()
        if not series:
            return
        pen = series.pen()
        if not pen:
            return
        pen.setWidth(pen.width() + (1 if status else -1))
        series.setPen(pen)

    def handleSeriesHoverd(self, point, state):
        # 设置series的画笔宽度
        series = self.sender()  # 信号发送者
        pen = series.pen()
        if not pen:
            return
        pen.setWidth(pen.width() + (1 if state else -1))
        series.setPen(pen)

    def initChart(self):
        self._chart = QChart(title="折线图堆叠")
        self._chart.setAcceptHoverEvents(True)
        # Series动画
        self._chart.setAnimationOptions(QChart.SeriesAnimations)
        dataTable = [["邮件营销", [120, 132, 101, 134, 90, 230, 210]],
                     ["联盟广告", [220, 182, 191, 234, 290, 330, 310]],
                     ["视频广告", [150, 232, 201, 154, 190, 330, 410]],
                     ["直接访问", [320, 332, 301, 334, 390, 330, 320]],
                     ["搜索引擎", [820, 932, 901, 934, 1290, 1330, 1320]]]
        for series_name, data_list in dataTable:
            series = QLineSeries(self._chart)
            for j, v in enumerate(data_list):
                series.append(j, v)
            series.setName(series_name)
            series.setPointsVisible(True)  # 显示圆点
            series.hovered.connect(self.handleSeriesHoverd)  # 鼠标悬停
            self._chart.addSeries(series)
        self._chart.createDefaultAxes()  # 创建默认的轴
        axisX = self._chart.axisX()  # x轴
        axisX.setTickCount(7)  # x轴设置7个刻度
        axisX.setGridLineVisible(False)  # 隐藏从x轴往上的线条
        axisY = self._chart.axisY()
        axisY.setTickCount(7)  # y轴设置7个刻度
        axisY.setRange(0, 1500)  # 设置y轴范围
        # 自定义x轴
        axis_x = QCategoryAxis(
            self._chart,
            labelsPosition=QCategoryAxis.AxisLabelsPositionOnValue)
        axis_x.setTickCount(7)
        axis_x.setGridLineVisible(False)
        min_x = axisX.min()
        max_x = axisX.max()
        step = (max_x - min_x) / (7 - 1)  # 7个tick
        for i in range(0, 7):
            axis_x.append(self.category[i], min_x + i * step)
        self._chart.setAxisX(axis_x, self._chart.series()[-1])
        # self._chart.addAxis(axis_x, Qt.AlignBottom)
        # chart的图例
        legend = self._chart.legend()
        # 设置图例由Series来决定样式
        legend.setMarkerShape(QLegend.MarkerShapeFromSeries)
        # 遍历图例上的标记并绑定信号
        for marker in legend.markers():
            # 点击事件
            marker.clicked.connect(self.handleMarkerClicked)
            # 鼠标悬停事件
            marker.hovered.connect(self.handleMarkerHovered)
        self.setChart(self._chart)
示例#23
0
class MyWidget(QWidget):
    def __init__(self):
        super().__init__()
        uic.loadUi('ws_ui.ui', self)
        self.con = sqlite3.connect("ws_database.db")
        self.setWindowFlags(QtCore.Qt.FramelessWindowHint)

        self.create_linechart()
        self.tmp, self.hmd, self.prs = 0, 0, 0
        self.make_measure()

        self.measure_timer = QTimer(self)
        self.measure_timer.setInterval(MEASURE_FREQUENCIES * 1000)
        self.measure_timer.timeout.connect(self.make_measure)
        self.measure_timer.start()

        self.update_timer = QTimer(self)
        self.update_timer.setInterval(1000)
        self.update_timer.timeout.connect(self.update_labels)
        self.update_timer.start()

        self.update_labels()

    def quit(self):
        self.destroy()
        quit()

    def make_measure(self):
        sns = sensor_measure()

        if sns[0] != ERROR_CODE:
            self.tmp = sns[0]

        else:
            print('tmp error')

        if sns[1] != ERROR_CODE:
            self.hmd = sns[1]

        else:
            print('hmd error')

        if sns[2] != ERROR_CODE:
            self.prs = sns[2]

        else:
            print('prs error')

        time = int(dt.datetime.now().timestamp())

        req = """
              INSERT INTO short_term_data(tmp, hmd, prs, time_from_epoch)
              VALUES(?,?,?,?)
              """

        self.con.execute(req, (self.tmp, self.hmd, self.prs, time))
        self.con.commit()

        self.update_linechart()

    def update_labels(self):
        deg = u'\N{DEGREE SIGN}'
        hpa = 'ʰᴾᵃ'
        self.time_label.setText(dt.datetime.now().strftime('%H:%M'))
        self.tmp_label.setText('{} {}C'.format(self.tmp, deg))
        self.hmd_label.setText('{} %'.format(self.hmd))
        self.prs_label.setText('{} {}'.format(self.prs, hpa))

    def create_linechart(self):
        self.chart = QChart()
        self.chart.legend().hide()

        self.series = QLineSeries()

        self.axisValue = QValueAxis()
        self.axisCurrentTime = QValueAxis()
        self.axisTime = QDateTimeAxis()
        self.axisTime.setFormat("hh:mm")

        self.chartview = QChartView(self.chart, self.groupBox)
        self.chartview.resize(540, 460)
        self.chartview.move(0, 0)
        self.chartview.setRenderHint(QPainter.Antialiasing)

    def update_linechart(self):
        if self.axisTime in self.chart.axes():
            self.chart.removeAxis(self.axisTime)

        if self.axisCurrentTime in self.chart.axes():
            self.chart.removeAxis(self.axisCurrentTime)

        if self.axisValue in self.chart.axes():
            self.chart.removeAxis(self.axisValue)

        if self.series in self.chart.series():
            self.chart.removeSeries(self.series)

        self.series.clear()

        self.axisValue.setMax(50)
        self.axisValue.setMin(-50)

        req = """
              SELECT tmp, time_from_epoch
              FROM short_term_data
              WHERE (time_from_epoch - ?) < 86400 AND NOT tmp = ?
              """

        cur = self.con.cursor()
        result = list(cur.execute(req, (int(dt.datetime.now().timestamp()), ERROR_CODE)))

        for measure in result:
            self.series.append(measure[1] * 1000, measure[0])

        self.axisTime.setMin(QDateTime.fromMSecsSinceEpoch(int(dt.datetime.now().timestamp()) * 1000 - 86390000))
        self.axisTime.setMax(QDateTime.fromMSecsSinceEpoch(int(dt.datetime.now().timestamp()) * 1000))

        self.chart.addSeries(self.series)
        self.chart.addAxis(self.axisTime, Qt.AlignBottom)
        self.series.attachAxis(self.axisTime)
        self.chart.addAxis(self.axisValue, Qt.AlignLeft)
        self.series.attachAxis(self.axisValue)

        self.chart.setTitle('Температура')