0

这是我基于 QThread 编写的可爱线程。您会注意到它有一个事件队列。4 秒后,一个事件触发并在doWork. doWork应该在所有打印之间休眠并给其他线程一个运行的机会。可以说,所有打印和睡眠doWork运行的时间足够长,以至于另一个线程确实应该有一些时间来执行。

from PySide.QtCore import *
from PySide.QtGui import *

class DoStuffPeriodically(QThread):
    def __init__(self):
        super(DoStuffPeriodically, self).__init__()

    def doWork(self):
        #... do work, post signal to consumer
        print "Start work"
        for i in range(0,100):
            print "work %i" % i
            QThread.msleep(10)
        print "Done work"
        return

    def run(self):
        """ Setup "pullFiles" to be called once a second"""
        self.timer= QTimer()
        self.timer.setSingleShot(True)
        self.timer.timeout.connect(self.doWork)
        self.timer.start(4000)
        self.exec_()

这是我用来控制线程的顶级 QT 小部件。它基本上只是一个启动/停止线程的按钮。

class Widg(QWidget):
    def __init__(self):
        super(Widg, self).__init__()
        self.thread = DoStuffPeriodically()
        self.startStopButton = QPushButton()
        hBoxLayout = QHBoxLayout()
        hBoxLayout.addWidget(self.startStopButton)
        self.startStopButton.pressed.connect(self.startStopThread)
        self.setLayout(hBoxLayout)
        self.threadRunning = False

    def startStopThread(self):
        if self.threadRunning:
            print "Stopping..."
            self.thread.exit(0)
            self.threadRunning = False
            print "Stopped"
        else:
            print "Starting..."
            self.thread.start()
            self.threadRunning = True
            print "Started"


if __name__ == "__main__":
    from sys import argv
    qApp = QApplication(argv)    
    widg = Widg()
    widg.show()
    qApp.exec_()

如果我单击 startStopButton,我希望看到线程开始打印

开始...
开始...
开始工作
工作 0
工作 1
...
工作 99
完成工作

但我想做的是能够在线程工作时停止线程。我期待一些类似的东西

开始...
开始...
开始工作
工作 0
工作 1
...
工作N
停止...
工作 99
完成工作
停...

相反,工作线程似乎正在阻止主线程执行?我必须等待工作完成,然后才能单击 startStopButton,给我

开始...
开始...
开始工作
工作 0
工作 1
...
工作 99
完成工作
停止...
停...

doWork跑多长时间都没关系。我已经将它提高到循环 10000 次。它似乎从来没有给主线程时间,并且小部件没有响应。我是否在做一些阻止真正线程实际工作的事情?

(我使用的是 python 2.7 和 pyside 1.10。)

更新

如果我修改run为直接完成工作,而不是基于QTimer线程似乎可以正常工作。即更改run为:

def run(self):
    self.doWork()
    return

这并不能解决我的问题,因为我想使用事件队列运行。因此,我怀疑这是某种信号/插槽问题,其中QTimer信号与错误的线程相关联。

请注意,在工作完成之前,我不会体验exit或阻止。quit我只是遇到线程根本不起作用。即主窗口被阻止,我什至无法单击按钮甚至启动退出线程

4

2 回答 2

3

问题是该QThread方法正在发挥作用。的线程亲和性QThread始终是创建QThread 的线程。因此,信号告诉QThread' 拥有线程执行doWork——在这种情况下是主线程。所以即使doWork是在这个 QThread 中定义的,工作也是由主线程完成的。我知道一种心灵扭曲。为了解释,让我首先引用文档

QThread 对象存在于另一个线程中,即创建它的那个线程。

所以当这个信号/插槽连接建立时

 self.timer.timeout.connect(self.doWork)

它,默认情况下是一个AutoConnection

(默认)如果信号是从与接收对象不同的线程发出的,则信号被排队,表现为 Qt::QueuedConnection。否则,槽被直接调用,表现为 Qt::DirectConnection。连接类型在信号发出时确定。

信号的来源是我的QThread,因为QTimer是在run方法中创建的,但目的地是主线程。它正在主线程的事件队列中排队!解决方案是创建第二个工作 QObject,它将具有当前线程的亲和性:

class Worker(QObject):
    def __init__(self, parent):
        super(Worker, self).__init__(parent=parent)

    def doWork(self):
        #... do work, post signal to consumer
        print "Start work"
        for i in range(0,1000):
            print "work %i" % i
            QThread.msleep(100)
        print "Done work"
        return

然后运行变为:

def run(self):
    """ Setup "pullFiles" to be called once a second"""
    print "Running..."       
    self.worker = Worker(parent=None) #affinity = this thread
    self.timer= QTimer() #affinity = this thread
    print self.timer.thread()
    self.timer.setSingleShot(True)
    self.timer.timeout.connect(self.worker.doWork)
    self.timer.start(4000)
    self.exec_()
    print "Exec_ done"

这有效。信号的来源和目的地都在一个线程中,不会遍历回主线程。瞧!

于 2012-08-15T16:05:19.940 回答
0

来自QThread::exit()文档:

Tells the thread's event loop to exit with a return code.

doWork是事件循环中的单个事件。事件循环调用了您的事件,因此它等待它完成。exit是另一个事件,在事件循环中排队等待doWork完成。这msleep有助于您的 GUI 的响应能力(给它时间重新绘制和执行按钮处理程序),但它确实不会使exit事件以某种方式潜入。

如果您希望您doWork随时可以被打断,您必须改变您的逻辑。使计时器更频繁地触发,并且只增加一。exit 然后可以在两者之间随时采取行动。

于 2012-08-15T15:54:00.100 回答