从字符串列表创建PyQt菜单

12 投票
2 回答
6484 浏览
提问于 2025-04-15 12:46

我有一个字符串列表,想为每个字符串创建一个菜单项。当用户点击其中一个菜单项时,应该始终调用同一个函数,并把这个字符串作为参数传递过去。经过一些尝试和研究,我得到了这样的代码:

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”,而不是对应的字符串。我很感谢任何关于如何解决这个问题的想法。谢谢。

2 个回答

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)
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),这也是一个不错的解决方案。不过我觉得用最简单、最常见的“伪默认值”方法会更好。

撰写回答