Example #1
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)
Example #2
0
class KLineChartView(QChartView):

    # QCandlestickSeries的hovered的信号响应后传递日期出去
    candles_hovered = pyqtSignal(bool, str)

    def __init__(self, data: pd.DataFrame):
        super(KLineChartView, self).__init__()
        self.setRenderHint(QPainter.Antialiasing)  # 抗锯齿
        self._chart = QChart()
        self._series = QCandlestickSeries()
        self._stocks = data
        self._category = list()
        self._count = None

        self.init_chart()
        self._zero_value = (0, self._chart.axisY().min())
        self._max_value = (len(self._chart.axisX().categories()),
                           self._chart.axisY().max())
        self._zero_point = self._chart.mapToPosition(
            QPointF(self._zero_value[0], self._zero_value[1]))
        self._max_point = self._chart.mapToPosition(
            QPointF(self._max_value[0], self._max_value[1]))
        # 计算x轴单个cate的宽度,用来处理横线不能画到边界
        self._cate_width = (self._max_point.x() - self._zero_point.x()) / len(
            self._category)

        self._series.hovered.connect(self.on_series_hovered)

    def on_series_hovered(self, status, candles_set):
        trade_date = time.strftime('%Y%m%d',
                                   time.localtime(candles_set.timestamp()))
        self.candles_hovered.emit(status, trade_date)

    def set_name(self, name):
        self._series.setName(name)

    def clear_series_values(self):
        self._series.clear()
        self._chart.axisY().setRange(0, 10)
        self._chart.axisX().setCategories(list())
        self._stocks = None

    def add_series_values(self, data: pd.DataFrame, is_init=False):
        self._stocks = data
        self._category = self._stocks['trade_date']
        self._count = len(self._category)
        for _, stock in self._stocks.iterrows():
            time_p = datetime.datetime.strptime(stock['trade_date'], '%Y%m%d')
            time_p = float(time.mktime(time_p.timetuple()))
            _set = QCandlestickSet(float(stock['open']), float(stock['high']),
                                   float(stock['low']), float(stock['close']),
                                   time_p, self._series)
            self._series.append(_set)

        if not is_init:
            self._stocks = data
            self._category = self._stocks['trade_date']
            axis_x = self._chart.axisX()
            axis_y = self._chart.axisY()
            axis_x.setCategories(self._category)
            max_p = self._stocks[['high', 'low']].stack().max()
            min_p = self._stocks[['high', 'low']].stack().min()
            axis_y.setRange(min_p * 0.99, max_p * 1.01)
            self._zero_value = (0, self._chart.axisY().min())
            self._max_value = (len(self._chart.axisX().categories()),
                               self._chart.axisY().max())
            # 计算x轴单个cate的宽度,用来处理横线不能画到边界
            self._cate_width = (self._max_point.x() -
                                self._zero_point.x()) / len(self._category)

    def resizeEvent(self, event):
        super(KLineChartView, self).resizeEvent(event)
        self._zero_point = self._chart.mapToPosition(
            QPointF(self._zero_value[0], self._zero_value[1]))
        self._max_point = self._chart.mapToPosition(
            QPointF(self._max_value[0], self._max_value[1]))
        self._cate_width = (self._max_point.x() - self._zero_point.x()) / len(
            self._category)

    def max_point(self):
        return QPointF(self._max_point.x() + self._cate_width / 2,
                       self._max_point.y())

    def min_point(self):
        return QPointF(self._zero_point.x() - self._cate_width / 2,
                       self._zero_point.y())

    def init_chart(self):
        self._chart.setAnimationOptions(QChart.SeriesAnimations)
        self._series.setIncreasingColor(QColor(Qt.red))
        self._series.setDecreasingColor(QColor(Qt.green))
        self._series.setName(self._stocks['name'].iloc[0])
        self.add_series_values(self._stocks, True)
        self._chart.addSeries(self._series)

        self._chart.createDefaultAxes()
        self._chart.setLocalizeNumbers(True)
        axis_x = self._chart.axisX()
        axis_y = self._chart.axisY()
        axis_x.setGridLineVisible(False)
        axis_y.setGridLineVisible(False)
        axis_x.setCategories(self._category)
        axis_x.setLabelsVisible(False)
        axis_x.setVisible(False)
        max_p = self._stocks[['high', 'low']].stack().max()
        min_p = self._stocks[['high', 'low']].stack().min()
        axis_y.setRange(min_p * 0.99, max_p * 1.01)

        # chart的图例
        legend = self._chart.legend()
        # 设置图例由Series来决定样式
        legend.setMarkerShape(QLegend.MarkerShapeFromSeries)

        self.setChart(self._chart)
        # 设置外边界全部为0
        self._chart.layout().setContentsMargins(0, 0, 0, 0)
        # 设置内边界的bottom为0
        margins = self._chart.margins()
        self._chart.setMargins(QMargins(margins.left(), 0, margins.right(), 0))
        # 设置背景区域无圆角
        self._chart.setBackgroundRoundness(0)