Create PyQt menu from a list of strings
-
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.
-
Answer:
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.
Gerald Senarclens de Grancy at Stack Overflow Visit the source
Other answers
This should work, but I'm pretty sure there was a better way that I can't recall right now. def do_stuff_caller(self, item): return lambda: self.doStuff(item) ... self.connect(entry, QtCore.SIGNAL('triggered()'), self.do_stuff_caller(item)) Edit: Shorter version, that still isn't what I'm thinking about... or maybe it was in another language? :) (lambda x: lambda self.do_stuff(x))(item)
wuub
Related Q & A:
- Is there a tool where you give a list of things and it returns related words?Best solution by Web Applications
- How to add a list to the existing list jQuery?Best solution by designchemical.com
- How can I create circular menu in android?Best solution by Stack Overflow
- How can I remove a contact from a list in my address book?Best solution by windows.microsoft.com
- How to tune a new violin strings?Best solution by wiki.answers.com
Just Added Q & A:
- How many active mobile subscribers are there in China?Best solution by Quora
- How to find the right vacation?Best solution by bookit.com
- How To Make Your Own Primer?Best solution by thekrazycouponlady.com
- How do you get the domain & range?Best solution by ChaCha
- How do you open pop up blockers?Best solution by Yahoo! Answers
For every problem there is a solution! Proved by Solucija.
-
Got an issue and looking for advice?
-
Ask Solucija to search every corner of the Web for help.
-
Get workable solutions and helpful tips in a moment.
Just ask Solucija about an issue you face and immediately get a list of ready solutions, answers and tips from other Internet users. We always provide the most suitable and complete answer to your question at the top, along with a few good alternatives below.