关于python:如何在使用QWaitCondition的QThread中使用QTimer? (吡side)

How to use QTimer inside QThread which uses QWaitCondition? (pyside)

我正在使用pyside,但是(我认为)是一个通用的Qt问题。

我知道QThread实现会调用._exec()方法,因此我们应该在启动的QThread上有一个事件循环。 这样,我们可以在该线程上使用QTimer(我已经做到了,它可以完美地工作)。 我的问题是,当还使用QWaitCondition时,我希望有一个带有无限循环的"消费者"线程,等待(从生产者处)在QWaitCondition上进行通知。 我的问题是,使用这种设计,我无法在使用者线程内使用QTimer。

这是我要解释的场景的摘要:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
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_())

单击按钮后,我们将获得如下输出:

1
2
3
4
5
loop from main window                    
loop from timer main window
loop from timer main window
loop from timer main window
...

这意味着在loop()方法内部创建的QTimer不会被事件循环执行。

如果我将设计从QWaitCondition更改为Signals(这是更好的设计,恕我直言),则QTimer可以工作,但是我想知道为什么使用QWaitCondition时它们不起作用。


若要仍在长时间运行的任务(也称为连续循环)中处理事件,则需要调用QCoreApplication::processEvents()

这实际上将穿过线程的所有排队的插槽。

对于信号(如果它们是QueuedConnection信号/插槽连接),也必须调用此函数才能使其从当前线程进入另一个线程。

对于PySides,您需要调用PySide.QtCore.QCoreApplication.processEvents()


您的方法loop完全占用线程。
它不会将控制权返回给事件循环。 计时器将其事件发送到无法获得控制的事件循环。
IMO您的恶意循环有问题。

解决此问题的一种方法是在循环中添加QApplication.processEvents()(错误的方法)。

我认为您还想要其他东西,这是我的更正:

1
2
3
4
5
6
7
8
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,直到您调用stop。