我使用了两个小部件: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_())
update
的另一个版本:
# 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))
答案 0 :(得分:2)
这里没有真正的谜。如果单击一个旋转框按钮,该值将增加一步。但是,如果按住按钮,它将持续增加值。为了区分点击和按下/保持之间的区别,使用计时器。据推测,门槛大约是半秒钟。因此,如果您插入一个小的额外延迟,点击可能会被解释为短按/按住,因此旋转框将增加两步而不是一步。
<强>更新强>:
解决此问题的一种方法是在工作线程中执行处理,以便消除延迟。这个问题的主要问题是避免了旋转框值更改和行编辑更新之间的过多延迟。如果按住旋转框按钮,工作线程可能会排队大量信号事件。在处理所有排队信号之前,一个简单的方法会等到旋转框按钮被释放 - 但这会导致长时间的延迟,而每个值都是单独处理的。更好的方法是压缩事件,以便只处理最新的信号。这仍然是有点滞后,但如果处理时间不是太长,它应该导致可接受的行为。
以下是实现此方法的演示:
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_())