我想在一个QThread中改变一个QTimer的间隔时间。这是我的代码。
import sys
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5.QtCore import QObject, QTimer, QThread
class Worker(QObject):
def __init__(self):
QObject.__init__(self)
self.timer = QTimer(self)
self.timer.timeout.connect(self.work)
def start(self):
self.timer.start(1000)
def work(self):
print("Hello World...")
def set_interval(self, interval):
self.timer.setInterval(interval)
def main():
# Set up main window
app = QApplication(sys.argv)
win = QMainWindow()
win.setFixedSize(200, 100)
spinbox_interval = QtWidgets.QSpinBox(win)
spinbox_interval.setMaximum(5000)
spinbox_interval.setSingleStep(500)
spinbox_interval.setValue(1000)
worker = Worker()
thread = QThread()
worker.moveToThread(thread)
thread.started.connect(worker.start)
thread.start()
def change_interval():
value = spinbox_interval.value()
worker.set_interval(value)
spinbox_interval.valueChanged.connect(change_interval)
win.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
如果我在启动定时器后调用worker.setInterval(),超时信号不再发出信号。谁能给我解释一下我做错了什么?
要了解这个问题,你必须在控制台CMD中运行以获得错误信息,从而了解原因,如果你这样做,你会得到以下错误信息。
QObject::killTimer: Timers cannot be stopped from another thread
QObject::startTimer: Timers cannot be started from another thread
要理解这个错误信息,你必须知道:
所以作为Worker的子代的定时器就和它的父代一样生活在二级线程中,因此你不能从其他线程中修改它。在这种情况下,就是要在二级线程中发送修改信息,为此有几种选择。
QMetaObject.invokeMethod()
用 pyqtSlot:
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QSpinBox
from PyQt5.QtCore import pyqtSlot, QMetaObject, QObject, Qt, QTimer, QThread, Q_ARG
class Worker(QObject):
def __init__(self):
QObject.__init__(self)
self.timer = QTimer(self)
self.timer.timeout.connect(self.work)
def start(self):
self.timer.start(1000)
def work(self):
print("Hello World...")
@pyqtSlot(int)
def set_interval(self, interval):
self.timer.setInterval(interval)
def main():
# Set up main window
app = QApplication(sys.argv)
win = QMainWindow()
win.setFixedSize(200, 100)
spinbox_interval = QSpinBox(win)
spinbox_interval.setMaximum(5000)
spinbox_interval.setSingleStep(500)
spinbox_interval.setValue(1000)
worker = Worker()
thread = QThread()
worker.moveToThread(thread)
thread.started.connect(worker.start)
thread.start()
def change_interval():
value = spinbox_interval.value()
QMetaObject.invokeMethod(
worker, "set_interval", Qt.QueuedConnection, Q_ARG(int, value)
)
spinbox_interval.valueChanged.connect(change_interval)
win.show()
ret = app.exec_()
QMetaObject.invokeMethod(worker.timer, "stop")
thread.quit()
thread.wait()
sys.exit(ret)
if __name__ == "__main__":
main()
自定义信号与槽。
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QSpinBox
from PyQt5.QtCore import pyqtSignal, pyqtSlot, QMetaObject, QObject, Qt, QTimer, QThread
class Worker(QObject):
updateInterval = pyqtSignal(int)
def __init__(self):
QObject.__init__(self)
self.timer = QTimer(self)
self.timer.timeout.connect(self.work)
self.updateInterval.connect(self.set_interval)
def start(self):
self.timer.start(1000)
def work(self):
print("Hello World...")
@pyqtSlot(int)
def set_interval(self, interval):
self.timer.setInterval(interval)
def main():
# Set up main window
app = QApplication(sys.argv)
win = QMainWindow()
win.setFixedSize(200, 100)
spinbox_interval = QSpinBox(win)
spinbox_interval.setMaximum(5000)
spinbox_interval.setSingleStep(500)
spinbox_interval.setValue(1000)
worker = Worker()
thread = QThread()
worker.moveToThread(thread)
thread.started.connect(worker.start)
thread.start()
def change_interval():
value = spinbox_interval.value()
worker.updateInterval.emit(value)
spinbox_interval.valueChanged.connect(change_interval)
win.show()
ret = app.exec_()
QMetaObject.invokeMethod(worker.timer, "stop")
thread.quit()
thread.wait()
sys.exit(ret)
if __name__ == "__main__":
main()
自定义QEvent。
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QSpinBox
from PyQt5.QtCore import QEvent, QMetaObject, QObject, Qt, QTimer, QThread
class IntervalEvent(QEvent):
def __init__(self, interval):
QEvent.__init__(self, QEvent.User + 1000)
self._interval = interval
@property
def interval(self):
return self._interval
class Worker(QObject):
def __init__(self):
QObject.__init__(self)
self.timer = QTimer(self)
self.timer.timeout.connect(self.work)
def start(self):
self.timer.start(1000)
def work(self):
print("Hello World...")
def set_interval(self, interval):
self.timer.setInterval(interval)
def event(self, e):
if isinstance(e, IntervalEvent):
self.set_interval(e.interval)
return Worker.event(self, e)
def main():
# Set up main window
app = QApplication(sys.argv)
win = QMainWindow()
win.setFixedSize(200, 100)
spinbox_interval = QSpinBox(win)
spinbox_interval.setMaximum(5000)
spinbox_interval.setSingleStep(500)
spinbox_interval.setValue(1000)
worker = Worker()
thread = QThread()
worker.moveToThread(thread)
thread.started.connect(worker.start)
thread.start()
def change_interval():
value = spinbox_interval.value()
event = IntervalEvent(value)
QApplication.postEvent(worker, event)
spinbox_interval.valueChanged.connect(change_interval)
win.show()
ret = app.exec_()
QMetaObject.invokeMethod(worker.timer, "stop")
thread.quit()
thread.wait()
sys.exit(ret)
if __name__ == "__main__":
main()
QTimer.singleShot()
用 functools.partial()
(只适用于PyQt5,不适用于PySide2)
import sys
from functools import partial
from PyQt5.QtWidgets import QApplication, QMainWindow, QSpinBox
from PyQt5.QtCore import QMetaObject, QObject, Qt, QTimer, QThread
class Worker(QObject):
def __init__(self):
QObject.__init__(self)
self.timer = QTimer(self)
self.timer.timeout.connect(self.work)
def start(self):
self.timer.start(1000)
def work(self):
print("Hello World...")
def set_interval(self, interval):
print(interval)
self.timer.setInterval(interval)
def main():
# Set up main window
app = QApplication(sys.argv)
win = QMainWindow()
win.setFixedSize(200, 100)
spinbox_interval = QSpinBox(win)
spinbox_interval.setMaximum(5000)
spinbox_interval.setSingleStep(500)
spinbox_interval.setValue(1000)
worker = Worker()
thread = QThread()
worker.moveToThread(thread)
thread.started.connect(worker.start)
thread.start()
def change_interval():
value = spinbox_interval.value()
wrapper = partial(worker.set_interval, value)
QTimer.singleShot(0, wrapper)
spinbox_interval.valueChanged.connect(change_interval)
win.show()
ret = app.exec_()
QMetaObject.invokeMethod(worker.timer, "stop")
thread.quit()
thread.wait()
sys.exit(ret)
if __name__ == "__main__":
main()