这是我基于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,我希望看到线程开始打印
Starting... Started... Start Work work 0 work 1 ... work 99 Done Work
但我想做的是能够在工作时停止线程。
我希望有所不同Starting... Started... Start Work work 0 work 1 ... work N Stopping... work 99 Done Work Stopped...
相反,工作线程似乎阻止主线程执行?我必须等待工作完成才能点击startStopButton,给我
Starting... Started... Start Work work 0 work 1 ... work 99 Done Work Stopping... Stopped...
doWork
运行多久并不重要。我把它提升了10000次循环。 它似乎没有给主线程留出时间,而且小部件没有响应。我做的是阻止真正的线程实际工作吗?
(我使用的是python 2.7和pyside 1.10。)
如果我修改run
直接执行工作,而不是基于QTimer
线程似乎正常工作。即将run
更改为:
def run(self):
self.doWork()
return
这不能解决我的问题,因为我想使用事件队列运行。因此,我怀疑这是某种信号/插槽问题,其中QTimer
信号与错误的线程相关联。
请注意,在完成工作之前,我不会遇到exit
或quit
个阻止。我只是遇到线程根本无法工作。即主窗口被阻止,我甚至无法点击按钮甚至开始退出线程
答案 0 :(得分:3)
问题在于QThread
方法正在开展工作。 QThread
的线程关联始终是thread that created QThread。因此,信号告诉QThread
拥有线程执行doWork
- 在这种情况下是主线程。因此,即使在此QThread中定义doWork
,工作也由主线程完成。我知道有点扭曲。为了解释,让我首先引用文档
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"
这很有效。信号的源和目标都在一个线程中,并且不会遍历回主线程。瞧!
答案 1 :(得分:0)
来自QThread::exit()文档:
Tells the thread's event loop to exit with a return code.
您的doWork
是事件循环中的单个事件。事件循环调用了您的事件,因此它等待它完成。 exit
是另一个事件,在事件循环中排队并等待doWork
完成。 msleep
有助于GUI的响应性(给它时间来重新绘制和执行按钮处理程序),但它确实不会使exit
事件以某种方式潜行。
如果您希望doWork
随时可以中断,则必须更改逻辑。使计时器更频繁地点火并且仅增加1。退出然后可以在任何时间进行。