Crear menú PyQt a partir de una lista de cadenas

Tengo una lista de cadenas y quiero crear una entrada de menú para cada una de esas cadenas. Cuando el usuario hace clic en una de las entradas, siempre se llamará a la misma función con la cadena como argumento. Después de algunos bashs e investigaciones se me ocurrió algo como esto:

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

Ahora el problema es que cada uno de los elementos del menú imprimirá el mismo resultado: “Elemento 3” en lugar del correspondiente. Estoy agradecido por cualquier idea sobre cómo puedo hacer esto bien. Gracias.

Está cumpliendo con lo que se ha referido a menudo (tal vez no completamente de forma pediátrica 😉 como el “problema de scope” en Python – la vinculación es tardía (búsqueda léxica en el momento de la llamada) mientras que le gustaría hacerlo pronto (en def-time). Entonces, donde ahora tienes:

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

intente en su lugar:

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

Esto “anticipa” el enlace, ya que los valores predeterminados (como el item uno aquí) se computan una vez y para siempre en la definición. Agregar un nivel de anidamiento de funciones (por ejemplo, una doble lambda) también funciona, ¡pero es un poco excesivo aquí! -)

Alternativamente, podría usar functools.partial(self.doStuff, item) (con una función de import functools en la parte superior, por supuesto) que es otra solución fina, pero creo que iría por el más simple (y más común) “falso por defecto- valor para el argumento “idioma.

Esto debería funcionar, pero estoy bastante seguro de que había una mejor manera que no puedo recordar en este momento.

 def do_stuff_caller(self, item): return lambda: self.doStuff(item) ... self.connect(entry, QtCore.SIGNAL('triggered()'), self.do_stuff_caller(item)) 

Edición : versión más corta, ¿eso no es lo que estoy pensando … o quizás fue en otro idioma? 🙂

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