2009-07-08 14 views
10

Ich habe eine Liste von Zeichenfolgen und möchte einen Menüeintrag für jede dieser Zeichenfolgen erstellen. Wenn der Benutzer auf einen der Einträge klickt, soll immer die gleiche Funktion mit der Zeichenkette als Argument aufgerufen werden. Nach einiger versuchen und Forschung kam ich mit so etwas wie dies oben:Erstellen PyQt-Menü aus einer Liste von Zeichenfolgen

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 des entsprechenden. Ich bin dankbar für irgendwelche Ideen, wie ich das richtig machen kann. Vielen Dank.

Antwort

23

Sie treffen, was oft zu (vielleicht nicht ganz ;-) pedantisch-korrekt als „Scoping-Problem“ in Python bezeichnet ist schon - die Bindung ist spät (lexikalische Nachschlagen bei Call-Zeit) während du es gerne früh haben würdest (zur def-time). Also, wo Sie jetzt haben:

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

versuchen statt:

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

Dieses „geht davon aus“ die Bindung, da die Standardwerte (als item man hier) auf einmal ein für alle berechnet bekommen def-Zeit . Hinzufügen einer Ebene der Funktions nisten (zB ein Doppel Lambda) funktioniert auch, aber es ist ein bisschen von einer hier übertrieben! -)

Sie alternativ functools.partial(self.doStuff, item) verwenden könnte (mit einem import functools an der Spitze natürlich), die eine andere feine Lösung , aber ich denke, ich würde für die einfachste (und am häufigsten) "falsche Standard-Wert für Argument" Idiom gehen.

+1

@Alex: +1 !!! Es nervt mich den ganzen Morgen! Lambda x = x: Funktion (x) FTW :) –

+0

Ausgezeichnete Antwort! Es funktioniert auch mit der neuen Verbindungssyntax: 'entry.trigger.connect (lambda item = item: self.doStuff (item))' – Leistungsabfall

2

Das sollte funktionieren, aber ich bin mir ziemlich sicher, dass es einen besseren Weg gab, an den ich mich gerade nicht erinnern kann.

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

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

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

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