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

如何在pyqtgraph的GraphicsView中正确实现鼠标十字准星

问题根源

你的代码无法在GraphicsView中复现十字准星效果,核心是3个逻辑错误:

  • 体系混用错误:pg.GraphicsView、pg.GraphicsLayout属于pyqtgraph的GraphicsItem图形项体系,你封装的CrosshairPlotWidget是基于QWidget的常规Qt组件,直接把QWidget的布局塞给GraphicsLayout完全不兼容,组件无法正确渲染,鼠标事件也无法正常传递。
  • 信号绑定错误:你绑定的是PlotWidget初始化时临时创建的scene的鼠标移动信号,当PlotWidget被嵌入到外层GraphicsView时,实际生效的是外层GraphicsView持有的scene,原来的信号绑定完全失效。
  • 范围判断错误:一方面你写死了X轴范围判断阈值-10 < index <=0但没有给控件设置初始范围,默认坐标区间不满足判断条件;另一方面用PlotWidget的整体边界做鼠标位置判断,会把坐标轴、标签区域也算进去,坐标映射结果不准。
修正后可运行代码
import sys
import pyqtgraph as pg
from PyQt6 import QtCore
from PyQt6.QtWidgets import QApplication


class CrosshairPlotWidget(pg.PlotWidget):
    """带鼠标十字准星的绘图组件"""
    def __init__(self, parent=None):
        super().__init__(parent)
        # 基础坐标轴配置
        self.setLabel('left', 'Price')
        self.setLabel('bottom', 'Time')
        self.crosshair_color = (196, 220, 255)
        
        # 初始化测试曲线
        self.demo_curve = self.plot()
        # 生成测试数据
        x_data = list(range(-10, 1))
        y_data = [v*2 + 3 for v in x_data]
        self.demo_curve.setData(x_data, y_data)
        self.setXRange(-10, 0)

        # 初始化十字准星线
        self.v_line = pg.InfiniteLine(angle=90, movable=False)
        self.h_line = pg.InfiniteLine(angle=0, movable=False)
        self.v_line.setPen(self.crosshair_color)
        self.h_line.setPen(self.crosshair_color)
        self.addItem(self.v_line, ignoreBounds=True)
        self.addItem(self.h_line, ignoreBounds=True)

        # 信号代理占位,等组件加载完成后再绑定
        self._mouse_proxy = None

    def bind_crosshair_event(self):
        """组件添加到GraphicsView后调用,绑定正确的鼠标移动信号"""
        if self._mouse_proxy is not None:
            return
        active_scene = self.scene()
        self._mouse_proxy = pg.SignalProxy(
            active_scene.sigMouseMoved,
            rateLimit=60,
            slot=self._update_crosshair
        )

    def _update_crosshair(self, event):
        mouse_pos = event[0]
        view_box = self.plotItem.vb
        # 仅当鼠标在实际绘图区域内时更新十字准星
        if view_box.sceneBoundingRect().contains(mouse_pos):
            map_pos = view_box.mapSceneToView(mouse_pos)
            # 更新标题栏坐标显示
            self.setTitle(
                "<span style='font-size: 12pt'>x=%0.1f,   <span style='color: red'>y=%0.1f</span>"
                % (map_pos.x(), map_pos.y())
            )
            # 移动十字准星位置
            self.v_line.setPos(map_pos.x())
            self.h_line.setPos(map_pos.y())


if __name__ == '__main__':
    app = QApplication(sys.argv)
    # 初始化GraphicsView容器
    view = pg.GraphicsView()
    g_layout = pg.GraphicsLayout()
    view.setCentralItem(g_layout)
    view.resize(800, 600)
    view.show()

    # 实例化带十字准星的绘图组件
    crosshair_plot = CrosshairPlotWidget()
    # 直接作为原生图形项添加到布局,去掉QWidget布局套娃
    g_layout.addItem(crosshair_plot, 0, 0)
    # 组件加载完成后绑定鼠标事件
    crosshair_plot.bind_crosshair_event()

    if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
        QApplication.instance().exec()
关键修改说明
  • 重构组件继承逻辑:直接继承pg.PlotWidget实现十字准星功能,属于pyqtgraph原生支持的图形项类型,可以直接通过addItem添加到GraphicsLayout,彻底解决两套UI体系混用的兼容问题。
  • 调整信号绑定时机:等组件完全添加到GraphicsView、获取到正确的活动scene后再绑定鼠标移动信号,避免绑定到临时无效scene导致事件不触发。
  • 修正坐标判断逻辑:用绘图核心区域plotItem.vb的场景边界判断鼠标位置,仅在鼠标处于实际绘图区时更新十字线,避免坐标轴、标签区域干扰坐标映射结果。
  • 移除冗余层级:去掉原来多余的QGridLayout中转逻辑,组件层级更简洁,事件传递不会被额外布局拦截。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 23:24:26