从字符串列表创建PyQt菜单

时间:2009-07-08 22:01:57

标签: python qt pyqt signals-slots

我有一个字符串列表,并希望为每个字符串创建一个菜单项。当用户单击其中一个条目时,始终使用字符串作为参数调用相同的函数。经过一番尝试和研究后,我想出了类似的东西:

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_())

现在的问题是每个菜单项都会打印相同的输出:“第3项”而不是相应的输出。我很感激有关如何做到这一点的任何想法。感谢。

2 个答案:

答案 0 :(得分:23)

你正在遇到经常提到的(可能不完全是迂腐正确的;-)作为Python中的“范围问题” - 绑定是迟到的(在通话时间进行词汇查找),而你喜欢它早(在def-time)。那么你现在的位置:

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

尝试改为:

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

这“预期”绑定,因为默认值(在此为item)在def-time计算一次。添加一个级别的函数嵌套(例如双lambda)也可以,但这里有点过分了! - )

你也可以使用functools.partial(self.doStuff, item)(当然在顶部有一个import functools),这是另一个很好的解决方案,但我想我会选择最简单的(也是最常见的)“假默认 - 参数“idiom。

答案 1 :(得分:2)

这应该有用,但我很确定现在有一种更好的方法让我无法回想起。

def do_stuff_caller(self, item):
    return lambda: self.doStuff(item)

...
self.connect(entry, QtCore.SIGNAL('triggered()'), self.do_stuff_caller(item))

修改: 更短的版本,仍然不是我正在考虑的......或者它可能是另一种语言? :)

(lambda x: lambda self.do_stuff(x))(item)