如何使用PyQt创建相互依赖的小部件



我正在尝试实现两个PyQt小部件,它们都会影响颜色值。如果用户更改了其中一个,则应调整另一个,反之亦然。到目前为止,我所追求的实现使用了一种方法,该方法从任何一个小部件接收信号,并相应地更改小部件的数据。

我自己不是一名软件工程师,我的印象是这种策略会导致某种循环,导致不希望的行为。也许存在某种设计模式来应对这种情况?

我确实意识到这个问题可能有点不合时宜,所以我希望能得到反馈。

以下是代码中似乎与问题相关的部分:

class Gui(QWidget):
def __init__(self, parent=None):
super(Gui, self).__init__(parent)
self.seed_color_line_edit = QLineEdit(placeholderText='Enter a hex color code...')
self.hue_slider = QLabeledSlider(Qt.Horizontal)
...
self.seed_color_line_edit.editingFinished.connect(self.set_color)
self.hue_slider.valueChanged.connect(self.set_color)
...
@pyqtSlot()
def set_color(self):
my_sender = self.sender()
if type(my_sender) is QLabeledSlider:
c = (self.hue_slider.value() / 360, 1, 1)
hex_color = rgb2hex(c)  # (hue, sat, val))
self.seed_color_line_edit.setText(hex_color)
if type(my_sender) is QLineEdit:
hex_color = self.seed_color_line_edit.text()
hsv = rgb_to_hsv(hex2rgb(hex_color))
hue, sat, val = int(hsv[0] * 360), int(hsv[1] * 100), int(hsv[2] * 100)
self.hue_slider.setValue(hue)

上面的代码没有产生所需的行为。如果移动了色调QLabeledSlider,则QLineEdit小部件中的十六进制颜色代码将正确更改。但是,如果更改了十六进制颜色QLineEdit,则会忽略此更改,或者将QLineEdit数据更改为其他值。

由于颜色值在3D空间中表示,例如HSV,因此需要3个输入来定义颜色。当使用十六进制颜色代码RGB表示时,用户对此十六进制代码的更改应更新h、s和v值。在简单的PyQt解决方案中,HSV值包含在3个QSlider对象中,十六进制颜色值包含在QLineEdit对象中。现在,使用各种对象之间的信号,可能会出现这样的情况:十六进制代码的更改首先触发h值更新,然后触发s和v值更新。但是,当h值首先更新时,这会导致信号返回到十六进制颜色的QLineEdit对象,但使用以前的s和v值。因此,十六进制颜色值也会更新,但其值与用户输入的值不同。

下面给出了一个显示这种行为的最小示例。这里,2个QSlider对象的值通过一些公式(旨在模拟十六进制到hsv或hsv到十六进制的转换(来确定QLineEdit对象中的值。如果QLineEdit值发生更改,则会依次生成两个到QSlider对象的信号,这两个信号又会生成到QLineEdit对象的信号。

现在,我找到的解决方法只是在更新滑块值集时阻止任何传出信号。

from PyQt5.QtWidgets import QWidget, QApplication, QVBoxLayout, QLineEdit, QSlider
from PyQt5.QtCore import Qt, pyqtSlot

class Gui(QWidget):
def __init__(self, parent=None):
super(Gui, self).__init__(parent)
self.line_edit = QLineEdit()
self.slider1 = QSlider(Qt.Horizontal)
self.slider2 = QSlider(Qt.Horizontal)
self.line_edit.editingFinished.connect(self.setter)
self.slider1.valueChanged.connect(self.setter)
self.slider2.valueChanged.connect(self.setter)
layout = QVBoxLayout()
layout.addWidget(self.line_edit)
layout.addWidget(self.slider1)
layout.addWidget(self.slider2)
self.setLayout(layout)
@pyqtSlot()
def setter(self):
my_sender = self.sender()
if type(my_sender) is QSlider:
a = self.slider1.value()
b = self.slider2.value()
print(f'setter QSlider {a} {b}')
self.line_edit.setText(str(a+b))
if type(my_sender) is QLineEdit:
self.slider1.blockSignals(True)
self.slider2.blockSignals(True)
c = int(self.line_edit.text())
a = c // 2
b = c - a
print(f'setter QLineEdit {a} {b} {c}')
self.slider1.setValue(a)
self.slider2.setValue(b)
self.slider1.blockSignals(False)
self.slider2.blockSignals(False)

if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
mw = Gui()
mw.show()
app.exec_()

最新更新