I'm creating a DICOM image visualization interface with PyQt6 and PyQtGraph. I display the image using an ImageContainer class, a subclass of pg.ImageView. I've modified the mouse behavior during click using mousePressEvent and mouseMoveEvent. I also display the pixel values hovered by the mouse without clicking, using an eventFilter.
I allow the user to add an elliptical region of interest (ROI) using a CustomEllipseROI class, a subclass of pg.EllipseROI. By default, the ROI is movable and resizable, but since I've modified the mouse behavior of the ImageContainer instance, it no longer works.
I tried to implement another eventFilter in the CustomEllipseROI class, but failed.
Question: How can I enable the movement and resizing of the ROI again, and even customize the mouse behavior specifically for the ROI?
Here's a minimal reproducible example. Thanks for your help!
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")
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)
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")
# Call function that displays the image value at mouse position
elif event.type() == QEvent.Type.HoverLeave:
print("Leave Image")
# Clear value display
return super().eventFilter(obj, event)
def mouse_press_event(self, event):
self.prevPos = event.pos()
def mouse_move_event(self, event):
if event.buttons() == Qt.MouseButton.MiddleButton:
print("Middle Button on Image")
# Call function to change contrast
elif event.buttons() == Qt.MouseButton.LeftButton:
print("Left Button on Image")
# Call function to move across slices of a volume
elif event.buttons() == Qt.MouseButton.RightButton:
print("Right Button on Image")
# Call function to zoom in/out
if __name__ == "__main__":
app = QApplication(sys.argv)
main = ImageContainer()
main.show()
sys.exit(app.exec())```
The problem is that you're overriding the
self.view.mousePressEventmethod without calling the original method. It is best practise to return with a call to the original method of any method you're overriding.My solution adds the return call to both
self.view.mousePressEventandself.view.mouseMoveEventas it is best practise but the bug only occurs because ofself.view.mousePressEventspecifically.