您好,登錄后才能下訂單哦!
QThread是Qt的線程類中最核心的底層類。由于PyQt的的跨平臺(tái)特性,QThread要隱藏所有與平臺(tái)相關(guān)的代碼
要使用的QThread開始一個(gè)線程,可以創(chuàng)建它的一個(gè)子類,然后覆蓋其它QThread.run()函數(shù)
class Thread(QThread): def __init __(self): super(Thread,self).__ init __() def run(self): #線程相關(guān)的代碼 pass
接下來創(chuàng)建一個(gè)新的線程
thread = Thread() thread.start()
可以看出,PyQt的線程使用非常簡單—-建立一個(gè)自定義的類(如thread),自我繼承自QThread ,并實(shí)現(xiàn)其run()方法即可
在使用線程時(shí)可以直接得到Thread實(shí)例,調(diào)用其start()函數(shù)即可啟動(dòng)線程,線程啟動(dòng)之后,會(huì)自動(dòng)調(diào)用其實(shí)現(xiàn)的run()的函數(shù),該方法就是線程的執(zhí)行函數(shù)
業(yè)務(wù)的線程任務(wù)就寫在run()函數(shù)中,當(dāng)run()退出之后線程就基本結(jié)束了,QThread有started和finished信號(hào),可以為這兩個(gè)信號(hào)指定槽函數(shù),在線程啟動(dòng)和結(jié)束之時(shí)執(zhí)行一段代碼進(jìn)行資源的初始化和釋放操作,更靈活的使用方法是,在自定義的QThread實(shí)例中自定義信號(hào),并將信號(hào)連接到指定的槽函數(shù),當(dāng)滿足一定的業(yè)務(wù)條件時(shí)發(fā)射此信號(hào)
方法 | 描述 |
---|---|
start() | 啟動(dòng)線程 |
wait() | 阻止線程,直到滿足如下條件之一 |
與此QThread對象關(guān)聯(lián)的線程已完成執(zhí)行(即從run返回時(shí)),如果線程完成執(zhí)行,此函數(shù)返回True,如果線程尚未啟動(dòng),也返回True | |
等待時(shí)間的單位是毫秒,如果時(shí)間是ULONG_MAX(默認(rèn)值·),則等待,永遠(yuǎn)不會(huì)超時(shí)(線程必須從run返回),如果等待超時(shí),此函數(shù)將會(huì)返回False | |
sleep() | 強(qiáng)制當(dāng)前線程睡眠多少秒 |
信號(hào) | 描述 |
---|---|
started | 在開始執(zhí)行run函數(shù)之前,從相關(guān)線程發(fā)射此信號(hào) |
finished | 當(dāng)程序完成業(yè)務(wù)邏輯時(shí),從相關(guān)線程發(fā)射此信號(hào) |
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) #設(shè)置標(biāo)題 self.setWindowTitle('QThread多線程例子') #實(shí)例化多線程對象 self.thread = Worker() #實(shí)例化列表控件與按鈕控件 self.listFile = QListWidget() self.btnStart = QPushButton('開始') #把控件放置在柵格布局中 layout = QGridLayout(self) layout.addWidget(self.listFile, 0, 0, 1, 2) layout.addWidget(self.btnStart, 1, 1) #信號(hào)與槽函數(shù)的連接 self.btnStart.clicked.connect(self.slotStart) self.thread.sinOut.connect(self.slotAdd) def slotAdd(self, file_inf): #向列表控件中添加條目 self.listFile.addItem(file_inf) def slotStart(self): #開始按鈕不可點(diǎn)擊,線程開始 self.btnStart.setEnabled(False) self.thread.start() class Worker(QThread): sinOut = pyqtSignal(str) def __init__(self, parent=None): super(Worker, self).__init__(parent) #設(shè)置工作狀態(tài)與初始num數(shù)值 self.working = True self.num = 0 def __del__(self): #線程狀態(tài)改變與線程終止 self.working = False self.wait() def run(self): while self.working == True: #獲取文本 file_str = 'File index{0}'.format(self.num) self.num += 1 # 發(fā)射信號(hào) self.sinOut.emit(file_str) # 線程休眠2秒 self.sleep(2) if __name__ == '__main__': app = QApplication(sys.argv) demo = MainWidget() demo.show() sys.exit(app.exec_())
運(yùn)行效果圖如下
代碼分析
在這個(gè)例子中,單擊開始按鈕,會(huì)在后臺(tái)定時(shí)讀取數(shù)據(jù),并把返回的數(shù)據(jù)顯示在界面中,首先使用以下代碼進(jìn)行布局,把列表控件和按鈕控件放在柵格布局管理器中
#實(shí)例化列表控件與按鈕控件 self.listFile = QListWidget() self.btnStart = QPushButton('開始') #把控件放置在柵格布局中 layout = QGridLayout(self) layout.addWidget(self.listFile, 0, 0, 1, 2) layout.addWidget(self.btnStart, 1, 1)
然后將按鈕的clicked信號(hào)連接到槽函數(shù),單擊開始觸發(fā)槽函數(shù)
self.btnStart.clicked.connect(self.slotStart) def slotStart(self): #開始按鈕不可點(diǎn)擊,線程開始 self.btnStart.setEnabled(False) self.thread.start()
比較復(fù)雜的是線程的信號(hào),將線程的sinOut信號(hào)連接到slotAdd()槽函數(shù),SlotAdd()函數(shù)負(fù)責(zé)在列表控件中動(dòng)態(tài)添加字符串條目
self.thread.sinOut.connect(self.slotAdd) def slotAdd(self,file_inf): #向列表控件中添加條目 self.listFile.addItem(file_inf)
定義一個(gè)線程類,繼承自QThread,當(dāng)線程啟動(dòng)時(shí),執(zhí)行run()函數(shù)
class Worker(QThread): sinOut = pyqtSignal(str) def __init__(self, parent=None): super(Worker, self).__init__(parent) #設(shè)置工作狀態(tài)與初始num數(shù)值 self.working = True self.num = 0 def __del__(self): #線程狀態(tài)改變與線程終止 self.working = False self.wait() def run(self): while self.working == True: #獲取文本 file_str = 'File index{0}'.format(self.num) self.num += 1 # 發(fā)射信號(hào) self.sinOut.emit(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顯示數(shù)字+1 lcdNumber.display(sec) def work(): #計(jì)時(shí)器每秒計(jì)數(shù) timer.start(1000) for i in range(200000000): pass timer.stop() if __name__ == '__main__': app=QApplication(sys.argv) top=QWidget() top.resize(300,120) #垂直布局 layout=QVBoxLayout(top) #添加一個(gè)顯示面板 lcdNumber=QLCDNumber() layout.addWidget(lcdNumber) button=QPushButton('測試') layout.addWidget(button) timer=QTimer() #每次計(jì)時(shí)結(jié)束,觸發(fā)setTime timer.timeout.connect(setTime) button.clicked.connect(work) top.show() sys.exit(app.exec_())
失敗效果圖如下
長時(shí)間停留在此界面,知道多線程任務(wù)完成后,此界面才會(huì)動(dòng),當(dāng)耗時(shí)程序非常大時(shí),就會(huì)造成程序運(yùn)行失敗的假象,實(shí)際還是在后臺(tái)運(yùn)行的,只是沒有顯示在主窗口的界面上,當(dāng)然用戶體驗(yàn)也就非常差,那么如何解決這個(gè)問題呢,下面實(shí)例三進(jìn)行解答
分離UI主線程與工作線程實(shí)例
import sys from PyQt5.QtCore import * from PyQt5.QtGui import * from PyQt5.QtWidgets import * global sec sec = 0 class WorkThread(QThread): #實(shí)例化一個(gè)信號(hào)對象 trigger = pyqtSignal() def __int__(self): super(WorkThread, self).__init__() def run(self): #開始進(jìn)行循環(huán) for i in range(2000000000): pass # 循環(huán)完畢后發(fā)出信號(hào) self.trigger.emit() def countTime(): global sec sec += 1 # LED顯示數(shù)字+1 lcdNumber.display(sec) def work(): # 計(jì)時(shí)器每秒計(jì)數(shù) timer.start(1000) # 計(jì)時(shí)開始 workThread.start() # 當(dāng)獲得循環(huán)完畢的信號(hào)時(shí),停止計(jì)數(shù) workThread.trigger.connect(timeStop) def timeStop(): #定時(shí)器停止 timer.stop() print("運(yùn)行結(jié)束用時(shí)", lcdNumber.value()) global sec sec = 0 if __name__ == "__main__": app = QApplication(sys.argv) top = QWidget() top.resize(300, 120) # 垂直布局類QVBoxLayout layout = QVBoxLayout(top) # 加顯示屏,按鈕到布局中 lcdNumber = QLCDNumber() layout.addWidget(lcdNumber) button = QPushButton("測試") layout.addWidget(button) #實(shí)例化定時(shí)器與多線程類 timer = QTimer() workThread = WorkThread() button.clicked.connect(work) # 每次計(jì)時(shí)結(jié)束,觸發(fā) countTime timer.timeout.connect(countTime) top.show() sys.exit(app.exec_())
運(yùn)行效果,程序主界面的數(shù)值會(huì)每秒增加1,直到循環(huán)結(jié)束,這里就避免了主界面長時(shí)間不動(dòng)的尷尬!
對于執(zhí)行很耗時(shí)的程序來說,由于PyQt需要等待程序執(zhí)行完畢才能進(jìn)行下一步,這個(gè)過程表現(xiàn)在界面上就是卡頓,而如果需要執(zhí)行這個(gè)耗時(shí)程序時(shí)不斷的刷新界面。那么就可以使用QApplication.processEvents(),那么就可以一邊執(zhí)行耗時(shí)程序,一邊刷新界面的功能,給人的感覺就是程序運(yùn)行很流暢,因此QApplicationEvents()的使用方法就是,在主函數(shù)執(zhí)行耗時(shí)操作的地方,加入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) #設(shè)置標(biāo)題與布局方式 self.setWindowTitle('實(shí)時(shí)刷新界面的例子') layout=QGridLayout() #實(shí)例化列表控件與按鈕控件 self.listFile=QListWidget() self.btnStart=QPushButton('開始') #添加到布局中指定位置 layout.addWidget(self.listFile,0,0,1,2) layout.addWidget(self.btnStart,1,1) #按鈕的點(diǎn)擊信號(hào)觸發(fā)自定義的函數(shù) self.btnStart.clicked.connect(self.slotAdd) self.setLayout(layout) def slotAdd(self): for n in range(10): #獲取條目文本 str_n='File index{0}'.format(n) #添加文本到列表控件中 self.listFile.addItem(str_n) #實(shí)時(shí)刷新界面 QApplication.processEvents() #睡眠一秒 time.sleep(1) if __name__ == '__main__': app=QApplication(sys.argv) win=WinForm() win.show() sys.exit(app.exec_())
本文詳細(xì)介紹了python GUI庫PyQt5的線程類QThread詳細(xì)使用方法,想了解更多相關(guān)知道請查看下面的相關(guān)鏈接
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。