Pregunta

Tengo una lista de cadenas y desea crear una entrada de menú para cada una de esas cadenas. Cuando el usuario hace clic en una de las entradas, siempre la misma función será llamada con la cadena como argumento. Después de algún tratando y la investigación me encontré con 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ú se imprimirá la misma salida: "Artículo 3" en lugar de la correspondiente. Estoy agradecido por alguna idea acerca de cómo puedo obtener este derecho. Gracias.

¿Fue útil?

Solución

Se está cumpliendo lo que se ha referido a menudo (tal vez no del todo correcta pedante-;-) como el "problema de alcance" en Python - la unión es tarde (de búsqueda léxica en llamadas en tiempo), mientras que le gustaría que temprano (a las def-tiempo). Entonces, ¿dónde ahora tiene:

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

probar su lugar:

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

Esta "anticipa" la unión, ya que los valores por defecto (como el item uno aquí) consiguen calculan una vez que una para todos en def-tiempo. La adición de un nivel de anidamiento función (por ejemplo, un doble lambda) funciona también, pero es un poco de una exageración aquí -!)

Se puede usar alternativamente functools.partial(self.doStuff, item) (con un import functools en la parte superior, por supuesto) que es otra buena solución, pero creo que me gustaría ir para el más simple (y más común) "falso default-valor para el argumento de la" expresión idiomática.

Otros consejos

Esto debería funcionar, pero estoy bastante seguro de que había una mejor manera de 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))

Editar : versión más corta, que todavía no es lo que estoy pensando ... o tal vez fue en otro idioma? :)

(lambda x: lambda self.do_stuff(x))(item)
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top