Frage

Ich habe eine Liste von Strings und will für jede dieses Strings einen Menüeintrag erstellen. Wenn der Benutzer auf einen der Einträge klickt, immer die gleiche Funktion wird mit der Zeichenfolge als Argument aufgerufen werden. Nach einiger versuchen und Forschung kam ich mit so etwas wie dies oben:

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

Das Problem ist jetzt, dass jeder der Menüpunkte wird die gleiche Ausgabe drucken: „Punkt 3“ anstelle der entsprechenden. Ich bin dankbar für alle Ideen, wie ich dieses Recht bekommen. Danke.

War es hilfreich?

Lösung

Sie treffen, was oft vielleicht bezeichnet ist schon auf (nicht ganz pedantisch-richtig ;-) als das „Scoping-Problem“ in Python - die Bindung ist spät (lexikalische Nachschlagen bei Call-Zeit), während Sie es möchten früh (bei def-Zeit). Also, wo Sie jetzt haben:

    for item in menuitems:
        entry = menu.addAction(item)
        self.connect(entry,QtCore.SIGNAL('triggered()'), lambda: self.doStuff(item))

versuchen Sie stattdessen:

    for item in menuitems:
        entry = menu.addAction(item)
        self.connect(entry,QtCore.SIGNAL('triggered()'), lambda item=item: self.doStuff(item))

Diese „geht davon aus“ die Bindung, da die Standardwerte (als item man hier) ein für allemal zu def Zeit berechnet bekommen. Hinzufügen einer Ebene der Funktions nisten (zum Beispiel ein Doppel lambda) funktioniert auch, aber es ist ein bisschen zuviel des Guten hier! -)

Sie könnten alternativ functools.partial(self.doStuff, item) verwenden (mit einem import functools an der Spitze natürlich), die eine weitere feine Lösung ist, aber ich glaube, ich für die einfachste gehen würde (und am häufigsten) „fake default-Wert für Argument“ Idiom.

Andere Tipps

Das sollte funktionieren, aber ich bin mir ziemlich sicher, dass ein besserer Weg, es war, dass ich kann jetzt nicht wieder zu verwenden.

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

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

Bearbeiten : Kürzere Version, die noch nicht das, was über ich denke ... oder vielleicht war es auch in einer anderen Sprache? :)

(lambda x: lambda self.do_stuff(x))(item)
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top