Pyqt5實(shí)現(xiàn)多線程程序的使用示例
主從架構(gòu)
Pyqt常常使用**主從架構(gòu)(Master-Workers 架構(gòu))**來避免界面卡死的情況。
Master-Workers 架構(gòu)就像它的名字,一個master統(tǒng)領(lǐng)著幾個workers一起干活。其中某個worker倒下了不會導(dǎo)致整體任務(wù)失敗。matser不用干活,因此可以專心指揮workers。
在qt5中,master代表主線程,主要維持主界面的運(yùn)行。當(dāng)觸發(fā)某項耗時耗力的任務(wù)時,主線程將這項任務(wù)分配給其他線程(workers)來做。其他線程出現(xiàn)了災(zāi)難性的錯誤,不會影響到主線程,因此程序不會完全崩潰。且主線程不承擔(dān)耗時耗力的任務(wù),因此避免了復(fù)雜運(yùn)算時主界面卡頓的問題。
進(jìn)程和線程詳見1
pyqt5多線程的架構(gòu)一般由三個模塊實(shí)現(xiàn):
Gui.py
:只存放GUI界面,一般是Qt Designer生成的代碼,無需做任何修改Thread.py
:從線程,主要的邏輯代碼都放在這里。接收主線程的指令,并向主線程返回信號。Main.py
:主線程,負(fù)責(zé)運(yùn)行GUI界面,向從線程發(fā)送指令并接收從線程返回的信號。
GUI模塊
Qt Designer保存的文件為Gui.ui
的格式,使用下面命令轉(zhuǎn)為Gui.py
。
pyuic5 Gui.ui -o Gui.py
轉(zhuǎn)換后的代碼結(jié)構(gòu)如下,我們不需要對這個代碼做任何修改。
# -*- coding: utf-8 -*- # Form implementation generated from reading ui file 'mainwindow.ui' # # Created by: PyQt5 UI code generator 5.5.1 # # WARNING! All changes made in this file will be lost! from PyQt5 import QtCore, QtGui, QtWidgets class Ui_MainWindow(object): # 注意這個類名,后面在Main.py中找到它 def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") # ------- 省略很多行... ------- # self.retranslateUi(MainWindow) QtCore.QMetaObject.connectSlotsByName(MainWindow) def retranslateUi(self, MainWindow): _translate = QtCore.QCoreApplication.translate MainWindow.setWindowTitle(_translate("MainWindow", "標(biāo)題")) # ------- 省略很多行... ------- #
不同線程間的信號與槽
在討論主線程和從線程之前,首先要明確線程間傳遞信號的方法2。
主 -> 從
主線程到從線程的信號就是最基本信號與槽的機(jī)制,使用槽函數(shù)來與從線程通信。一般是Wight被clicked,然后觸發(fā)槽函數(shù)。傳遞的路徑為:1.觸發(fā)信號事件 -> 2.信號clicked -> 3.槽函數(shù)接收信號并運(yùn)行
。使用以下語句綁定槽函數(shù):
from Thread import New_thread # 從線程的引用 class MainWindow(QMainWindow, Ui_MainWindow): def __init__(self, parent=None) -> None: super(MainWindow,self).__init__(parent) self.setupUi(self) # ----------------------------------------- # # ↓↓↓↓↓↓↓ 不用管上面的代碼,主要看下面 ↓↓↓↓↓↓↓ # # ----------------------------------------- # self.thread = None # 先預(yù)定義一個從線程的實(shí)例屬性,這里無需將從線程實(shí)例化 # self.btn為Ui_MainWindow中定義的按鈕,這里將按鈕點(diǎn)擊的信號與槽函數(shù)self.func連接起來 self.btn.clicked.connect(self.func) def func(self): self.thread = New_thread() # 在槽函數(shù)中實(shí)例化從線程,然后就可以操作從線程了 self.thread.start() pass
有時我們需要向槽函數(shù)傳遞參數(shù),一般使用偏函數(shù)或lambda,偏函數(shù)可參見3:
from functools import partial # 偏函數(shù)的引用 from Thread import New_thread # 從線程的引用 class MainWindow(QMainWindow, Ui_MainWindow): def __init__(self, parent=None) -> None: super(MainWindow,self).__init__(parent) self.setupUi(self) # ----------------------------------------- # # ↓↓↓↓↓↓↓ 不用管上面的代碼,主要看下面 ↓↓↓↓↓↓↓ # # ----------------------------------------- # self.thread = None # 先預(yù)定義一個從線程的實(shí)例屬性,這里無需將從線程實(shí)例化 # 第一種方法:這個槽函數(shù)被寫成了偏函數(shù)的形式 partial(self.func, param1, param2) self.btn1.clicked.connect(partial(self.func, param1=1, param2=2)) # 第二種方法:這個槽函數(shù)被寫成了lambda的形式 lambda:self.func(param1=1, param2=2) self.btn2.clicked.connect(lambda:self.func(param1=1, param2=2)) # 這個槽函數(shù)監(jiān)聽了兩個信號哦 def func(self, param1, param2): # 在槽函數(shù)中實(shí)例化從線程,然后就可以操作從線程了 self.thread = New_thread(param1, param2) # 從線程實(shí)例化也可以放在__init__里面,但我喜歡放在這。 self.thread.start() pass
從 -> 主
從線程向主線程傳遞信號一般使用自定義信號,觸發(fā)后,從線程的自定義信號傳遞給主線程連接的槽函數(shù)。觸發(fā)的路徑為1.從線程觸發(fā)信號emit -> 2.聲明信號pyqtSignal -> 3.傳遞給主線程連接的槽函數(shù)
。使用下面代碼建立自定義信號。
先在Thread.py
中定義信號:
# Thread.py from PyQt5.QtCore import QThread, pyqtSignal class New_Thread(QThread): # 聲明定義信號,注意它必須是類屬性。 mySignal = pyqtSignal(int,str) # 后面的參數(shù)是信號的數(shù)據(jù)類型 def __init__(self) -> None: super(New_Thread, self).__init__(parent) pass def run(self): pass # ------------------------------------------ # # ↓↓↓↓↓↓↓ 上面的兩個函數(shù)不用理會,看下面 ↓↓↓↓↓↓↓ # # ------------------------------------------ # # 下面是從線程的邏輯代碼 def func(self): # ------- 省略很多邏輯代碼... ------- # # 向主線程發(fā)送信號 self.mySignal.emit(1,"Hello, Pyqt5")
在主線程Main.py
中監(jiān)聽信號并連接到槽:
# Main.py from Thread import New_thread # 從線程的引用 class MainWindow(QMainWindow, Ui_MainWindow): def __init__(self, parent=None) -> None: super(MainWindow,self).__init__(parent) self.setupUi(self) # ----------------------------------------- # # ↓↓↓↓↓↓↓ 不用管上面的代碼,主要看下面 ↓↓↓↓↓↓↓ # # ----------------------------------------- # def func1(self): self.thread = New_thread() # 從線程實(shí)例化(也可以放在__init__里面) self.thread.start() # 監(jiān)聽從線程發(fā)出的信號,并連接到槽函數(shù)func2 # 記得嗎?mySignal發(fā)出了兩個數(shù)據(jù),一個是int類型,一個str類型 self.thread.mySignal.connect(self.func2) # 槽函數(shù)接收了從線程的信號 def func2(self, param1:int, param2:str): pass
主線程Main模塊
主線程的作用是維護(hù)UI界面運(yùn)行,下面給出Main模塊的一般架構(gòu)
import sys # 顯示ui界面必要的引用 from PyQt5.QtWidgets import QMainWindow, QApplication # 顯示ui界面必要的引用 from GUI import * # 引用Qt Designer生成的GUI模塊 from Thread import New_thread # 從線程的引用 # 第一個父類是PyQt5.QtWidgets.QMainWindow(取決于你在Qt Designer選擇的窗口類型) # 第二個父類是GUI.Ui_MainWindow class MainWindow(QMainWindow, Ui_MainWindow): def __init__(self, parent=None) -> None: super(MainWindow,self).__init__(parent) self.setupUi(self) # 初始化UI界面 self.thread = None # 先預(yù)定義一個從線程的實(shí)例屬性,這里無需將從線程實(shí)例化 self.btn1.clicked.connect(self.func1) # 綁定控件的槽函數(shù),以啟動從線程 self.thread.finished.connect(self.func3) # 監(jiān)聽線程是否完成任務(wù),以結(jié)束從線程 # 省略一萬行綁定槽函數(shù)的代碼... # 定義槽函數(shù),這里可以放入從線程。 def func1(self): self.thread = New_Thread() # 實(shí)例化一個從函數(shù) self.thread.start() self.thread.mySignal.connect(self.func2) # 監(jiān)聽從線程的信號,并綁定槽函數(shù) # 定義響應(yīng)從線程信號的槽函數(shù) def func2(self,param:int): pass # 定義結(jié)束從線程的槽函數(shù) def func3(self): self.thread.stop() # 省略一萬個槽函數(shù)... if __name__ == '__main__': # 任何一個qt應(yīng)用都必須有且僅有一個QApplication對象 # sys.argv是一組命令行參數(shù)的列表。 # 這行代碼就是實(shí)例化一個QApplication app = QApplication(sys.argv) # 主線程實(shí)例化 main_window = MainWindow() # 顯示窗口 main_window.show() # sys.exit()是Python退出進(jìn)程的函數(shù) # QApplication.exec_()的功能是“qt程序進(jìn)入主循環(huán),直到exit()被調(diào)用” # 沒有exec_()的話,程序不會進(jìn)入主循環(huán),會閃退。沒有sys.exit()的話,程序退出后進(jìn)程不會結(jié)束。 sys.exit(app.exec_())
從線程Thread模塊
from PyQt5.QtCore import QThread, pyqtSignal from functools import partial class New_Thread(QThread): # 聲明定義信號,注意它必須是類屬性。 mySignal = pyqtSignal(int,str) # 后面的參數(shù)是信號的數(shù)據(jù)類型 finishedSignal = pyqtSignal() # 線程完成的信號 def __init__(self) -> None: super(New_Thread, self).__init__(parent) # run()是父類的方法,這里要重寫run方法 # 將邏輯代碼放在run里面,當(dāng)主線程調(diào)用thead.start()時會自動運(yùn)行run函數(shù)。 def run(self): # 省略一萬行代碼 self.finishedSignal.emit() # 停止線程 def stop(self): self.isRunning = False # isRunning是父類的屬性,可以停止線程。
參考文章
到此這篇關(guān)于Pyqt5實(shí)現(xiàn)多線程程序的使用示例的文章就介紹到這了,更多相關(guān)Pyqt5多線程內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- 詳解PyQt5中Thread多線程的使用
- Python Pyqt5多線程更新UI代碼實(shí)例(防止界面卡死)
- Pyqt5 實(shí)現(xiàn)多線程文件搜索的案例
- PyQt5多線程防卡死和多窗口用法的實(shí)現(xiàn)
- PyQt5中多線程模塊QThread使用方法的實(shí)現(xiàn)
- PYQT5開啟多個線程和窗口,多線程與多窗口的交互實(shí)例
- 淺談PyQt5中異步刷新UI和Python多線程總結(jié)
- PyQt5多線程刷新界面防假死示例
- python3+PyQt5 創(chuàng)建多線程網(wǎng)絡(luò)應(yīng)用-TCP客戶端和TCP服務(wù)器實(shí)例
- 對pyqt5多線程正確的開啟姿勢詳解
- PyQt5 pyqt多線程操作入門
- python3+PyQt5實(shí)現(xiàn)支持多線程的頁面索引器應(yīng)用程序
相關(guān)文章
js對數(shù)組中的數(shù)字從小到大排序?qū)崿F(xiàn)代碼
對數(shù)組中的數(shù)字從小到大排序,很多時候需要用的多,需要的朋友可以參考下2012-09-09layui動態(tài)渲染生成select的option值方法
今天小編就為大家分享一篇layui動態(tài)渲染生成select的option值方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-09-09微信小程序?qū)崿F(xiàn)下拉刷新和上拉分頁效果的方法詳解
這篇文章主要為大家詳細(xì)介紹了微信小程序動畫是如何實(shí)現(xiàn)下拉刷新和上拉分頁效果的,文中示例代碼講解詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-06-06TypeScript學(xué)習(xí)之強(qiáng)制類型的轉(zhuǎn)換
眾所周知TypeScript是一種由微軟開發(fā)的自由和開源的編程語言。它是JavaScript的一個超集,而且本質(zhì)上向這個語言添加了可選的靜態(tài)類型和基于類的面向?qū)ο缶幊?,下面這篇文章主要介紹了TypeScript中強(qiáng)制類型的轉(zhuǎn)換,需要的朋友可以參考借鑒下。2016-12-12小程序顯示彈窗時禁止下層的內(nèi)容滾動實(shí)現(xiàn)方法
這篇文章主要介紹了小程序顯示彈窗時禁止下層的內(nèi)容滾動實(shí)現(xiàn)方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-03-03js中字符串編碼函數(shù)escape()、encodeURI()、encodeURIComponent()區(qū)別詳解
JavaScript中有三個可以對字符串編碼的函數(shù),分別是: escape,encodeURI,encodeURIComponent,相應(yīng)3個解碼函數(shù):unescape,decodeURI,decodeURIComponent 。接下來通過本文給大家介紹三者之家的區(qū)別,感興趣的朋友一起學(xué)習(xí)吧2016-04-04js實(shí)現(xiàn)背景圖自適應(yīng)窗口大小
本文分享了背景圖自適應(yīng)窗口大小的實(shí)例代碼,具有一定的參考作用。下面跟著小編一起來看下吧2017-01-01