#python #qt #pyqt
#python #qt #pyqt
Вопрос:
По какой-то причине выполнение этого кода переключает только синий цвет, хотя предполагается, что он переключает каждый из цветов по отдельности.
Этот код является моей версией примера кода на http://eli.thegreenplace.net/2011/04/25/passing-extra-arguments-to-pyqt-slot , это учебное пособие по PyQt, которое я только начал изучать.
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_())
Ответ №1:
Причина, по которой connect(lambda: self.set_color(x))
это не работает, заключается в том, что x
будет вычисляться только при вызове лямбда-выражения, то есть при отправке сигнала, что будет намного позже, после завершения цикла. Таким образом, set_color()
будет получено значение x
во время отправки сигнала. В вашем коде это будет последнее значение, которое x
было в цикле, т.е. 2.
Хотя ответ @Hi верен, я нахожу решение Ачаяна (упомянутое в комментарии) более явным и работает просто отлично (вопреки некоторым комментариям — я проверил это с помощью кода):
for x in range(0, 3):
...
self.buttons[x].clicked[bool].connect(partial(self.set_color, x))
Причина, по которой это работает, заключается в том, что x
является аргументом вызова функции (функция, являющаяся functools.partial
), поэтому x
вычисляется немедленно. Функция, возвращаемая с помощью, partial(f, a)
when f
является функцией одного аргумента, является функцией, g()
которая не принимает аргументов и будет вызывать f(a)
.
Комментарии:
1. Просто для ясности: мои комментарии были не о
partial
, а о неправильном использованииlambda
в ответе, который теперь удален. Я указал правильное использование, которое заключается в следующем:connect(lambda checked, x=x: self.set_color(x))
. На самом деле это более явно, чемpartial
, потому что оно включаетchecked
аргумент для параметра, отправленногоclicked
сигналом. Но очевидно, что любое решение работает так же хорошо.