本文主要講解使用多線程模塊QThread解決PyQt界面程序唉執行耗時操做時,程序卡頓出現的無響應以及界面輸出沒法實時顯示的問題。用戶使用工具過程當中出現這些問題時會誤覺得程序出錯,從而把程序關閉。這樣,致使工具的用戶使用體驗很差。下面咱們經過模擬上述出現的問題並講述使用多線程QThread模塊解決此類問題的方法。多線程
使用PyQt界面程序,點擊運行按鈕後,程序在顯示框中每秒打印1個數字。程序代碼以下:app
# -*- coding: utf-8 -*- import sys import time from PyQt5.QtCore import QThread, pyqtSignal from PyQt5.QtWidgets import QApplication, QMainWindow from QThread_Example_UI import Ui_Form class MyMainForm(QMainWindow, Ui_Form): def __init__(self, parent=None): super(MyMainForm, self).__init__(parent) self.setupUi(self) self.runButton.clicked.connect(self.display) def display(self): for i in range(20): time.sleep(1) self.listWidget.addItem(str(i)) if __name__ == "__main__": app = QApplication(sys.argv) myWin = MyMainForm() myWin.show() sys.exit(app.exec_())
程序運行過程結果以下(點擊Run按鈕後界面出現未響應字樣,同時程序也沒有出現每隔1秒打印1個數字,實際結果是循環結束後20個數字一同展現):函數
問題分析工具
上述實現的GUI程序都是單線程運行,對於須要執行一個特別耗時的操做時就會出現該問題現象。要解決這種問題能夠考慮使用多線程模塊QThread。spa
QThread是Qt的線程類中最核心的底層類。因爲PyQt的的跨平臺特性,QThread要隱藏全部與平臺相關的代碼 要使用的QThread開始一個線程,能夠建立它的一個子類,而後覆蓋其它QThread.run()函數。線程
class Thread(QThread): def __init__(self): super(Thread,self).__init__() def run(self): #
接下來建立一個新的線程code
thread = Thread()
thread.start()
能夠看出,PyQt的線程使用很是簡單,創建一個自定義的類(如Thread),自我繼承自QThread ,並實現其run()方法便可。在使用線程時能夠直接獲得Thread實例,調用其start()函數便可啓動線程,線程啓動以後,會自動調用其實現的run()的函數,該方法就是線程的執行函數 。orm
業務的線程任務就寫在run()函數中,當run()退出以後線程就基本結束了,QThread有started和finished信號,能夠爲這兩個信號指定槽函數,在線程啓動和結束之時執行一段代碼進行資源的初始化和釋放操做,更靈活的使用方法是,在自定義的QThread實例中自定義信號,並將信號鏈接到指定的槽函數,當知足必定的業務條件時發射此信號。對象
start():啓動線程blog
wait():阻止線程,直到知足以下條件之一
(1)與此QThread對象關聯的線程已完成執行(即從run返回時),若是線程完成執行,此函數返回True,若是線程還沒有啓動,也返回True
(2)等待時間的單位是毫秒,若是時間是ULONG_MAX(默認值·),則等待,永遠不會超時(線程必須從run返回),若是等待超時,此函數將會返回False
sleep():強制當前線程睡眠多少秒
QThread類中的經常使用信號
started:在開始執行run函數以前,從相關線程發射此信號
finished:當程序完成業務邏輯時,從相關線程發射此信號
先繼承QThread類並從新實現其中的run()函數,也就是說把耗時的操做放入run()函數中。代碼以下:
# -*- coding: utf-8 -*- import sys import time from PyQt5.QtCore import QThread, pyqtSignal from PyQt5.QtWidgets import QApplication, QMainWindow from QThread_Example_UI import Ui_Form class MyMainForm(QMainWindow, Ui_Form): def __init__(self, parent=None): super(MyMainForm, self).__init__(parent) self.setupUi(self) # 實例化線程對象 self.work = WorkThread() self.runButton.clicked.connect(self.execute) def execute(self): # 啓動線程 self.work.start() # 線程自定義信號鏈接的槽函數 self.work.trigger.connect(self.display) def display(self,str): # 因爲自定義信號時自動傳遞一個字符串參數,因此在這個槽函數中要接受一個參數 self.listWidget.addItem(str) class WorkThread(QThread): # 自定義信號對象。參數str就表明這個信號能夠傳一個字符串 trigger = pyqtSignal(str) def __int__(self): # 初始化函數 super(WorkThread, self).__init__() def run(self): #重寫線程執行的run函數 #觸發自定義信號 for i in range(20): time.sleep(1) # 經過自定義信號把待顯示的字符串傳遞給槽函數 self.trigger.emit(str(i)) if __name__ == "__main__": app = QApplication(sys.argv) myWin = MyMainForm() myWin.show() sys.exit(app.exec_())
程序運行結果以下(實現了每隔1秒打印1個數字):
若是你實現的工具須要執行特別耗時的操做,能夠參考使用本文多線程QThread處理方法實現。固然,工具實際實現過程當中的場景會比這複雜。好比,你的輸出並非有固定時間間隔輸出的文本框,能夠嘗試使用屢次self.trigger.emit(str)方法進行操做。