Вопрос

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

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

Это было полезно?

Решение

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

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

Другие советы

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

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)
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top