Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
643 views
in Technique[技术] by (71.8m points)

qt - How come this code only toggles blue? (Python, PyQt)

For some reason running this code only toggles the color blue, when its supposed to toggle each one of the colors individually.

This code is my version of the example code at http://eli.thegreenplace.net/2011/04/25/passing-extra-arguments-to-pyqt-slot , which is a tutorial for PyQt, which I have just started learning.

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_())
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

The reason that connect(lambda: self.set_color(x)) does not work is that x will be evaluated only when the lambda is called, i.e. when the signal is emitted, which will be much later, after the loop has completed. So set_color() will receive the value of x at the time the signal is emitted. In your code, this is going to be the last value that x had in the loop, i.e. 2.

Although @Hi's answer is valid, I find Achayan's solution (mentioned in a comment) more explicit and works just fine (contrary to some comments -- I verified it with code):

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

The reason this works is that x is an argument to a function call (the function being functools.partial), so x is evaluated immediately. The function returned by partial(f, a) when f is a function of one arg is a function g() that takes no arguments and will call f(a).


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...