Home > Mobile >  PyQtGraph ROI RemoveHandle Does Not Remove the Circular Handle for the Ellipse ROI
PyQtGraph ROI RemoveHandle Does Not Remove the Circular Handle for the Ellipse ROI

Time:06-02

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: enter image description here

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.

  • Related