PyQt5下PyQtGraph双图表实现十字线共享的技术咨询
实现PyQtGraph双图表共享十字线功能
当然可以实现!PyQtGraph完全支持双图表间的十字线共享,我们只需要调整十字线的创建逻辑和事件监听方式,就能达到你想要的同步效果,同时还能获取十字线位置对应的两组数据。
修改后的完整可运行代码
import sys import traceback from PyQt5.QtWidgets import * from PyQt5.QtCore import * from PyQt5.QtGui import * import pyqtgraph as pg class GUI_create_main_window(QWidget): def __init__(self): super().__init__() main_layout = QVBoxLayout(self) # 初始化两个图表组件 self.plot1 = pg.PlotWidget() self.plot2 = pg.PlotWidget() main_layout.addWidget(self.plot1) main_layout.addWidget(self.plot2) # 生成测试数据(方便你直观测试十字线功能) self._add_test_data() # 初始化共享十字线 self._setup_shared_crosshair() # 配置鼠标事件监听 self._setup_mouse_tracking() self.setLayout(main_layout) self.show() def _add_test_data(self): # 生成两组示例数据,你可以替换成自己的业务数据 x = list(range(0, 100)) y1 = [i**0.5 for i in x] # 平方根曲线 y2 = [i*0.5 for i in x] # 线性曲线 self.plot1.plot(x, y1, pen='blue') self.plot2.plot(x, y2, pen='red') def _setup_shared_crosshair(self): # 为每个图表创建独立的十字线(避免跨图表坐标冲突) # 图表1的十字线 self.v_line1 = pg.InfiniteLine(angle=90, movable=False, pen=pg.mkPen('black', width=1)) self.h_line1 = pg.InfiniteLine(angle=0, movable=False, pen=pg.mkPen('black', width=1), label='{value:0.1f}', labelOpts={'position':0.98, 'color': (0,0,255), 'fill': (0,0,255,100)}) self.plot1.addItem(self.v_line1, ignoreBounds=True) self.plot1.addItem(self.h_line1, ignoreBounds=True) # 图表2的十字线 self.v_line2 = pg.InfiniteLine(angle=90, movable=False, pen=pg.mkPen('black', width=1)) self.h_line2 = pg.InfiniteLine(angle=0, movable=False, pen=pg.mkPen('black', width=1), label='{value:0.1f}', labelOpts={'position':0.98, 'color': (255,0,0), 'fill': (255,0,0,100)}) self.plot2.addItem(self.v_line2, ignoreBounds=True) self.plot2.addItem(self.h_line2, ignoreBounds=True) # 保存两个图表的视口组件,用于坐标转换 self.vb1 = self.plot1.plotItem.vb self.vb2 = self.plot2.plotItem.vb def _setup_mouse_tracking(self): # 开启两个图表的鼠标追踪功能 self.plot1.setMouseTracking(True) self.plot2.setMouseTracking(True) # 给两个图表的视口安装事件过滤器,监听鼠标移动 self.plot1.viewport().installEventFilter(self) self.plot2.viewport().installEventFilter(self) def eventFilter(self, source, event): try: if event.type() == QtCore.QEvent.MouseMove: # 判断鼠标当前在哪个图表上 if source is self.plot1.viewport(): pos = event.pos() if self.plot1.sceneBoundingRect().contains(pos): # 将鼠标位置转换为图表1的数据坐标系 mouse_point = self.vb1.mapSceneToView(pos) self._update_crosshair_pos(mouse_point.x(), mouse_point.y()) elif source is self.plot2.viewport(): pos = event.pos() if self.plot2.sceneBoundingRect().contains(pos): # 将鼠标位置转换为图表2的数据坐标系 mouse_point = self.vb2.mapSceneToView(pos) self._update_crosshair_pos(mouse_point.x(), mouse_point.y()) # 传递事件给父类处理 return super().eventFilter(source, event) except Exception as e: traceback.print_exc() print(f"事件处理出错: {str(e)}") return super().eventFilter(source, event) def _update_crosshair_pos(self, x_pos, y_pos): # 同步更新两个图表的十字线位置 self.v_line1.setPos(x_pos) self.h_line1.setPos(y_pos) self.v_line2.setPos(x_pos) self.h_line2.setPos(y_pos) # 获取十字线位置对应的图表数据(示例逻辑,可根据你的需求调整) x_index = int(round(x_pos)) # 确保索引在数据范围内 if 0 <= x_index < len(self.plot1.plotItem.listDataItems()[0].xData): y1_value = self.plot1.plotItem.listDataItems()[0].yData[x_index] y2_value = self.plot2.plotItem.listDataItems()[0].yData[x_index] print(f"当前X坐标: {x_pos:.1f} | 图表1Y值: {y1_value:.1f} | 图表2Y值: {y2_value:.1f}") if __name__ == '__main__': pg.setConfigOption('background', 'white') pg.setConfigOption('foreground', 'black') app = QApplication(sys.argv) gui = GUI_create_main_window() currentExitCode = app.exec_()
核心改动说明
- 独立十字线组件:为每个图表创建单独的十字线,避免单个组件在多图表中显示时的坐标偏移问题,通过同步位置实现共享效果。
- 双图表事件监听:同时监听两个图表的鼠标移动事件,不管鼠标在哪个图表上操作,都会触发十字线的同步更新。
- 坐标系转换:针对每个图表的视口(ViewBox)做坐标转换,确保鼠标的屏幕位置能正确映射到图表的数据坐标系。
- 数据获取逻辑:在十字线更新时添加了数据获取的示例,你可以根据自己的数据集调整,比如使用插值算法获取更精确的数值,或者绑定到UI组件显示。
运行这段代码后,鼠标在任意一个图表上移动时,两个图表的十字线会同步跟随,控制台会实时输出当前十字线位置对应的两组数据,完全符合你期望的效果。
内容的提问来源于stack exchange,提问作者shadow dk
相关产品推荐
相关产品推荐

