Search code examples
pythonqgraphicsviewqmenupyqt6

how to initiate context menu event in QGraphicsItem from QGraphicsView context menu - PyQt6


I have a context menu on the QGraphicsView. I cannot activate the context menu of a QTextGraphicsItem. I read that I need to send a scene event to the item, but I cannot find the event needed to make the sendEvent method work.

def graphicsview_menu(self, position):
    item = self.ui.graphicsView.itemAt(position)
    if item is not None:
        self.scene.sendEvent(item, event)
        return
    # Menu for blank graphics view area
    menu = QtWidgets.QMenu()
    ...

Solution

  • As the contextMenuPolicy documentation explains:

    The default value of this property is Qt::DefaultContextMenu, which means the contextMenuEvent() handler is called. [...] With Qt::CustomContextMenu, the signal customContextMenuRequested() is emitted.

    This means that if you set the custom policy, the graphics view will not call contextMenuEvent(), which is what actually allows it to eventually send a new graphics context menu event to the item, if it supports it.

    The solution is to install an event filter on the viewport (which is what actually receives the event in the first place), call the base implementation and return the result of the event.isAccepted(), which tells if the event was handled (an item probably showing a menu) or not.

    If the event wasn't handled, then the filter will return False, which means that the default implementation would be called: the filter was installed on the viewport, False will make it ignore the default call and forward it to the event of the parent (the view itself), and since the custom policy tells to emit the custom context menu signal, your graphicsview_menu function will be actually called:

            # somewhere in the __init__
            self.ui.graphicsView.viewport().installEventFilter(self)
    
        # ...
    
        def eventFilter(self, obj, event):
            if (obj == self.ui.graphicsView.viewport() and 
                event.type() == event.Type.ContextMenu):
                    self.ui.graphicsView.contextMenuEvent(event)
                    return event.isAccepted()
            return super().eventFilter(obj, event)
    

    Note: the above obviously assumes that the main class inherits from QWidget (or at least QObject).