¿Por qué este código solo cambia a azul? (Python, PyQt)

Por alguna razón, al ejecutar este código solo se alterna el color azul, cuando se supone que debe alternar cada uno de los colores individualmente.

Este código es mi versión del código de ejemplo en http://eli.thegreenplace.net/2011/04/25/passing-extra-arguments-to-pyqt-slot , que es un tutorial para PyQt, que acabo de comenzar aprendizaje.

import sys from PyQt5.QtWidgets import (QWidget, QPushButton, QFrame, QApplication) from PyQt5.QtGui import QColor class Example(QWidget): red = False blue = False green = False buttons = [] def __init__(self): super().__init__() self.init_UI() def init_UI(self): self.col = QColor(0, 0, 0) for x in range(0, 3): self.buttons.append(QPushButton('Red' if x == 0 else ('Green' if x == 1 else 'Blue'), self)) self.buttons[x].setCheckable(True) self.buttons[x].move(10, 10 + 50 * x) self.buttons[x].clicked[bool].connect(lambda: self.set_color(x)) self.square = QFrame(self) self.square.setGeometry(150, 20, 100, 100) self.square.setStyleSheet("QWidget { background-color: %s }" % self.col.name()) self.setGeometry(300, 300, 280, 170) self.setWindowTitle('Toggle button') self.show() def set_color(self, button): if button == 0: if self.red == False: self.red = True self.col.setRed(255) else: self.red = False self.col.setRed(0) elif button == 1: if self.green == False: self.green = True self.col.setGreen(255) else: self.green = False self.col.setGreen(0) else: if self.blue == False: self.blue = True self.col.setBlue(255) else: self.blue = False self.col.setBlue(0) self.square.setStyleSheet("QFrame { background-color: %s }" % self.col.name()) print(self.col.name()) if __name__ == '__main__': app = QApplication(sys.argv) ex = Example() sys.exit(app.exec_()) 

La razón por la que connect(lambda: self.set_color(x)) no funciona es que x se evaluará solo cuando se llame a la lambda, es decir, cuando se emite la señal, que será mucho más tarde, una vez que se haya completado el bucle. Entonces set_color() recibirá el valor de x en el momento en que se emite la señal. En su código, este va a ser el último valor que x tenía en el bucle, es decir, 2.

Aunque la respuesta de @Hi es válida, encuentro que la solución de Achayan (mencionada en un comentario) es más explícita y funciona bien (al contrario de algunos comentarios, la verifiqué con el código):

 for x in range(0, 3): ... self.buttons[x].clicked[bool].connect(partial(self.set_color, x)) 

La razón por la que funciona es que x es un argumento para una llamada de función (la función es functools.partial ), por lo que x se evalúa de inmediato . La función devuelta por partial(f, a) cuando f es una función de un arg es una función g() que no toma argumentos y llamará f(a) .