pyqt dynamic generate QMenu action and connect

12.4k views Asked by At

Still learning how pyqt works. I want to dynamically generate a customContextMenu and connect with a function. So far I got the following but the connect part not working ?

import sys
from PyQt4 import QtGui, QtCore

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

    # create button
    self.button = QtGui.QPushButton("test button", self)       
    self.button.resize(100, 30)

    # set button context menu policy
    self.button.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
    self.connect(self.button, QtCore.SIGNAL('customContextMenuRequested(const QPoint&)'), self.on_context_menu)
    self.popMenu = QtGui.QMenu(self)

    def on_context_menu(self, point):
        self.popMenu.clear()

        #some test list for test
        testItems = ['itemA', 'itemB', 'itemC']
        for item in testItems:
            action = self.btn_selectPyFilterPopMenu.addAction("Selected %s" % item)
            self.connect(action,QtCore.SIGNAL("triggered()"),self,QtCore.SLOT("printItem('%s')" % item))    
        self.popMenu.exec_(self.button.mapToGlobal(point))

    @pyqtSlot(str)
    def printItem(self, item):
        print item

def main():
    app = QtGui.QApplication(sys.argv)
    form = MainForm()
    form.show()
    app.exec_()

if __name__ == '__main__':
    main()
3

There are 3 answers

2
ekhumoro On BEST ANSWER

Your code is almost right. You just need to connect the signals to a lambda with a default argument, like this:

    for item in testItems:
        action = self.popMenu.addAction('Selected %s' % item)
        action.triggered.connect(
            lambda chk, item=item: self.printItem(item))

The default argument ensures that each lambda gets a copy of the current loop variable. Also note that an initial chk argument is also required. This is because the triggered signal sends its current checked-state (true or false) by default, which would clobber the item argument of the lambda.

Finally, I would urge to use the new-style syntax when connecting signals - the old style can be very error-prone, and is far less pythonic.

3
Rustam Safin On

If I understand you right:

import sys
from PyQt4 import QtGui, QtCore

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

    # create button
    self.button = QtGui.QPushButton("test button", self)       
    self.button.resize(100, 30)

    # set button context menu policy
    self.button.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
    self.customContextMenuRequested.connect (self.on_context_menu)

    def on_context_menu(self, point):

        popMenu = QtGui.QMenu(self)

        #some test list for test
        testItems = ['itemA', 'itemB', 'itemC']

        #
        for item in testItems:
            action = QtGui.Action(item)
            action.triggered.connect(lambda x: print item)

        popMenu.exec_(self.button.mapToGlobal(point))
0
Davy On

I tryed and correct the example given in the first post. Here is a working version. Right click on the button, select an item and it will be printed inn your terminal :

import sys
from PyQt4 import QtGui, QtCore
class MainForm(QtGui.QMainWindow):
    def __init__(self, parent=None):
        super(MainForm, self).__init__(parent)

        # create button
        self.button = QtGui.QPushButton("test button",self)       
        self.button.resize(100, 30)

        # set button context menu policy
        self.button.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        self.connect(self.button, QtCore.SIGNAL('customContextMenuRequested(const QPoint&)'), self.on_context_menu)
        self.popMenu = QtGui.QMenu(self)

    def on_context_menu(self, point):
        self.popMenu.clear()

        #some test list for test
        testItems = ['itemA', 'itemB', 'itemC']
        for item in testItems:
        action = self.popMenu.addAction('Selected %s' % item)
        action.triggered[()].connect(
            lambda item=item: self.printItem(item))
        self.popMenu.exec_(self.button.mapToGlobal(point))

    @QtCore.pyqtSlot(str)
    def printItem(self, item):
        print item

def main():
    app = QtGui.QApplication(sys.argv)
    form = MainForm()
    form.show()
    app.exec_()

if __name__ == '__main__':
    main()