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.mousePressEvent
method 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.mousePressEvent
andself.view.mouseMoveEvent
as it is best practise but the bug only occurs because ofself.view.mousePressEvent
specifically.