PyQt5绘图pyqtgraph多条折线图

本文介绍了一个使用PyQt5和pyqtgraph实现的图表应用,亮点在于自定义的RotateAxisItem用于倾斜长轴标签,同时展示了如何在PlotWidget中绘制多条共享x轴的曲线,并实现实时鼠标位置触发的数据详细展示。
摘要由CSDN通过智能技术生成

最终效果图

 代码(可直接复制到一个.py文件运行,运行后的效果如上图)

from PyQt5 import QtWidgets
import pyqtgraph as pg
from typing import Any,Dict
import random,sys

class RotateAxisItem(pg.AxisItem):
    def drawPicture(self, p, axisSpec, tickSpecs, textSpecs):
        p.setRenderHint(p.Antialiasing,False)
        p.setRenderHint(p.TextAntialiasing,True)

        ## draw long line along axis
        pen,p1,p2 = axisSpec
        p.setPen(pen)
        p.drawLine(p1,p2)
        p.translate(0.5,0)  ## resolves some damn pixel ambiguity

        for pen,p1,p2 in tickSpecs:
            p.setPen(pen)
            p.drawLine(p1,p2)

        p.setPen(self.pen())
        for rect,flags,text in textSpecs:
            # this is the important part
            p.save()
            p.translate(rect.x(),rect.y())
            p.rotate(-30)
            p.drawText(-rect.width(),rect.height(),rect.width(),rect.height(),flags,text)
            # restoring the painter is *required*!!!
            p.restore()

class LineShowManager(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()

        self.y_datas = []

        self.init_ui()
        pass

    def init_ui(self) -> None:

        pg.setConfigOptions(leftButtonPan=False, antialias=True)
        pg.setConfigOption('background', 'w')
        pg.setConfigOption('foreground', 'k')

        xax = RotateAxisItem(orientation='bottom')
        xax.setHeight(h=80)
        self.pw = pg.PlotWidget(axisItems={'bottom': xax})
        self.pw.setLabel("left", "值")
        self.pw.setLabel("bottom", "时间")
        self.label = pg.TextItem()
        layout = QtWidgets.QVBoxLayout()
        layout.addWidget(self.pw)
        self.setLayout(layout)
        pass

    def plot_data(self, data: Dict[str, Any]):
        self.pw.clear()
        self.pw.addLegend()
        # xTick x  多条曲线共用x轴数据
        xTick = [data['xTick']]
        x = data['x']
        y_list = data['y_list']
        y_names = data['y_names']
        y_min = data['y_min']
        y_max = data['y_max']
        self.y_datas = y_list
        self.x_data = xTick
        self.y_names = y_names

        xax = self.pw.getAxis('bottom')
        xax.setTicks(xTick)

        for i in range(len(y_names)):
            self.pw.plot(x, y_list[i], connect='finite', pen=pg.mkPen(
                {'color': (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)), 'width': 4}),
                         name=y_names[i])
        self.vLine = pg.InfiniteLine(angle=90, movable=False)
        self.hLine = pg.InfiniteLine(angle=0, movable=False)
        self.pw.addItem(self.vLine, ignoreBounds=True)
        self.pw.addItem(self.hLine, ignoreBounds=True)
        self.pw.addItem(self.label, ignoreBounds=True)
        self.pw.setRange(xRange=[0,len(x)],yRange=[y_min,y_max])
        self.vb = self.pw.getViewBox()
        self.proxy = pg.SignalProxy(self.pw.scene().sigMouseMoved, rateLimit=60, slot=self.mouseMoved)
        pass

    def mouseMoved(self, evt):
        pos = evt[0]
        if len(self.y_datas) <= 0:
            return
        if self.pw.sceneBoundingRect().contains(pos):
            mousePoint = self.vb.mapSceneToView(pos)
            index = int(mousePoint.x())
            pos_y = int(mousePoint.y())

            if index >= 0 and index < len(self.y_datas[0]):
                # Here we have obtained the mouse x point
                # So lets use a for loop for each curve to set the Pos
                time_str = self.x_data[0][index][1]
                # time_str = ''
                data_str = ''
                for m in range(len(self.y_datas)):
                    data_str += '<p style="color:black;">' + self.y_names[m] + ':' + str(
                        self.y_datas[m][index]) + '</p>'
                html_str = '<p style="color:black;">' + time_str + '</p>' + data_str
                self.label.setHtml(html_str)
                self.label.setPos(mousePoint.x(), mousePoint.y())
            self.vLine.setPos(mousePoint.x())
            self.hLine.setPos(mousePoint.y())
        pass

    def showEvent(self, QShowEvent):
        self.showMaximized()

if __name__ == '__main__':
    temp_map = {}
    temp_map['xTick'] = [(0,'2021-01-01'),
                         (1,'2021-02-0'),
                         (2,'2021-03-01'),
                         (3,'2021-04-01'),
                         (4,'2021-05-01'),
                         (5,'2021-06-01'),
                         (6,'2021-07-01'),
                         ]
    temp_map['x'] = [0,1,2,3,4,5,6]
    temp_map['y_list'] = [[1,8,0,5,1,-6,3],[-9,12,6,0,8,-2,8],[-7,19,16,-9,10,-6,8]]
    temp_map['y_names'] = ['线条1','线条2','线条3']
    temp_map['y_min'] = -9
    temp_map['y_max'] = 19

    app = QtWidgets.QApplication(sys.argv)
    temp_widget = LineShowManager()
    temp_widget.show()
    temp_widget.plot_data(temp_map)
    sys.exit(app.exec_())


几个关注点:

1. 横坐标字符串型太长要倾斜显示,实现方式是重写pg.AxisItem,查看代码中的RotateAxisItem类,我在此处设置的是倾斜-30度

2. 多条曲线共用x轴,所以传入的数据中x轴和每个y轴数据的形状要一致,也可以理解为长度要一样,不然会报x轴和y轴数据的形状不一致的错误

3. 如果plotWidget执行了clear(),那么之前所有通过addItem添加的控件都要重新添加

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值