Search code examples
pythonpyqt5pyqtgraph

PyQtGraph: get SpotItem objects within ROI selection


Using PyQt5 and PyQtGraph, I have a circular Scatter Plot displayed on top of an image. Both these items are inside a QGraphicsView, which was promoted from an ImageView object. I am trying to find an easy way of mass-selecting specific points when there could be potentially hundreds generated.

I discovered a CircleROI object that subclasses pyqtgraph's ROI. After creating the circle, I'd like to have some way to reference the points "selected" inside it...from there data export is simple. But I don't know how to make that work.

Here is what I am trying at the moment:

    self.roi = pg.CircleROI([500, 500], [100, 100], pen=(4,9), movable=True, resizable=True)
    self.roi.setAcceptedMouseButtons(QtCore.Qt.MouseButton.RightButton)
    self.roi.sigClicked.connect(self.select_points)
    self.graphicsView.view.addItem(self.roi)

    def select_points(self):
        #self.dataPlot is a pg.ScatterPlotItem
        selected = self.roi.getArrayRegion(self.dataPlot.points, self.graphicsView.imageItem, axes=(0))
        print(*selected, sep = ", ")

I am getting this error:

selected = self.roi.getArrayRegion(self.dataPlot.points, self.graphicsView.imageItem, axes=(0))
  File "C:\runnable\soft\Python37\lib\site-packages\pyqtgraph\graphicsItems\ROI.py", line 1891, in getArrayRegion
returnMappedCoords, **kwds)
  File "C:\runnable\soft\Python37\lib\site-packages\pyqtgraph\graphicsItems\ROI.py", line 1193, in getArrayRegion
rgn = fn.affineSlice(data, shape=shape, vectors=vectors, origin=origin, axes=axes, **kwds)
  File "C:\runnable\soft\Python37\lib\site-packages\pyqtgraph\functions.py", line 810, in affineSlice
x = affineSliceCoords(shape, origin, vectors, axes)
  File "C:\runnable\soft\Python37\lib\site-packages\pyqtgraph\functions.py", line 740, in affineSliceCoords
if len(origin) != len(axes):
TypeError: object of type 'int' has no len()

How can I fix this error? If this is the wrong approach, what would I do instead?


Solution

  • I actually found the answer a while ago but forgot to update. I had to get a little creative to get exactly what I wanted.

    The first step is of course to make the actual ROI object, and add it to the graphics view:

    self.roi = pg.CircleROI([110, 50], [20, 20], pen=(4,9))
    self.graphicsView.view.addItem(self.roi)
    

    Then use the ROI's geometry to determine what spots on the graph it is colliding with. I had to use mapRectToItem to have the roi's bounding rect be in the same coordinate space as the data plot:

    roiHitbox = self.roi.mapRectToItem(self.dataPlot, self.roi.boundingRect())
    points = self.dataPlot.points()
    collidedPoints = []
    for p in points:
        #p.viewPos() is a QPointF.
        if(roiHitbox.contains(p.viewPos())):
            collidedPoints.append(p)
    newList = set(collidedPoints)