Search code examples
pythonpyqtpyqt5qgraphicsviewqgraphicsscene

Determine which ellipse in click event


I'm very new to python and PyQT and have become stuck. So apologies if my code is a bit of a mess. I have spent quite some time searching but haven't yet found anything that helps my situation.

I'm trying to identify which ellipse was clicked.

I basically have a section that creates ellipses in a circle and prints text over it based on values from an array of lists:

class MyFrame(QtWidgets.QGraphicsView):
    def __init__(self, parent = None ):
        super(MyFrame, self).__init__(parent)

        self.setScene(QtWidgets.QGraphicsScene())
        self.setBackgroundBrush(QtGui.QColor(QtCore.Qt.darkGray))
        self.setRenderHints(self.renderHints() | QtGui.QPainter.Antialiasing  | QtGui.QPainter.SmoothPixmapTransform)

        arLights = list( {} for i in xrange(12) )
        circX = 0
        circY = 0
        maxX = 0
        maxY = 0
        i = 0
        pen = QtGui.QPen(QtGui.QColor(QtCore.Qt.lightGray).darker(50))
        brush = QtGui.QBrush(QtGui.QColor(QtCore.Qt.lightGray))
        font = QtGui.QFont('White Rabbit')
        font.setPointSize(12)

        arLights = getLights()

        theta = radians(360)
        columns = len(filter(None, arLights))
        alpha = theta / columns

        for a in range(columns):
            angle = a * alpha
            circX = (w + x) * cos(angle)
            circY = (h + y) * sin(angle)

            item = callbackEllipse(circX, circY, w, h) #(x+xi*(w+x), y+yi*(h+y), w, h)
            item.setAcceptHoverEvents(True)
            item.setPen(pen)
            item.setBrush(brush)
            self.scene().addItem(item)
            item.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable)
            self.writeText(arrLight[i]['name'], circX + (w / 4), circY + (h * 0.75))
            i = i + 1

This is my mouseReleaseEvent, where I am then trying to work out the name associated with that ellipse by using the same formula that I used to lay them out.

class callbackEllipse(QtWidgets.QGraphicsEllipseItem):
    def mouseReleaseEvent(self, event):
        # recolor on click
        color = QtGui.QColor(QtCore.Qt.lightGray)
        brush = QtGui.QBrush(color)
        QtWidgets.QGraphicsEllipseItem.setBrush(self, brush)

        theta = radians(360)
        alpha = theta / arrRange

        arrLights = getLights()
        for a in xrange(0, len(filter(None, arrLights))):
            if (event.scenePos().x() > ((w + x) * cos(a * alpha))
                and event.scenePos().x() < (((w + x) * cos(a * alpha)) + w)
                and event.screenPos().y() < ((h + y) * sin(a * alpha))
                and event.screenPos().y() < (((h + y) * sin(a * alpha)) + h)
                ):
                print(arrLights[a]['name'])
                break

        return QtWidgets.QGraphicsEllipseItem.mouseReleaseEvent(self, event)

This doesn't work as intended. I've tried .sceenPos() .screenPos() and .pos() but they don't appear to match up with the co-ordinates from when I created the ellipses, resulting in it identifying the wrong associated text or not finding a match at all.

Can anyone help me with a way to identify the ellipse or what why my code isn't working the way I want it to? There doesn't seem to be a way to 'name' them, which would be easier.


Solution

  • Do not work 2 times unnecessarily, save a value reference and print it since mouseReleaseEvent() will only be called if the item is pressed:

    import math
    from PyQt5 import QtCore, QtGui, QtWidgets
    
    def getLights():
        return [{"name": str(i)} for i in range(10)]
    
    class CallbackEllipse(QtWidgets.QGraphicsEllipseItem):
        def __init__(self, light, *args, **kwargs):
            super(CallbackEllipse, self).__init__(*args, **kwargs)
            self._light = light
    
        def mouseReleaseEvent(self, event):
            color = QtGui.QColor(QtCore.Qt.lightGray)
            brush = QtGui.QBrush(color)
            self.setBrush(brush)    
            print(self._light["name"])
            super(CallbackEllipse, self).mouseReleaseEvent(event)
    
    class MyFrame(QtWidgets.QGraphicsView):
        def __init__(self, parent=None):
            super(MyFrame, self).__init__(parent)
    
            self.setScene(QtWidgets.QGraphicsScene(self))
            self.setBackgroundBrush(QtGui.QColor(QtCore.Qt.darkGray))
            self.setRenderHints(self.renderHints() | QtGui.QPainter.Antialiasing  | QtGui.QPainter.SmoothPixmapTransform)
    
            arLights = getLights()
    
            theta = math.radians(360)
            filter_lights = list(filter(None, arLights)) 
            num_of_columns = len(filter_lights)
            delta = theta/num_of_columns
            circX, circY = 0, 0
            w, h, x, y = 100, 100, 100, 100
    
            pen = QtGui.QPen(QtGui.QColor(QtCore.Qt.lightGray).darker(50))
            brush = QtGui.QBrush(QtGui.QColor(QtCore.Qt.lightGray))
    
            for i, light in enumerate(filter_lights):
                angle = i*delta
                circX = (w + x) * math.cos(angle)
                circY = (h + y) * math.sin(angle)
                item = CallbackEllipse(light, circX, circY, w, h)
                item.setAcceptHoverEvents(True)
                item.setPen(pen)
                item.setBrush(brush)
                item.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable)
                self.scene().addItem(item)
    
    if __name__ == '__main__':
        import sys
        app = QtWidgets.QApplication(sys.argv)
        w = MyFrame()
        w.show()
        sys.exit(app.exec_())