我正在使用pyside,但(我认为)是一个通用的Qt问题。
我知道QThread实现调用._exec()方法,所以我们应该在启动的QThread上有一个事件循环。这样我们就可以在那个线程上使用QTimer(我已经完成了这个并且它完美地工作)。我的问题是当QWaitCondition也被使用时,我想要一个带有无限循环的“消费者”线程等待在QWaitCondition上通知(来自生产者)。我遇到的问题是,使用此设计我不能在消费者线程中使用QTimer。
这是我试图解释的场景的片段:
from PySide import QtGui
from PySide import QtCore
import sys
class MainWindow(QtGui.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.button = QtGui.QPushButton(self)
self.button.setText("Periodical")
self.button.clicked.connect(self.periodical_call)
self.thread = QtCore.QThread()
self.worker = Worker()
self.worker.moveToThread(self.thread)
self.thread.started.connect(self.worker.loop)
self.thread.start()
def closeEvent(self, x):
self.worker.stop()
self.thread.quit()
self.thread.wait()
def periodical_call(self):
self.worker.do_stuff("main window") # this works
self.timer = QtCore.QTimer()
self.timer.timeout.connect(self.do_stuff) # this also works
self.timer.start(2000)
def do_stuff(self):
self.worker.do_stuff("timer main window")
class Worker(QtCore.QObject):
def do_stuff_timer(self):
do_stuff("timer worker")
def do_stuff(self, origin):
self.origin = origin
self.wait.wakeOne()
def stop(self):
self._exit = True
self.wait.wakeAll()
def loop(self):
self.wait = QtCore.QWaitCondition()
self.mutex = QtCore.QMutex()
self._exit = False
while not self._exit:
self.wait.wait(self.mutex)
print "loop from %s" % (self.origin,)
self.timer = QtCore.QTimer()
self.timer.setSingleShot(True)
self.timer.timeout.connect(self.do_stuff_timer)
self.timer.start(1000) # <---- this doesn't work
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
frame = MainWindow()
frame.show()
sys.exit(app.exec_())
点击按钮后,我们会得到如下输出:
loop from main window
loop from timer main window
loop from timer main window
loop from timer main window
...
这意味着在loop()方法中创建的QTimer永远不会被事件循环执行。
如果我将设计从QWaitCondition更改为Signals(这是更好的设计imho),QTimer可以工作,但我想知道为什么在使用QWaitCondition时它们不起作用。
答案 0 :(得分:5)
要在长时间运行的任务(也称为连续循环)中处理事件,您需要调用QCoreApplication::processEvents()
。
这将基本上遍历您的线程的所有排队的插槽。
调用此函数对于信号(如果它们是QueuedConnection信号/插槽连接)也是必要的,以使其脱离当前线程并进入另一个线程。
对于PySides,您需要致电PySide.QtCore.QCoreApplication.processEvents()
答案 1 :(得分:3)
您的方法loop
完全占用线程。
它不会将控制权返回给事件循环。定时器将其事件发送到无法获得控制的事件循环
IMO你的诡计循环是错误的。
解决这个问题的一种方法是在循环中添加QApplication.processEvents()
(糟糕的方法)。
我想你想要别的东西,这是我的更正:
def loop(self):
self.timer = QtCore.QTimer()
self.timer.setSingleShot(False)
self.timer.timeout.connect(self.do_stuff_timer)
self.timer.start(1000)
def stop(self):
self.timer.stop()
这将每秒调用do_stuff_timer
,直到你打电话给停止。