简单应用QThread

时间:2016-01-16 15:18:21

标签: python pyqt qthread

我正在抓住QThread,以便在后台执行可能更长的线程时不锁定我的GUI。我正在尝试练习写一个简单的应用程序:倒数计时器,我可以点击“开始”按钮开始,从而启动倒计时循环,我可以通过点击“暂停”按钮暂停。

我想以“正确的方式”使用QThread(解释over here),即继承QObject,然后通过moveToThread将此子类的实例附加到QThread。我用QTDesigner做了GUI,这是我到目前为止修改过的内容(来自Net上的其他示例):

from PyQt4 import QtGui, QtCore
from PyQt4.QtCore import QThread, SIGNAL

import time, sys, mydesign

class SomeObject(QtCore.QObject):

    def __init__(self, lcd):
        super(self.__class__, self).__init__()
        self.lcd = lcd
        self.looping = True
    finished = QtCore.pyqtSignal()
    def pauseLoop(self):
        print "Hello?"
        self.looping = False

    def longRunning(self):
        count = 10
        self.lcd.display(count)
        while count > 0 and self.looping:
            time.sleep(1)
            count -= 1
            self.lcd.display(count)
        self.finished.emit()

class ThreadingTutorial(QtGui.QMainWindow, mydesign.Ui_MainWindow):
    def __init__(self):
        super(self.__class__, self).__init__()
        self.setupUi(self)

        #an instance of SomeObject gets attached to
        #an instance of wrapper class QThread()

        #objThread is a wrapper object for an instance of
        # self-defined class SomeObject
        self.objThread = QtCore.QThread()
        self.obj = SomeObject(self.lcdNumber)
        self.obj.moveToThread(self.objThread)
        #connect all the signals
        self.obj.finished.connect(self.objThread.quit)
        self.objThread.started.connect(self.obj.longRunning)
        self.startCountdown.clicked.connect(self.objThread.start)
        self.pauseButton.clicked.connect(self.obj.pauseLoop)


if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    form = ThreadingTutorial()
    form.show()
    app.exec_()

GUI未锁定,但函数pauseLoop()仅在循环结束后执行。我如何才能完成我的目标才能在longRunning()中暂停循环?

提前谢谢!

更新

在史蒂夫和three_pineapples的评论的帮助下,我使用objThread的内部事件循环提出了以下解决方案:

from PyQt4 import QtGui, QtCore
from PyQt4.QtCore import QThread, SIGNAL, QTimer

import time, sys, mydesign

class SomeObject(QtCore.QObject):

    def __init__(self):
        super(self.__class__, self).__init__()
        self.looping = True
        self.count = 10
        self.timer = QTimer(self)
        self.timer.start(1000)
        self.connect(self.timer, SIGNAL("timeout()"), self.longRunning)

    finished = QtCore.pyqtSignal()
    iterated = QtCore.pyqtSignal()
    def pauseLoop(self):
        self.looping = False

    def longRunning(self):
        if self.count > 0 and self.looping:
            self.count -= 1
            self.iterated.emit()
        else:
            self.finished.emit()# sends signal for stopping event loop

class ThreadingTutorial(QtGui.QMainWindow, mydesign.Ui_MainWindow):
    def __init__(self):
        super(self.__class__, self).__init__()
        self.setupUi(self)


        #an instance of SomeObject gets attached to
        #an instance of wrapper class QThread()

        #objThread is a wrapper object for an instance of
        # self-defined class SomeObject
        self.objThread = QtCore.QThread()
        self.obj = SomeObject()
        self.lcdNumber.display(self.obj.count)
        self.obj.moveToThread(self.objThread)
        #connect all the signals
        self.obj.finished.connect(self.objThread.quit)
        self.obj.iterated.connect(lambda: self.lcdNumber.display(self.obj.count))
        self.objThread.started.connect(self.obj.longRunning)
        self.startCountdown.clicked.connect(self.objThread.start)        
        self.pauseButton.clicked.connect(self.obj.pauseLoop)


if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    form = ThreadingTutorial()
    form.show()
    app.exec_()

1 个答案:

答案 0 :(得分:1)

这种情况正在发生,因为您告诉Qt在单个线程中运行SomeObject对象的代码。在您的代码中,您有两个线程,主GUI线程和self.objThreadpauseLoop()longRunning()在同一个帖子中调用,这意味着longRunning()必须在pauseLoop()运行之前完成。相反,您需要从其他线程调用pauseLoop(),而不是self.objThread

请注意,当您开始从两个不同的线程(主线程和新线程)更改数据时,您可能会开始遇到竞争条件。既然你只设置了一个布尔变量,那么你会没事的,但要记住这一点。

编辑:正如three_pineapples在评论中指出的那样,访问GUI对象(例如:QWidget)只能在主GUI线程中完成。为了说明这是如何工作的,我更新了这个答案,使用信号来更新GUI线程中的LCD,而不是仅仅将值打印到stdout。我还添加了代码来打印不同部分的当前线程。

from PySide import QtGui, QtCore
from PySide.QtCore import QThread, SIGNAL

import time, sys

class SomeObject(QtCore.QObject):
    updateCounter = QtCore.Signal(int)
    finished = QtCore.Signal()
    def __init__(self):
        super(self.__class__, self).__init__()
        self.looping = True
    def pauseLoop(self):
        self.looping = False
        print 'Pause Loop: '+str(QThread.currentThreadId())
    def longRunning(self):
        print 'Long Running: '+str(QThread.currentThreadId())
        count = 10
        while count > 0 and self.looping:
            count -= 1
            self.updateCounter.emit(count)
            time.sleep(1)
        self.finished.emit()

class ThreadingTutorial(QtGui.QWidget):
    def __init__(self):
        super(self.__class__, self).__init__()
        #
        self.thisLayout = QtGui.QVBoxLayout(self)
        self.startCountdown = QtGui.QPushButton('Start', self)
        self.pauseButton = QtGui.QPushButton('Stop', self)
        self.lcd = QtGui.QLabel('', self)
        self.thisLayout.addWidget(self.startCountdown)
        self.thisLayout.addWidget(self.pauseButton)
        self.thisLayout.addWidget(self.lcd)
        #
        print 'Main GUI Thread: '+str(QThread.currentThreadId())
        self.objThread = QtCore.QThread()
        self.obj = SomeObject()
        self.obj.moveToThread(self.objThread)
        self.obj.updateCounter.connect(self._updateTimer)
        #
        self.obj.finished.connect(self.objThread.quit)
        self.objThread.started.connect(self.obj.longRunning)
        self.startCountdown.clicked.connect(self.objThread.start)
        self.pauseButton.clicked.connect(self._stopTimer)
    def _stopTimer(self):
        self.obj.pauseLoop()
    def _updateTimer(self, num):
        self.lcd.setText(str(num))
        print 'Update Timer: '+str(QThread.currentThreadId())


if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    form = ThreadingTutorial()
    form.show()
    app.exec_()

示例输出:

Main GUI Thread: 3074717376
Long Running: 3034471232
Update Timer: 3074717376
Update Timer: 3074717376
Update Timer: 3074717376
Update Timer: 3074717376
Update Timer: 3074717376
Update Timer: 3074717376
Pause Loop: 3074717376