Create PyQt menu from a list of strings

pyqt, python, qt, signals-slots

Solution

You're meeting what's been often referred to (maybe not entirely pedantically-correctly;-) as the "scoping problem" in Python -- the binding is late (lexical lookup at call-time) while you'd like it early (at def-time). So where you now have:

    for item in menuitems:
        entry = menu.addAction(item)
        self.connect(entry,QtCore.SIGNAL('triggered()'), lambda: self.doStuff(item))

try instead:

    for item in menuitems:
        entry = menu.addAction(item)
        self.connect(entry,QtCore.SIGNAL('triggered()'), lambda item=item: self.doStuff(item))

This "anticipates" the binding, since default values (as the `item` one here) get computed once an for all at def-time. Adding one level of function nesting (e.g. a double lambda) works too, but it's a bit of an overkill here!-)

You could alternatively use `functools.partial(self.doStuff, item)` (with an `import functools` at the top of course) which is another fine solution, but I think I'd go for the simplest (and most common) "fake default-value for argument" idiom.

Problem

I have a list of strings and want to create a menu entry for each of those strings. When the user clicks on one of the entries, always the same function shall be called with the string as an argument. After some trying and research I came up with something like this: ``` import sys from PyQt4 import QtGui, QtCore class MainWindow(QtGui.QMainWindow): def __init__(self): QtGui.QMainWindow.__init__(self) self.menubar = self.menuBar() menuitems = ["Item 1","Item 2","Item 3"] menu = self.menubar.addMenu('&Stuff') for item in menuitems: entry = menu.addAction(item) self.connect(entry,QtCore.SIGNAL('triggered()'), lambda: self.doStuff(item)) menu.addAction(entry) print "init done" def doStuff(self, item): print item app = QtGui.QApplication(sys.argv) main = MainWindow() main.show() sys.exit(app.exec_()) ``` Now the problem is that each of the menu items will print the same output: "Item 3" instead of the corresponding one. I'm thankful for any ideas about how I can get this right. Thanks.

Original source