2

I'm looking for a way to trigger an event upon clicking on a QgsVertexMarker object which is located on a mapCanvas. In the official docs, there is a mousePressEvent method which I actually re-implement in my custom QgsVertexMarker class:

class CustomMarker(QgsVertexMarker):
...
    def mousePressEvent(self, event):
        print("---MOUSE EVENT---")
        if event.button() == Qt.LeftButton:
            print("Left click")
            self.getMarkerProperties()
...

Now, the problem is that I cannot figure out how to make it work. I read about instantiating a QGraphicsView object and eventFilters but I'm not sure if this is the right path to follow.

NOTE: The default implementation of setToolTip method already works without any tinkering so I'm wondering why mousePressEvent doesn't work out of the box.

1 Answer 1

2

The map canvas of QGIS is a subclass of QGraphicsView. For events to be propagated to the actual canvas items it is required to call the default implementations of overridden mouse event methods (ref). QGIS does not do that! It only passes the events to the active map tool, not to the canvas items.

To intercept the events anyway you can use an event filter. Which in this specific case is also not straightforward, because QgsVertexMarker (and all subclasses of QGraphicsItem) don't inherit QObject therefore they don't qualify as valid filter items. Instead you need to define a custom class which can receive the events and then forward them to your actual canvas item.

Consider the following example, which can be pasted in the console to try out:

class CustomMarker(QgsVertexMarker):
    
    def __init__(self, canvas):
        super(CustomMarker, self).__init__(canvas)
        self.canvas = canvas
        self.event_filter = CanvasEventFilter(self)
        self.canvas.viewport().installEventFilter(self.event_filter)

    def mousePressEvent(self, event):
        print("---MOUSE EVENT---")
        if event.button() == Qt.LeftButton:
            print("Left click")
            
        super().mousePressEvent(event)


class CanvasEventFilter(QObject):
    
    def __init__(self, marker):
        self.marker = marker
        super(CanvasEventFilter, self).__init__()
        
    def eventFilter(self, obj, event):
        if event.type() == QEvent.MouseButtonPress:
            # forward event if marker is under current mouse position 
            if self.marker.isUnderMouse():
                self.marker.mousePressEvent(event)
            
        return False
        

m = CustomMarker(iface.mapCanvas())

m.setCenter(QgsPointXY(0, 0))
m.setColor(QColor(0, 0, 255))
m.setPenWidth(5)
m.show()
2
  • thank you for such a detailed explanation with the working code snippet included. This is the best of what I could expect. The only issue I have with it is the TypeError: mousePressEvent(self, QGraphicsSceneMouseEvent): argument 1 has unexpected type 'QMouseEvent' error which occurs when I click on the marker. But that's the least problem I have to deal with now :-)
    – druid
    Commented Oct 17, 2022 at 6:55
  • 1
    @druid You're welcome. You can safely remove the call to super().mousePressEvent(event). That's the origin of the error you described, because mousePressEvent needs a QGraphicsSceneMouseEvent as argument instead of plain QMouseEvent which is received from the event filter. I missed that while writing the answer. Also consider accepting the answer if you found it helpful :)
    – CodeBard
    Commented Oct 17, 2022 at 12:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.