Conexión de múltiples señales / ranura en un bucle for en pyqt

Estoy conectando múltiples señales / ranuras usando un for loop en PyQt. El código es abajo:

 # Connect Scan Callbacks for button in ['phase', 'etalon', 'mirror', 'gain']: getattr(self.ui, '{}_scan_button' .format(button)).clicked.connect( lambda: self.scan_callback(button)) 

Lo que espero:

  • Botón de conexión phase_scan_button hizo clic en la signal a la slot scan_callback y envía la phase cadena como parámetro a la slot . Lo mismo para etalon , mirror y gain .

Lo que estoy recibiendo:

  • Por alguna razón, mis funciones siempre pasan la gain cadena como parámetro para todos los botones. No estoy seguro si estoy siendo estúpido (probable) o si es un error.

Para referencia, el método de slot :

 def scan_callback(self, scan): print(scan) # Here I always get 'gain' if self.scanner.isWorking: self.scanner.isWorking = False self.scan_thread.terminate() self.scan_thread.wait() else: self.scanner.isWorking = True self.scan_thread.start() getattr(self.ui, '{}_scan_button' .format( scan)).setText('Stop Scan') getattr(self, '_signal{}Scan' .format(scan)).emit() 

Mi forma preferida de iterar sobre varios widgets en pyqt es almacenarlos como objetos en listas.

 myButtons = [self.ui.phase_scan_button, self.ui.etalon_scan_button, self.ui.mirror_scan_button, self.ui.gain_scan_button] for button in myButtons: button.clicked.connect(lambda _, b=button: self.scan_callback(scan=b)) 

Si necesita las cadenas “fase”, “etalon”, “espejo” y “ganancia” por separado, puede almacenarlas en otra lista o crear un diccionario como

 myButtons_dict = {"phase": self.ui.phase_scan_button, "etalon": self.ui.etalon_scan_button, "mirror": self.ui.mirror_scan_button, "gain": self.ui.gain_scan_button} for button in myButtons_dict: myButtons_dict[button].clicked.connect(lambda: _, b=button self.scan_callback(scan=b)) 

Tenga en cuenta cómo uso la expresión lambda con variables sólidas que luego se pasan a la función self.scan_callback . De esta manera, el valor del button se almacena para siempre.

Sus lambdas no almacenan el valor del button cuando está definido. El código que describe la función lambda se analiza y comstack, pero no se ejecuta hasta que realmente se llama lambda . Cuando se hace clic en cualquiera de los botones, se usa el valor actual del button variable . Al final del bucle, el button contiene "gain" y esto provoca el comportamiento que se ve.

Prueba esto:

 funcs = [] for button in ['phase', 'etalon', 'mirror', 'gain']: funcs.append( lambda : print(button)) for fn in funcs: fn() 

La salida es:

 gain gain gain gain 

Extendiendo el ejemplo, como prueba de que las lambdas no almacenan el valor del button tenga en cuenta que si el button deja de existir, tendrá un error:

 del button for fn in funcs: fn() 

que tiene salida

 funcs.append( lambda : print(button)) NameError: name 'button' is not defined