Usando expresión lambda para conectar ranuras en pyqt

Resuelto zeus300 asked hace 8 años • 4 respuestas

Estoy intentando conectar ranuras con funciones lambda, pero no funciona como esperaba. En el código siguiente, logro conectar los dos primeros botones correctamente. Para los dos segundos, que conecto en un bucle, esto sale mal. Alguien antes que yo tuvo la misma pregunta ( Qt - Conectar ranura con argumento usando lambda ), pero esta solución no me funciona. He estado mirando mi pantalla durante media hora, pero no puedo entender en qué se diferencia mi código.

class MainWindow(QtGui.QWidget):
    def __init__(self):
        super(QtGui.QWidget, self).__init__()

        main_layout = QtGui.QVBoxLayout(self)

        # Works:
        self.button_1 = QtGui.QPushButton('Button 1 manual', self)
        self.button_2 = QtGui.QPushButton('Button 2 manual', self)
        main_layout.addWidget(self.button_1)
        main_layout.addWidget(self.button_2)

        self.button_1.clicked.connect(lambda x:self.button_pushed(1))
        self.button_2.clicked.connect(lambda x:self.button_pushed(2))

        # Doesn't work:
        self.buttons = []
        for idx in [3, 4]:
            button = QtGui.QPushButton('Button {} auto'.format(idx), self)
            button.clicked.connect(lambda x=idx: self.button_pushed(x))
            self.buttons.append(button)
            main_layout.addWidget(button)


    def button_pushed(self, num):
        print 'Pushed button {}'.format(num)

Al presionar los dos primeros botones se obtiene "Botón presionado 1" y "Botón presionado 2", los otros dos producen "Botón presionado Falso", aunque esperaba 3 y 4.

Tampoco he entendido completamente el mecanismo lambda. ¿Qué se conecta exactamente? ¿Un puntero a una función generada por lambda (con el parámetro sustituido) o se evalúa la función lambda cada vez que se activa la señal?

zeus300 avatar Mar 06 '16 03:03 zeus300
Aceptado

La QPushButton.clickedseñal emite un argumento que indica el estado del botón. Cuando se conecta a su ranura lambda, el idxestado del botón sobrescribe el argumento opcional que asigna.

En su lugar, haga su conexión como

button.clicked.connect(lambda state, x=idx: self.button_pushed(x))

De esta manera se ignora el estado del botón y se pasa el valor correcto a su método.

three_pineapples avatar Mar 05 '2016 23:03 three_pineapples

¡Tener cuidado! Tan pronto como conecte su señal a una ranura lambda con una referencia a usted mismo, ¡su widget no será recolectado como basura! Esto se debe a que lambda crea un cierre con otra referencia incobrable al widget.

Por lo tanto, self.someUIwidget.someSignal.connect(lambda p: self.someMethod(p))es muy malo :)

Grigory Makeev avatar Jan 29 '2018 12:01 Grigory Makeev