pythonGUI库图形界⾯开发之PyQt5线程类QThread详细
使⽤⽅法
QThread是Qt的线程类中最核⼼的底层类。由于PyQt的的跨平台特性,QThread要隐藏所有与平台相关的代码
要使⽤的QThread开始⼀个线程,可以创建它的⼀个⼦类,然后覆盖其它QThread.run()函数
class Thread(QThread):
def __init __(self):
super(Thread,self).__ init __()
def run(self):
#线程相关的代码
pass
接下来创建⼀个新的线程
thread = Thread()
thread.start()
可以看出,PyQt的线程使⽤⾮常简单—-建⽴⼀个⾃定义的类(如thread),⾃我继承⾃QThread ,并实现其run()⽅法即可
在使⽤线程时可以直接得到Thread实例,调⽤其start()函数即可启动线程,线程启动之后,会⾃动调⽤其实现的run()的函数,该⽅法就是线程的执⾏函数
业务的线程任务就写在run()函数中,当run()退出之后线程就基本结束了,QThread有started和finished信号,可以为这两个信号指定槽函数,在线程启动和结束之时执⾏⼀段代码进⾏资源的初始化和释放操作,更灵活的使⽤⽅法是,在⾃定义的QThread实例中⾃定义信号,并将信号连接到指定的槽函数,当满⾜⼀定的业务条件时发射此信号
QThread类中的常⽤⽅法
⽅法描述
start()启动线程
wait()阻⽌线程,直到满⾜如下条件之⼀
与此QThread对象关联的线程已完成执⾏(即从run返回时),如果线程完成执⾏,此函数返回True,如果线程尚未启动,也返回True
等待时间的单位是毫秒,如果时间是ULONG_MAX(默认值·),则等待,永远不会超时(线程必须从run返回),如果等待超时,此函数将会返回False
sleep()强制当前线程睡眠多少秒
QThread类中的常⽤信号
信号描述
started在开始执⾏run函数之前,从相关线程发射此信号
finished当程序完成业务逻辑时,从相关线程发射此信号
QThread的使⽤⽅法实例
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class MainWidget(QWidget):
def __init__(self, parent=None):
super(MainWidget, self).__init__(parent)
#设置标题
self.setWindowTitle('QThread多线程例⼦')
#实例化多线程对象
self.thread = Worker()
#实例化列表控件与按钮控件
self.listFile = QListWidget()
self.btnStart = QPushButton('开始')
#把控件放置在栅格布局中
layout = QGridLayout(self)
layout.addWidget(self.listFile, 0, 0, 1, 2)
layout.addWidget(self.btnStart, 1, 1)
#信号与槽函数的连接
self.t(self.slotStart)
self.t(self.slotAdd)
def slotAdd(self, file_inf):
#向列表控件中添加条⽬
self.listFile.addItem(file_inf)
def slotStart(self):
#开始按钮不可点击,线程开始
self.btnStart.setEnabled(False)
self.thread.start()
class Worker(QThread):
sinOut = pyqtSignal(str)
def __init__(self, parent=None):
super(Worker, self).__init__(parent)
#设置⼯作状态与初始num数值
self.working = True
self.num = 0
def __del__(self):
#线程状态改变与线程终⽌
self.working = False
self.wait()
def run(self):
while self.working == True:
#获取⽂本
file_str = 'File index{0}'.format(self.num)
self.num += 1
# 发射信号
it(file_str)
# 线程休眠2秒
self.sleep(2)
if __name__ == '__main__':
app = QApplication(sys.argv)
demo = MainWidget()
demo.show()
<_())
运⾏效果图如下
代码分析
在这个例⼦中,单击开始按钮,会在后台定时读取数据,并把返回的数据显⽰在界⾯中,⾸先使⽤以下代码进⾏布
局,把列表控件和按钮控件放在栅格布局管理器中
#实例化列表控件与按钮控件
self.listFile = QListWidget()
self.btnStart = QPushButton('开始')
#把控件放置在栅格布局中
layout = QGridLayout(self)
layout.addWidget(self.listFile, 0, 0, 1, 2)
layout.addWidget(self.btnStart, 1, 1)
然后将按钮的clicked信号连接到槽函数,单击开始触发槽函数
self.t(self.slotStart)
def slotStart(self):
#开始按钮不可点击,线程开始
self.btnStart.setEnabled(False)
self.thread.start()
⽐较复杂的是线程的信号,将线程的sinOut信号连接到slotAdd()槽函数,SlotAdd()函数负责在列表控件中动态添加字符串条⽬
self.t(self.slotAdd)
def slotAdd(self,file_inf):
#向列表控件中添加条⽬
self.listFile.addItem(file_inf)
定义⼀个线程类,继承⾃QThread,当线程启动时,执⾏run()函数
class Worker(QThread):
sinOut = pyqtSignal(str)
def __init__(self, parent=None):
super(Worker, self).__init__(parent)
#设置⼯作状态与初始num数值
self.working = True
self.num = 0
def __del__(self):
#线程状态改变与线程终⽌
self.working = False
self.wait()
def run(self):
while self.working == True:
#获取⽂本
file_str = 'File index{0}'.format(self.num)
self.num += 1
# 发射信号
it(file_str)
# 线程休眠2秒
self.sleep(2)
多线程失败案例
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
global sec
sec=0
def setTime():
global sec
sec+=1
#Led显⽰数字+1
lcdNumber.display(sec)app开发公司哪家好
def work():
#计时器每秒计数
timer.start(1000)
for i in range(200000000):
pass
timer.stop()
if __name__ == '__main__':
app=QApplication(sys.argv)
top=QWidget()
#垂直布局
layout=QVBoxLayout(top)
#添加⼀个显⽰⾯板
lcdNumber=QLCDNumber()
layout.addWidget(lcdNumber)
button=QPushButton('测试')
layout.addWidget(button)
timer=QTimer()
#每次计时结束,触发setTime
t(setTime)
t(work)
top.show()
<_())
失败效果图如下
长时间停留在此界⾯,知道多线程任务完成后,此界⾯才会动,当耗时程序⾮常⼤时,就会造成程序运
⾏失败的假象,实际还是在后台运⾏的,只是没有显⽰在主窗⼝的界⾯上,当然⽤户体验也就⾮常差,那么如何解决这个问题呢,下⾯实例三进⾏解答
分离UI主线程与⼯作线程实例
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
global sec
sec = 0
class WorkThread(QThread):
#实例化⼀个信号对象
trigger = pyqtSignal()
def __int__(self):
super(WorkThread, self).__init__()
def run(self):
#开始进⾏循环
for i in range(2000000000):
pass
# 循环完毕后发出信号
def countTime():
global sec
sec += 1
# LED显⽰数字+1
lcdNumber.display(sec)
def work():
# 计时器每秒计数
timer.start(1000)
# 计时开始
workThread.start()
# 当获得循环完毕的信号时,停⽌计数
def timeStop():
#定时器停⽌
timer.stop()
print("运⾏结束⽤时", lcdNumber.value())
global sec
sec = 0
if __name__ == "__main__":
app = QApplication(sys.argv)
top = QWidget()
# 垂直布局类QVBoxLayout
layout = QVBoxLayout(top)
# 加显⽰屏,按钮到布局中
lcdNumber = QLCDNumber()
layout.addWidget(lcdNumber)
button = QPushButton("测试")
layout.addWidget(button)
#实例化定时器与多线程类
timer = QTimer()
workThread = WorkThread()
t(work)
# 每次计时结束,触发 countTime
t(countTime)
top.show()
<_())
运⾏效果,程序主界⾯的数值会每秒增加1,直到循环结束,这⾥就避免了主界⾯长时间不动的尴尬!
QThread线程事件处理实例
对于执⾏很耗时的程序来说,由于PyQt需要等待程序执⾏完毕才能进⾏下⼀步,这个过程表现在界⾯上就是卡顿,⽽如果需要执⾏这个耗时程序时不断的刷新界⾯。那么就可以使⽤QApplication.processEvents(),那么就可以⼀边执⾏耗时程序,⼀边刷新界⾯的功能,给⼈的感觉就是程序运⾏很流畅,因此QApplicationEvents()的使⽤⽅法就是,在主函数执⾏耗时操作的地⽅,加⼊QApplication.processEvents()
import sys,time
from PyQt5.QtWidgets import QWidget,QPushButton,QApplication,QListWidget,QGridLayout
class WinForm(QWidget):
def __init__(self,parent=None):
super(WinForm, self).__init__(parent)
#设置标题与布局⽅式
self.setWindowTitle('实时刷新界⾯的例⼦')
layout=QGridLayout()
#实例化列表控件与按钮控件
self.listFile=QListWidget()
self.btnStart=QPushButton('开始')
#添加到布局中指定位置
layout.addWidget(self.listFile,0,0,1,2)
layout.addWidget(self.btnStart,1,1)