Search code examples
pythonpyqtpyqt4qmenu

Slotting in new items at specified positions in QMenu


I am trying to achieve the following design of a QMenu:

|  Add New Item  |
------------------
|   New Item 1   |
|   New Item 2   |
|   New Item 3   |
------------------
| Default Item 1 |
| Default Item 2 |

Suppose if it is a new QMenu, the default design looks something lie:

|  Add New Item  |
------------------
| Default Item 1 |
| Default Item 2 |

In my code, while I am able to create the new item, I am having 2 issues.

  1. For any of the new items that I have specified while using the option - Add new item, the default 3 options are re-added into the QMenu, which results in duplicated options... If I add in self.qmenu.clear(), while it resolves the duplicated default items, no new items will be populated...

  2. Is it possible to slot new items to be in-between of the add new item and default items? Or, combine 2 qmenus into one, something akin to how you add widgets in a QVBoxLayout where you can control the ordering?

Appreciate in advance for any insights.

class Example(QtGui.QMainWindow):
    def __init__(self, parent=None):
        super(Example, self).__init__(parent)
        self.initUI()


    def initUI(self):         
        self.setGeometry(300, 300, 300, 200)
        self.setWindowTitle('Context menu')    
        self.qmenu = QtGui.QMenu()

    def contextMenuEvent(self, event):
        # self.qmenu.clear()

        # For adding in new item(s)
        add_item_action = QtGui.QAction('Add new item', self)
        slot = functools.partial(self.add_new_item)
        add_item_action.triggered.connect(slot)
        self.qmenu.addAction(add_item_action)
        self.qmenu.addSeparator()

        # Default items
        def_item_01 = self.qmenu.addAction("Default Item A")
        def_item_02 = self.qmenu.addAction("Default Item B")
        action = self.qmenu.exec_(self.mapToGlobal(event.pos()))

    def add_new_item(self):
        new_item_name = raw_input('Name of new item : ')
        if new_item_name:
            self.qmenu.addSeparator()
            # The new items should be checked upon created
            new_action = QtGui.QAction(new_item_name, self.qmenu, checkable=True)
            new_action.setChecked(True)
            self.qmenu.addAction(new_action)

my_win = Example()
my_win.show()

Solution

  • How many times do you call contextMenuEvent? every time you click right so you are continually adding the Defaults items, do not you think it is better to add it only once so as not to add duplicates?, that is the right thing to do.

    On the other hand if you want to insert an item in the QMenu you must use insertAction().

    And finally do not use raw_input() (or input() in python3) since they are blocking and freeze the GUI, it is best to use a dialog that asks you for the data.

    class Example(QtGui.QMainWindow):
        def __init__(self, parent=None):
            super(Example, self).__init__(parent)
            self.initUI()
    
        def initUI(self):         
            self.setGeometry(300, 300, 300, 200)
            self.setWindowTitle('Context menu')    
    
            self.qmenu = QtGui.QMenu()
            add_item_action = QtGui.QAction('Add new item', self,
                triggered=self.add_new_item)
            self.qmenu.addAction(add_item_action)
            self.qmenu.addSeparator()
    
            self.separator =  self.qmenu.addSeparator()
            # Default items
            def_item_01 = self.qmenu.addAction("Default Item A")
            def_item_02 = self.qmenu.addAction("Default Item B")
    
        def contextMenuEvent(self, event):
            action = self.qmenu.exec_(self.mapToGlobal(event.pos()))
    
        @QtCore.pyqtSlot()
        def add_new_item(self):
            new_item_name, ok = QtGui.QInputDialog.getText(self, "name of item", "Name of new item : ")
            if ok:
                new_action = QtGui.QAction(new_item_name, self.qmenu, checkable=True)
                self.qmenu.insertAction(self.separator, new_action)
    
    if __name__ == '__main__':
        import sys
        app = QtGui.QApplication(sys.argv)
        my_win = Example()
        my_win.show()
        sys.exit(app.exec_())