My objective is code out an ellipse ROI that cannot be rotated. Consider the following piece of code modified from one of the PyQtGraph examples :
import numpy as np
import cv2
import pyqtgraph as pg
from PyQt5 import QtGui
import numpy.ma as ma
from PyQt5.QtWidgets import QMessageBox
pg.setConfigOptions(imageAxisOrder='row-major')
## Create image to display
image = cv2.imread('panda.jpg')
def picturetranspose(picture):
shape = picture.shape
result = np.empty((shape[1],shape[0],shape[2]),dtype= np.uint8)
for i in range(0,3):
result[:,:,i] = np.transpose(picture[:,:,i])
return result
arr = np.rot90(picturetranspose(image))
app = pg.mkQApp("ROI Examples")
w = pg.GraphicsLayoutWidget(show=True, size=(1000,800), border=True)
w.setWindowTitle('pyqtgraph example: ROI Examples')
w1 = w.addLayout(row=0, col=0)
v1a = w1.addViewBox(row=1, col=0, lockAspect = True)
v1a.setLimits(minXRange = arr.shape[0]//10, minYRange = arr.shape[1]//10, maxXRange = 5*arr.shape[0], maxYRange = 5*arr.shape[1])
img1a = pg.ImageItem(arr)
v1a.addItem(img1a)
rois = []
x=pg.EllipseROI([60, 10], [30, 20], pen=pg.mkPen('b', width=5),rotatable = False)
x.removeHandle(0)
rois.append(x)
for roi in rois:
roi.sigRegionChanged.connect(img1a.setImage(arr))
v1a.addItem(roi)
img1a.setImage(arr)
Here the image "panda.jpg" is given by:
By clicking on the square light blue handle, the size of ROI can be changed. Since I set rotatable = False
, the ROI cannot be rotated and clicking on the circular handle will not rotate the ROI .
However, the line x.removeHandle(0)
should delete the light blue circular handle so that it does not show up on the screen at all . Is this a bug? What am I missing ?
CodePudding user response:
When a QGraphicsItem is created it's not immediately added to a scene, and in that time frame some scene-related aspects are "stored" until the item is actually put to a scene. Some of those properties are pretty obvious (such as the item position), but others are not. One of them is adding child items. When a "main" item is added to the scene, all of its children (and grandchildren, great-grandchildren, etc.) are added along with it.
This is what happens when a ROI is created: its handles are created in its __init__
(see the sources), but removeHandle()
only removes the handle from the scene if the ROI actually has a scene()
.
Remember that PyQt (and PySide) are bindings to Qt, meaning that we always work with python wrappers around C objects. Even when the last reference to a python object is deleted, only the python object is actually deleted, but if that object is a wrapper around a C object and that object has a parent, the actual object is not deleted after all.
While pyqtgraph removes the handle from its internal list, the C object that represents the handle still exists as a child of the ROI, so, when the ROI is added to the scene, the handle still exists.
A possible solution is to remove the handles after the ROI has been added to the scene:
for roi in rois:
roi.sigRegionChanged.connect(img1a.setImage(arr))
v1a.addItem(roi)
roi.removeHandle(0)
This works because removeHandle
only removes the handle when a scene exists for the item, and in this way the child item can be actually removed from the scene.
Note that, according to the sources, EllipseROI
adds two handles: rotate and scale. You will probably want to remove all of them:
for roi in rois:
roi.sigRegionChanged.connect(img1a.setImage(arr))
v1a.addItem(roi)
while roi.handles:
roi.removeHandle(0)
I suggest you to file a report on the pyqtgraph repository about this, solving it on their side should be pretty easy.