Python PyQt:计算过长时发出两次信号

Python PyQt:计算过长时发出两次信号,python,pyqt,pyqt5,signals-slots,qspinbox,Python,Pyqt,Pyqt5,Signals Slots,Qspinbox,我使用两个小部件:一个QSpinBox和一个QLineEditvalueChanged小部件的QSpinBox插槽连接到update功能。此函数由耗时的处理(带计算的循环或time.sleep()调用)和QLineEdit.setText()调用组成。一开始,我以为它能像预期的那样工作,但我注意到,当计算花费很长时间时,信号似乎会发出两次 下面是代码: import time from PyQt5.QtWidgets import QWidget, QSpinBox, QVBoxLayout,

我使用两个小部件:一个
QSpinBox
和一个
QLineEdit
valueChanged
小部件的
QSpinBox
插槽连接到
update
功能。此函数由耗时的处理(带计算的循环或
time.sleep()
调用)和
QLineEdit.setText()
调用组成。一开始,我以为它能像预期的那样工作,但我注意到,当计算花费很长时间时,信号似乎会发出两次

下面是代码:

import time

from PyQt5.QtWidgets import QWidget, QSpinBox, QVBoxLayout, QLineEdit


class Window(QWidget):
    def __init__(self):
        # parent constructor
        super().__init__()

        # widgets
        self.spin_box = QSpinBox()
        self.line_edit = QLineEdit()

        # layout
        v_layout = QVBoxLayout()
        v_layout.addWidget(self.spin_box)
        v_layout.addWidget(self.line_edit)

        # signals-slot connections
        self.spin_box.valueChanged.connect(self.update)

        #
        self.setLayout(v_layout)
        self.show()

    def update(self, param_value):
        print('update')
        # time-consuming part
        time.sleep(0.5) # -> double increment
        #time.sleep(0.4) # -> works normally!
        self.line_edit.setText(str(param_value))


if __name__ == '__main__':
    from PyQt5.QtWidgets import QApplication
    import sys

    app = QApplication(sys.argv)
    win = Window()
    sys.exit(app.exec_())
更新的另一个版本

# alternative version, calculations in a loop instead of time.sleep()
# -> same behaviour
def update2(self, param_value):
    print('update2')
    for i in range(2000000): # -> double increment
        x = i**0.5 * i**0.2
    #for i in range(200000): # -> works normally!
    #    x = i**0.5 * i**0.2
    self.line_edit.setText(str(param_value))

这里没有真正的神秘。如果单击旋转框按钮,该值将增加一步。但如果您按住按钮,它将不断增加值。为了区分点击和按住之间的区别,使用了计时器。据推测,阈值大约为半秒。因此,如果插入一个小的额外延迟,单击可能会被解释为短按/保持,因此旋转框将增加两步而不是一步

更新

解决此行为的一种方法是在工作线程中进行处理,以消除延迟。这样做的主要问题是避免了旋转框值更改和行编辑更新之间的太多延迟。如果按住旋转框按钮,大量信号事件可能会由工作线程排队。一种过于简单的方法是,在处理所有排队的信号之前,等待自旋盒按钮被释放,但这会导致长时间的延迟,而每个值都是单独处理的。更好的方法是压缩事件,以便只处理最近的信号。这仍然有点滞后,但如果处理时间不太长,应该会导致可接受的行为

下面是实现此方法的演示:

import sys, time
from PyQt5.QtWidgets import (
    QApplication, QWidget, QSpinBox, QVBoxLayout, QLineEdit,
    )
from PyQt5.QtCore import (
    pyqtSignal, pyqtSlot, Qt, QObject, QThread, QMetaObject,
    )

class Worker(QObject):
    valueUpdated = pyqtSignal(int)

    def __init__(self, func):
        super().__init__()
        self._value = None
        self._invoked = False
        self._func = func

    @pyqtSlot(int)
    def handleValueChanged(self, value):
        self._value = value
        if not self._invoked:
            self._invoked = True
            QMetaObject.invokeMethod(self, '_process', Qt.QueuedConnection)
            print('invoked')
        else:
            print('received:', value)

    @pyqtSlot()
    def _process(self):
        self._invoked = False
        self.valueUpdated.emit(self._func(self._value))

class Window(QWidget):
    def __init__(self):
        super().__init__()
        self.spin_box = QSpinBox()
        self.line_edit = QLineEdit()
        v_layout = QVBoxLayout()
        v_layout.addWidget(self.spin_box)
        v_layout.addWidget(self.line_edit)
        self.setLayout(v_layout)
        self.thread = QThread(self)
        self.worker = Worker(self.process)
        self.worker.moveToThread(self.thread)
        self.worker.valueUpdated.connect(self.update)
        self.spin_box.valueChanged.connect(self.worker.handleValueChanged)
        self.thread.start()
        self.show()

    def process(self, value):
        time.sleep(0.5)
        return value

    def update(self, param_value):
        self.line_edit.setText(str(param_value))

if __name__ == '__main__':

    app = QApplication(sys.argv)
    win = Window()
    sys.exit(app.exec_())

我用同样的结果尝试了同样的代码,参数值的递增?我勒个去。。。我真的很想看到答案:)这特别烦人,因为如果你只是减少范围参数,它就会工作。。。去掉一个零,它就工作了。我使用了
time。sleep
而不是
time。sleep(0.4)
它工作了
time。sleep(0.5)
越多,它就不会得到同样的结果。我从来没有听说过这样的限制,我一定是做错了什么。。。