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

如何在PyQt6与PyQtGraph中恢复ROI交互并定制鼠标行为

问题

使用PyQt6和PyQtGraph开发DICOM图像可视化界面,通过继承pg.ImageView实现ImageContainer类展示图像。已通过替换self.view的mousePressEvent和mouseMoveEvent实现自定义鼠标行为,同时用eventFilter实现鼠标悬停显示像素值。

允许用户添加继承自pg.EllipseROI的CustomEllipseROI椭圆ROI,默认ROI支持移动和调整大小,但由于修改了ImageContainer的鼠标行为,该功能失效,尝试给CustomEllipseROI添加eventFilter也未解决。

需求: 重新启用ROI的移动、调整大小功能,同时保留自定义鼠标行为,甚至能为ROI定制特定鼠标行为。

解决方案

直接替换self.view的鼠标事件方法会覆盖PyQtGraph内部的事件处理链,导致ROI无法接收鼠标事件。需要保留原有事件处理逻辑,再插入自定义行为,推荐以下实现方式:

方式一:保留原view鼠标事件并追加自定义逻辑

保存view原本的鼠标事件方法,在自定义方法中先调用原方法,确保ROI的事件被正常处理,再执行自定义逻辑。

方式二:给view安装事件过滤器(更灵活)

通过事件过滤器拦截view的鼠标事件,先判断事件是否命中ROI,若ROI已处理事件则跳过自定义逻辑,否则执行自己的鼠标行为。

修改后的完整代码(方式一)

import sys
import pyqtgraph as pg
import numpy as np

from PyQt6.QtWidgets import QApplication
from PyQt6.QtCore import Qt, QEvent

class CustomEllipseROI(pg.EllipseROI):
    def __init__(self, pos, size, **args):
        super().__init__(pos, size, **args)
   
    def hoverEvent(self, event):
        print("Hover ROI")
        # 调用父类方法保证ROI的悬停样式正常
        super().hoverEvent(event)

class ImageContainer(pg.ImageView):
    def __init__(self, parent = None):
        super().__init__(parent)
        self.create_data()
        self.init_image()
        self.create_ROI()

    def create_data(self):
        dataRed = np.ones((100, 200, 200)) * np.linspace(90, 150, 100)[:, np.newaxis, np.newaxis]
        dataRed += pg.gaussianFilter(np.random.normal(size=(200, 200)), (5, 5)) * 100
        dataGrn = np.ones((100, 200, 200)) * np.linspace(90, 180, 100)[:, np.newaxis, np.newaxis]
        dataGrn += pg.gaussianFilter(np.random.normal(size=(200, 200)), (5, 5)) * 100
        dataBlu = np.ones((100, 200, 200)) * np.linspace(180, 90, 100)[:, np.newaxis, np.newaxis]
        dataBlu += pg.gaussianFilter(np.random.normal(size=(200, 200)), (5, 5)) * 100
        self.data = np.concatenate(
            (dataRed[:, :, :, np.newaxis], dataGrn[:, :, :, np.newaxis], dataBlu[:, :, :, np.newaxis]), axis=3
        )

    def init_image(self):
        self.setImage(self.data)
        self.ui.histogram.hide()
        self.ui.roiBtn.hide()
        self.ui.menuBtn.hide()
        self.ui.roiPlot.hide()

        self.setAttribute(Qt.WidgetAttribute.WA_Hover)
        self.installEventFilter(self)

        # 保存view原本的鼠标事件方法
        self.original_view_mouse_press = self.view.mousePressEvent
        self.original_view_mouse_move = self.view.mouseMoveEvent
        # 替换为自定义方法
        self.view.mousePressEvent = self.mouse_press_event
        self.view.mouseMoveEvent = self.mouse_move_event

    def create_ROI(self):
        self.ellipseROI_item = CustomEllipseROI((50, 50), (50, 50))
        self.view.addItem(self.ellipseROI_item)

    def eventFilter(self, obj, event):
        if event.type() == QEvent.Type.HoverMove:
            print("Hover Image")
            # 调用显示像素值的函数
        elif event.type() == QEvent.Type.HoverLeave:
            print("Leave Image")
            # 清空像素值显示
        return super().eventFilter(obj, event)
    
    def mouse_press_event(self, event):
        # 先调用原view的鼠标点击事件,保证ROI能处理点击
        self.original_view_mouse_press(event)
        # 仅当事件未被ROI处理时,执行自定义逻辑
        if not event.isAccepted():
            self.prevPos = event.pos()

    def mouse_move_event(self, event):
        # 先调用原view的鼠标移动事件,保证ROI能处理拖拽/调整
        self.original_view_mouse_move(event)
        # 仅当事件未被ROI处理时,执行自定义逻辑
        if not event.isAccepted():
            if event.buttons() == Qt.MouseButton.MiddleButton:
                print("Middle Button on Image")
                # 调用调整对比度的函数
            elif event.buttons() == Qt.MouseButton.LeftButton:
                print("Left Button on Image")
                # 调用切换切片的函数
            elif event.buttons() == Qt.MouseButton.RightButton:
                print("Right Button on Image")
                # 调用缩放的函数

if __name__ == "__main__":
    app = QApplication(sys.argv)
    main = ImageContainer()
    main.show()
    sys.exit(app.exec())

关键修改说明

  1. 保存了self.view原本的鼠标事件方法,在自定义方法中优先调用,确保ROI的事件处理逻辑正常执行。
  2. 通过event.isAccepted()判断事件是否被ROI处理,只有未被处理时才执行自定义鼠标行为,避免冲突。
  3. 在CustomEllipseROI的hoverEvent中调用父类方法,保证ROI的悬停样式(如控制点高亮)正常显示。

定制ROI特定鼠标行为

如果需要给ROI添加专属鼠标行为,可直接在CustomEllipseROI中重写对应的事件方法,比如:

class CustomEllipseROI(pg.EllipseROI):
    def __init__(self, pos, size, **args):
        super().__init__(pos, size, **args)
   
    def hoverEvent(self, event):
        print("Hover ROI")
        super().hoverEvent(event)
    
    def mousePressEvent(self, event):
        print("Clicked on ROI")
        # 调用父类方法保证ROI原有功能正常
        super().mousePressEvent(event)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 16:44:51