pythonpyqtpyqt5qmenu

In a QMenu, how to make a submenu callable like a QAction


I am trying to implement something like a classification tree, in which I right-click a row in QTableWidget to call a Menu, then I choose a class label for it. My label tree is like this:

Here Class A, Class A2 are obviously added through QMenu.addMenu() method, in which they are not callable. Class A1, Class A3, and Class B are added through QMenu.addAction() method and they are callable. So I am wondering is there any way to make a submenu callable like a QAction that can be triggered? Or are there any better ways to achieve my design purpose?

btw I think aboutToShow() signal does not work, since the signal is emitted when the submenu shows up instead of being clicked.

Code:

class ClassMenu(QMenu):

    def __init__(self):
        
        super(ClassMenu, self).__init__()
        class_A = self.addMenu("ClassA")
        class_A1 = class_A.addAction("ClassA1")
        class_A2 = class_A.addMenu("ClassA2")
        class_A3 = class_A2.addAction("ClassA3")
        class_B = self.addAction("ClassB")
        
        class_A1.triggered.connect(self.onMenuClicked)
        class_A3.triggered.connect(self.onMenuClicked)
        class_B.triggered.connect(self.onMenuClicked)

    def onMenuClicked(self):
        print(self.sender().text())

Solution

  • A possible solution is to override mousePressEvent and verify that the QAction associated with the position has a QMenu associated with it:

    from PyQt5.QtWidgets import QApplication, QMenu
    
    
    class ClassMenu(QMenu):
        def __init__(self):
            super(ClassMenu, self).__init__()
            class_A = self.addMenu("ClassA")
            class_A1 = class_A.addAction("ClassA1")
            class_A2 = class_A.addMenu("ClassA2")
            class_A3 = class_A2.addAction("ClassA3")
            class_B = self.addAction("ClassB")
    
            class_A1.triggered.connect(self.onActionClicked)
            class_A3.triggered.connect(self.onActionClicked)
            class_B.triggered.connect(self.onActionClicked)
    
            print(class_A.menuAction())
    
        def onActionClicked(self):
            print(self.sender().text())
    
        def onMenuClicked(self, menu):
            print(menu.title())
    
        def mousePressEvent(self, event):
            super().mousePressEvent(event)
            action = self.actionAt(event.pos())
            if not action:
                return
            menu = action.menu()
            if menu:
                self.onMenuClicked(menu)
    
    
    def main():
        app = QApplication([])
        menu = ClassMenu()
        menu.move(100, 100)
        menu.exec_()
    
    
    if __name__ == "__main__":
        main()