12

我有一个字符串列表,想为每个字符串创建一个菜单项。当用户单击其中一个条目时,应始终以字符串作为参数调用相同的函数。经过一些尝试和研究,我想出了这样的事情:

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

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

4

2 回答 2

25

您遇到了通常被称为(可能不完全是迂腐正确的;-)作为 Python 中的“范围问题”——绑定是迟到的(在调用时进行词法查找),而您希望它早(在默认时间)。所以你现在有:

    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这里的那个)在默认时间被计算一次。添加一层函数嵌套(例如双 lambda)也可以,但在这里有点过头了!-)

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

于 2009-07-09T06:10:54.320 回答
3

这应该可行,但我很确定有一种更好的方法,我现在不记得了。

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)
于 2009-07-08T22:13:07.490 回答