12

文字列のリストがあり、それらの文字列ごとにメニュー エントリを作成したいと考えています。ユーザーがエントリの 1 つをクリックすると、常に同じ関数が文字列を引数として呼び出されます。いくつかの試行錯誤の後、私は次のようなものを思いつきました:

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」です。これを正しく行う方法についてのアイデアに感謝します。ありがとう。

4

2 に答える 2

25

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で一度だけ計算されるため、これはバインディングを「予測」します。1レベルの関数ネスト(たとえば、ダブルラムダ)を追加することも機能しますが、ここでは少しやり過ぎです!-)

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 に答える