PyQt5中多線程模塊QThread使用方法的實現
本文主要講解使用多線程模塊QThread解決PyQt界面程序唉執(zhí)行耗時操作時,程序卡頓出現的無響應以及界面輸出無法實時顯示的問題。用戶使用工具過程中出現這些問題時會誤以為程序出錯,從而把程序關閉。這樣,導致工具的用戶使用體驗不好。下面我們通過模擬上述出現的問題并講述使用多線程QThread模塊解決此類問題的方法。
PyQt程序卡頓和無法實時顯示問題現象
使用PyQt界面程序,點擊運行按鈕后,程序在顯示框中每秒打印1個數字。程序代碼如下:
# -*- 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個數字,實際結果是循環(huán)結束后20個數字一同展示):

問題分析
上述實現的GUI程序都是單線程運行,對于需要執(zhí)行一個特別耗時的操作時就會出現該問題現象。要解決這種問題可以考慮使用多線程模塊QThread。
多線程模塊QThread基本原理
QThread是Qt的線程類中最核心的底層類。由于PyQt的的跨平臺特性,QThread要隱藏所有與平臺相關的代碼 要使用的QThread開始一個線程,可以創(chuàng)建它的一個子類,然后覆蓋其它QThread.run()函數。
class Thread(QThread):
def __init__(self):
super(Thread,self).__init__()
def run(self):
#
接下來創(chuàng)建一個新的線程
thread = Thread() thread.start()
可以看出,PyQt的線程使用非常簡單,建立一個自定義的類(如Thread),自我繼承自QThread ,并實現其run()方法即可。在使用線程時可以直接得到Thread實例,調用其start()函數即可啟動線程,線程啟動之后,會自動調用其實現的run()的函數,該方法就是線程的執(zhí)行函數 。
業(yè)務的線程任務就寫在run()函數中,當run()退出之后線程就基本結束了,QThread有started和finished信號,可以為這兩個信號指定槽函數,在線程啟動和結束之時執(zhí)行一段代碼進行資源的初始化和釋放操作,更靈活的使用方法是,在自定義的QThread實例中自定義信號,并將信號連接到指定的槽函數,當滿足一定的業(yè)務條件時發(fā)射此信號。
QThread類中的常用方法
start():啟動線程
wait():阻止線程,直到滿足如下條件之一
(1)與此QThread對象關聯的線程已完成執(zhí)行(即從run返回時),如果線程完成執(zhí)行,此函數返回True,如果線程尚未啟動,也返回True
(2)等待時間的單位是毫秒,如果時間是ULONG_MAX(默認值·),則等待,永遠不會超時(線程必須從run返回),如果等待超時,此函數將會返回False
sleep():強制當前線程睡眠多少秒
QThread類中的常用信號
started: 在開始執(zhí)行run函數之前,從相關線程發(fā)射此信號
finished:當程序完成業(yè)務邏輯時,從相關線程發(fā)射此信號
使用QThread重新實現程序解決問題
先繼承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):
#重寫線程執(zhí)行的run函數
#觸發(fā)自定義信號
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個數字):

小結
如果你實現的工具需要執(zhí)行特別耗時的操作,可以參考使用本文多線程QThread處理方法實現。當然,工具實際實現過程中的場景會比這復雜。比如,你的輸出并不是有固定時間間隔輸出的文本框,可以嘗試使用多次self.trigger.emit(str)方法進行操作。
附錄
1、使用pyuic5轉換界面.ui程序后的QThread_Example_UI.py代碼如下:
# -*- coding: utf-8 -*-
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Form(object):
def setupUi(self, Form):
Form.setObjectName("Form")
Form.resize(498, 331)
self.runButton = QtWidgets.QPushButton(Form)
self.runButton.setGeometry(QtCore.QRect(190, 30, 75, 23))
self.runButton.setObjectName("runButton")
self.listWidget = QtWidgets.QListWidget(Form)
self.listWidget.setGeometry(QtCore.QRect(30, 70, 431, 192))
self.listWidget.setObjectName("listWidget")
self.retranslateUi(Form)
QtCore.QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
_translate = QtCore.QCoreApplication.translate
Form.setWindowTitle(_translate("Form", "Qthread Example"))
self.runButton.setText(_translate("Form", "Run"))
2、Qtdesigner設計的界面源程序代碼QThread_Example_UI.ui如下:
<?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>Form</class> <widget class="QWidget" name="Form"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>498</width> <height>331</height> </rect> </property> <property name="windowTitle"> <string>Qthread Example</string> </property> <widget class="QPushButton" name="runButton"> <property name="geometry"> <rect> <x>190</x> <y>30</y> <width>75</width> <height>23</height> </rect> </property> <property name="text"> <string>Run</string> </property> </widget> <widget class="QListWidget" name="listWidget"> <property name="geometry"> <rect> <x>30</x> <y>70</y> <width>431</width> <height>192</height> </rect> </property> </widget> </widget> <resources/> <connections/> </ui>
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
詳解Python的Django框架中inclusion_tag的使用
這篇文章主要介紹了詳解Python的Django框架中inclusion_tag的使用,文中示例基于Python較早的2.x版本,希望能夠注意一下,需要的朋友可以參考下2015-07-07
python如何獲取Prometheus監(jiān)控數據
這篇文章主要介紹了python如何獲取Prometheus監(jiān)控數據,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-05-05
python gui開發(fā)——制作抖音無水印視頻下載工具(附源碼)
這篇文章主要介紹了python gui開發(fā)——制作抖音無水印視頻下載工具(附源碼)的相關資料,幫助大家更好的理解和學習使用python,感興趣的朋友可以了解下2021-02-02

