如何摆脱相互更新小部件的循环?

时间:2019-06-26 00:36:43

标签: python pyqt pyqt5

我正在用PyQt5编写一个小的GUI,它链接两个系列的值(我们分别称它们为a,b,c和alpha,beta)。所有数字都是通过QDoubleSpinBox小部件输入的。

这些数字可以通过两个函数相互计算:

alpha, beta = f1(a,b,c)
a,b,c = f2(alpha, beta)

问题在于,由于a,b,c的信号触发了修改alpha,beta的槽,反之亦然,因此,一旦我更改了某个东西的值,我就陷入了不断更新小部件的无限循环中,直到UI崩溃为止。在沿一个方向或另一个方向注释掉更新时,一切正常。

没有GUI布局的详细信息,这基本上就是我正在做的事情:

from PyQt5.QtWidgets import (QApplication, QWidget, QDoubleSpinBox)

class MainWindow(QWidget):
    def __init__(self):
        super().__init__()

        # initialize UI and widgets (not shown)
        self.initUI()

        # update alpha beta
        self.a.valueChanged.connect(self.update_alphabeta)
        self.b.valueChanged.connect(self.update_alphabeta)
        self.c.valueChanged.connect(self.update_alphabeta)

        # update abc
        self.alpha.valueChanged.connect(self.update_abc)
        self.beta.valueChanged.connect(self.update_abc)


        def update_alphabeta(self):
            alpha,beta = f1(self.a, self.b, self.c)
            self.alpha.setValue(alpha)
            self.beta.setValue(beta)

        def update_abc(self):
            a,b,c = f2(self.alpha, self.beta)
            self.a.setValue(a)
            self.b.setValue(b)
            self.c.setValue(c)

到目前为止,我找不到合适的解决方案。我尝试执行此操作的方式可能存在根本缺陷。

1 个答案:

答案 0 :(得分:1)

据我了解,您希望如果用户修改了“ a”,“ b”或“ c”,则只能使用f1修改“ alpha”和“ beta”,并更改“ alpha”和“ beta”它不会更改“ a”,“ b”和“ c”。对于带有f2的“ alpha”和“ beta”,情况相同。如果是这样,那么解决方案是阻止以编程方式更改的元素发出信号,因为这是使用blockSignals()进行无限递归的原因。

from PyQt5 import QtCore, QtGui, QtWidgets


def f1(a, b, c):
    return 1 / (1 + a ** 2 + b ** 2 + c ** 2) ** 0.5, a + b + c


def f2(alpha, beta):
    return (
        alpha + beta,
        1 / (alpha ** 2 + beta ** 2 + 1),
        (alpha ** 2 + beta ** 2) ** 0.5,
    )


class Widget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(Widget, self).__init__(parent)
        max_f = 1.7976931348623157e308
        min_f = -max_f
        self.m_a_dsp = QtWidgets.QDoubleSpinBox(
            value=0,
            valueChanged=self.update_alpha_beta,
            minimum=min_f,
            maximum=max_f,
        )
        self.m_b_dsp = QtWidgets.QDoubleSpinBox(
            value=0,
            valueChanged=self.update_alpha_beta,
            minimum=min_f,
            maximum=max_f,
        )
        self.m_c_dsp = QtWidgets.QDoubleSpinBox(
            value=0,
            valueChanged=self.update_alpha_beta,
            minimum=min_f,
            maximum=max_f,
        )
        self.m_alpha_dsp = QtWidgets.QDoubleSpinBox(
            value=0,
            valueChanged=self.update_a_b_c,
            minimum=min_f,
            maximum=max_f,
        )
        self.m_beta_dsp = QtWidgets.QDoubleSpinBox(
            value=0,
            valueChanged=self.update_a_b_c,
            minimum=min_f,
            maximum=max_f,
        )

        lay = QtWidgets.QGridLayout(self)
        lay.addWidget(self.m_a_dsp, 0, 0, 1, 2)
        lay.addWidget(self.m_b_dsp, 0, 2, 1, 2)
        lay.addWidget(self.m_c_dsp, 0, 4, 1, 2)
        lay.addWidget(self.m_alpha_dsp, 1, 0, 1, 3)
        lay.addWidget(self.m_beta_dsp, 1, 3, 1, 3)

    @QtCore.pyqtSlot()
    def update_alpha_beta(self):
        alpha, beta = f1(
            self.m_a_dsp.value(), self.m_b_dsp.value(), self.m_c_dsp.value()
        )
        for spinbox, value in zip(
            (self.m_alpha_dsp, self.m_beta_dsp), (alpha, beta)
        ):
            spinbox.blockSignals(True)
            spinbox.setValue(value)
            spinbox.blockSignals(False)

    @QtCore.pyqtSlot()
    def update_a_b_c(self):
        a, b, c = f2(self.m_alpha_dsp.value(), self.m_beta_dsp.value())

        for spinbox, value in zip(
            (self.m_a_dsp, self.m_b_dsp, self.m_c_dsp), (a, b, c)
        ):
            spinbox.blockSignals(True)
            spinbox.setValue(value)
            spinbox.blockSignals(False)


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)
    w = Widget()
    w.show()
    sys.exit(app.exec_())