Creare menu di PyQt da una lista di stringhe
-
11-09-2019 - |
Domanda
Ho una lista di stringhe e voglio creare una voce di menu per ciascuna di queste stringhe. Quando l'utente clicca su una delle voci, sempre la stessa funzione sarà chiamata con la stringa come argomento. Dopo qualche tentativo e di ricerca mi si avvicinò con qualcosa di simile:
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_())
Ora il problema è che ciascuno degli elementi del menu stamperà la stessa uscita: "Articolo 3" invece di quello corrispondente. Sono grato per tutte le idee su come posso ottenere questo diritto. Grazie.
Soluzione
Si sta incontrando ciò che è stato spesso definito (forse non del tutto pedantemente-correttamente ;-) come il "problema scoping" in Python - l'associazione è in ritardo (di ricerca lessicale al call-tempo), mentre ti piacerebbe presto (alle DEF-time). Allora, dove ora avete:
for item in menuitems:
entry = menu.addAction(item)
self.connect(entry,QtCore.SIGNAL('triggered()'), lambda: self.doStuff(item))
provare invece:
for item in menuitems:
entry = menu.addAction(item)
self.connect(entry,QtCore.SIGNAL('triggered()'), lambda item=item: self.doStuff(item))
Questa "anticipa" il legame, in quanto i valori di default (come il item
uno qui) vengono calcolati una volta per tutte al DEF-tempo. L'aggiunta di un livello di funzione di nidificazione (ad esempio un doppio lambda) funziona anche, ma è un po 'di un peso inutile qui -!)
Si potrebbe in alternativa utilizzare functools.partial(self.doStuff, item)
(con una import functools
nella parte superiore del corso), che è un altro bel soluzione, ma penso che mi piacerebbe andare per il più semplice (e più comune) "default-valore falso per argomento" idioma.
Altri suggerimenti
Questo dovrebbe funzionare, ma sono abbastanza sicuro che ci fosse un modo migliore che non riesco a ricordare in questo momento.
def do_stuff_caller(self, item):
return lambda: self.doStuff(item)
...
self.connect(entry, QtCore.SIGNAL('triggered()'), self.do_stuff_caller(item))
Modifica : versione più breve, che ancora non è quello che sto pensando ... o forse era in un'altra lingua? :)
(lambda x: lambda self.do_stuff(x))(item)