Создайте меню PyQt из списка строк

У меня есть список строк и вы хотите создать запись в меню для каждой из этих строк. Когда пользователь нажимает на одну из записей, всегда должна вызываться одна и та же функция со строкой в ​​качестве аргумента. После некоторых попыток и исследований я придумал что-то вроде этого:

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" вместо соответствующего. Я благодарен за любые идеи о том, как я могу получить это право. Спасибо.

Ответ 1

Вы встречаетесь с тем, что часто упоминалось (возможно, не полностью педантично-правильно;-) как "проблема определения области видимости" в 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 здесь) вычисляются один раз для всех во время def-time. Добавление одного уровня вложенности функций (например, двойной лямбда) тоже работает, но это немного перебор! -)

В качестве альтернативы вы можете использовать functools.partial(self.doStuff, item)import functools в верхней части курса), что является еще одним прекрасным решением, но я думаю, что я пошел бы на самый простой (и самый общий) "поддельный значение по умолчанию для аргумента" Идиома.

Ответ 2

Это должно работать, но я уверен, что был лучший способ, о котором я не могу сейчас вспомнить.

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

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

Edit: Короче говоря, это еще не то, о чем я думаю... или, может быть, это было на другом языке?:)

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