Menu Créer PyQt à partir d'une liste de chaînes
-
11-09-2019 - |
Question
J'ai une liste de chaînes et que vous souhaitez créer une entrée de menu pour chacune de ces chaînes. Lorsque l'utilisateur clique sur l'une des entrées, toujours la même fonction doit être appelée avec la chaîne comme argument. Après quelques recherches et d'essayer, je suis venu avec quelque chose comme ceci:
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_())
Maintenant, le problème est que chacun des éléments du menu imprimer la même sortie: « Article 3 » au lieu de celui correspondant. Je suis reconnaissant pour toutes les idées sur la façon dont je peux obtenir ce droit. Merci.
La solution
Vous vous réunissez ce qui a été souvent appelé (peut-être pas tout à fait pédante-correctement ;-) comme le « problème de cadrage » en Python - la liaison est en retard (recherche lexicale à l'appel-temps) pendant que vous souhaitez qu'il début (à temps def). Alors, où vous avez maintenant:
for item in menuitems:
entry = menu.addAction(item)
self.connect(entry,QtCore.SIGNAL('triggered()'), lambda: self.doStuff(item))
essayer la place:
for item in menuitems:
entry = menu.addAction(item)
self.connect(entry,QtCore.SIGNAL('triggered()'), lambda item=item: self.doStuff(item))
« prévoit » la liaison, car les valeurs par défaut (comme le item
un ici) se calcule une fois que pour tous à temps def. Ajout d'un niveau de fonction d'imbrication (par exemple une double lambda) fonctionne aussi, mais il est un peu un surpuissant ici -)
Vous pouvez également utiliser functools.partial(self.doStuff, item)
(avec un import functools
en haut bien sûr) qui est une autre solution bien, mais je pense que je vais le plus simple (et la plus courante) « faux-valeur par défaut pour argument » langage.
Autres conseils
Cela devrait fonctionner, mais je suis sûr qu'il y avait une meilleure façon que je ne me souviens pas en ce moment.
def do_stuff_caller(self, item):
return lambda: self.doStuff(item)
...
self.connect(entry, QtCore.SIGNAL('triggered()'), self.do_stuff_caller(item))
Modifier : version plus courte, qui est toujours pas ce que je pense à ... ou peut-être il était dans une autre langue? :)
(lambda x: lambda self.do_stuff(x))(item)