目录
- 一、效果展示
- 二、资料参考
- 三、实例详解
- 四、自定义案例
一、效果展示
开门见山,上效果:

窗口共有三个部分
- 图2为所有数据画出的折线图
- 图1为图2的蓝色矩形区域处的放大显示
- 右上角的图例可以根据鼠标在图1中的移动,实时显示鼠标所在x轴处的折线y值
二、资料参考
- pyqtgraph的examples的Crosshair / Mouse interaction

安装好后pyqtgraph包之后,上面的窗口在命令行中输入python -m pyqtgraph.examples 即可。
三、实例详解
详细解释见代码注释:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | # # ********************************************************************** # CrossHair,借鉴显示鼠标所在x值处的线的y值 """ Demonstrates some customized mouse interaction by drawing a crosshair that follows the mouse. """ # import initExample ## Add path to library (just for examples; you do not need this) import numpy as np import pyqtgraph as pg from pyqtgraph.Qt import QtGui, QtCore from pyqtgraph.Point import Point #generate layout app = QtGui.QApplication([]) # 初始化app win = pg.GraphicsLayoutWidget(show=True) # 设置widget,并属性show为True,即显示 win.setWindowTitle('pyqtgraph example: crosshair') # 设置窗口名称 label = pg.LabelItem(justify='right') # 设置label用于跟随鼠标显示横纵坐标有效值 win.addItem(label) # 定义了label之后,要addItem之后才会真正加进去 p1 = win.addPlot(row=1, col=0) # p1为p2的区域放大,并显示鼠标所在位置值 p2 = win.addPlot(row=2, col=0) # p2为全部数据画出的折线致密的原图 # 定义区域 region = pg.LinearRegionItem() region.setZValue(10) # 不过这一句话好像没什么作用 # Add the LinearRegionItem to the ViewBox, but tell the ViewBox to exclude this # item when doing auto-range calculations. p2.addItem(region, ignoreBounds=True) #pg.dbg() p1.setAutoVisible(y=True) #create numpy arrays #make the numbers large to show that the xrange shows data from 10000 to all the way 0 data1 = 10000 + 15000 * pg.gaussianFilter(np.random.random(size=10000), 10) + 3000 * np.random.random(size=10000) data2 = 15000 + 15000 * pg.gaussianFilter(np.random.random(size=10000), 10) + 3000 * np.random.random(size=10000) # 画图 p1.plot(data1, pen="r") p1.plot(data2, pen="g") p2.plot(data1, pen="w") # 设置图2中放大区域被移动后的触发函数 def update(): region.setZValue(10) minX, maxX = region.getRegion() # 调整p1的横轴显示区域坐标范围 p1.setXRange(minX, maxX, padding=0) region.sigRegionChanged.connect(update) def updateRegion(window, viewRange): rgn = viewRange[0] region.setRegion(rgn) p1.sigRangeChanged.connect(updateRegion) # 初始的region位置 region.setRegion([1000, 2000]) # 设置跟随鼠标移动的线 #cross hair vLine = pg.InfiniteLine(angle=90, movable=False) # angle控制线相对x轴正向的相对夹角 hLine = pg.InfiniteLine(angle=0, movable=False) p1.addItem(vLine, ignoreBounds=True) p1.addItem(hLine, ignoreBounds=True) vb = p1.vb # 跟随鼠标移动,提取鼠标的横轴值,并自定义纵轴的值显示 def mouseMoved(evt): pos = evt[0] ## using signal proxy turns original arguments into a tuple if p1.sceneBoundingRect().contains(pos): mousePoint = vb.mapSceneToView(pos) # 建议不用int,精度高时用float,这样可以显示横坐标的小数 index = int(mousePoint.x()) if index > 0 and index < len(data1): label.setText("<span style='font-size: 12pt'>x=%0.1f, <span style='color: red'>y1=%0.1f</span>, <span style='color: green'>y2=%0.1f</span>" % (mousePoint.x(), data1[index], data2[index])) vLine.setPos(mousePoint.x()) hLine.setPos(mousePoint.y()) # 设置鼠标移动的触发,限制速率,移动则触发mouseMoved函数 proxy = pg.SignalProxy(p1.scene().sigMouseMoved, rateLimit=60, slot=mouseMoved) #p1.scene().sigMouseMoved.connect(mouseMoved) ## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': import sys if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): QtGui.QApplication.instance().exec_() |
四、自定义案例
p1为加了该功能的,p2未加,做对比,可以不管它。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 | class ChildWinSystemStatus(QDialog, Ui_SystemStatus): signal_plot = pyqtSignal(object, object, object) # 当主窗口被关闭后,也关闭子窗口 signal_exit = pyqtSignal(object) def __init__(self, qSystem): super(ChildWinSystemStatus, self).__init__() self.setupUi(self) # 设置窗口最小化与最大化按钮,关闭按钮置灰 self.setWindowFlags(Qt.WindowMaximizeButtonHint | Qt.WindowMinimizeButtonHint) self.label1 = pg.LabelItem(justify='right') self.guiplot_consensus.addItem(self.label1) # label2 = pg.LabelItem(justify='right') # self.guiplot_growthRate.addItem(label2) # 美化 self.p1 = self.guiplot_consensus.addPlot(title="Consensus Time", row=1, col=0) self.p1.setAutoVisible(y=True) self.p1.showGrid(x=True, y=True) # 设置网格 self.p1.setLabel('bottom', "Simulation time") self.vLine1 = pg.InfiniteLine(angle=90, movable=False) self.hLine1 = pg.InfiniteLine(angle=0, movable=False) self.p1.addItem(self.vLine1, ignoreBounds=True) self.p1.addItem(self.hLine1, ignoreBounds=True) self.vb1 = self.p1.vb # 将鼠标移动事件,设置触发函数self.mouseMoved self.proxy = pg.SignalProxy(self.p1.scene().sigMouseMoved, rateLimit=60, slot=self.mouseMoved) self.p2 = self.guiplot_growthRate.addPlot(title="Growth Rate") # self.p2.setLabel('bottom', "Simulation time") self.p2.showGrid(x=True, y=True) self.signal_plot.connect(self.child_plotSystem) self.signal_exit.connect(self.closeEvent) self.queue_system = qSystem self.consensusLatency = [] self.rateGrowth = [] self.simulationTime = [] def mouseMoved(self, evt): pos = evt[0] ## using signal proxy turns original arguments into a tuple if self.p1.sceneBoundingRect().contains(pos): mousePoint = self.vb1.mapSceneToView(pos) # 一定要注意用float,这个bug花了一个小时才找出来。注意图中y值变化时刻的x,发现都是x超过整数时才变化,找出的本bug index = float(mousePoint.x()) # 当未启动时,鼠标移动,只显示x的值 if len(self.simulationTime) == 0: self.label1.setText("<span style='font-size: 12pt'>SimTime=%0.2f s, <span style='color: white'>ConTime= s</span>" % (mousePoint.x())) # self.label1.setText("<span style='font-size: 12pt;color: white'>SimTime=%0.1f s, ConTime= </span>" % (mousePoint.x())) # 调整,使label精确显示y值 if len(self.simulationTime) >= 2 and index > self.simulationTime[0] and index < self.simulationTime[-1]: for i in range(len(self.simulationTime)): if self.simulationTime[i] <= index < self.simulationTime[i + 1]: self.label1.setText("<span style='font-size: 12pt'>SimTime=%0.2f s, <span style='color: white'>ConTime=%0.2f s</span>" % (mousePoint.x(), self.consensusLatency[i])) break self.vLine1.setPos(mousePoint.x()) self.hLine1.setPos(mousePoint.y()) def closeEvent(self, event): os._exit(0) def analyzeSystem(self, dataSystem): value = dataSystem['value'] if dataSystem['stat'] == 0: self.consensusLatency.append(value) self.simulationTime.append(dataSystem['time_simulated']) rate_growth = 1 / value self.rateGrowth.append(round(rate_growth, 4)) # 向连接槽发射信号,触发窗口类中画图函数 self.signal_plot.emit(self.consensusLatency, self.rateGrowth, self.simulationTime) if dataSystem['stat'] == 1: value_rate = round(value * 100, 2) self.rate_anti.setText(str(value_rate) + '%') def child_plotSystem(self, consensusLatency, growth_rate, list_time): if consensusLatency: self.p1.plot(list_time, consensusLatency, pen=(0, 255, 0)) self.p2.plot(list_time, growth_rate, pen=(0, 255, 0)) else: pass def run(self): while True: if not self.queue_system.empty(): dataSystem = self.queue_system.get() if dataSystem == 'exit': self.signal_exit.emit('exit') break self.analyzeSystem(dataSystem) |
效果如下:

