You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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_()

核心改动说明

  1. 独立十字线组件:为每个图表创建单独的十字线,避免单个组件在多图表中显示时的坐标偏移问题,通过同步位置实现共享效果。
  2. 双图表事件监听:同时监听两个图表的鼠标移动事件,不管鼠标在哪个图表上操作,都会触发十字线的同步更新。
  3. 坐标系转换:针对每个图表的视口(ViewBox)做坐标转换,确保鼠标的屏幕位置能正确映射到图表的数据坐标系。
  4. 数据获取逻辑:在十字线更新时添加了数据获取的示例,你可以根据自己的数据集调整,比如使用插值算法获取更精确的数值,或者绑定到UI组件显示。

运行这段代码后,鼠标在任意一个图表上移动时,两个图表的十字线会同步跟随,控制台会实时输出当前十字线位置对应的两组数据,完全符合你期望的效果。

内容的提问来源于stack exchange,提问作者shadow dk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 10:18:30