Pyqt5實(shí)現(xiàn)多線(xiàn)程程序的使用示例
主從架構(gòu)
Pyqt常常使用**主從架構(gòu)(Master-Workers 架構(gòu))**來(lái)避免界面卡死的情況。
Master-Workers 架構(gòu)就像它的名字,一個(gè)master統(tǒng)領(lǐng)著幾個(gè)workers一起干活。其中某個(gè)worker倒下了不會(huì)導(dǎo)致整體任務(wù)失敗。matser不用干活,因此可以專(zhuān)心指揮workers。
在qt5中,master代表主線(xiàn)程,主要維持主界面的運(yùn)行。當(dāng)觸發(fā)某項(xiàng)耗時(shí)耗力的任務(wù)時(shí),主線(xiàn)程將這項(xiàng)任務(wù)分配給其他線(xiàn)程(workers)來(lái)做。其他線(xiàn)程出現(xiàn)了災(zāi)難性的錯(cuò)誤,不會(huì)影響到主線(xiàn)程,因此程序不會(huì)完全崩潰。且主線(xiàn)程不承擔(dān)耗時(shí)耗力的任務(wù),因此避免了復(fù)雜運(yùn)算時(shí)主界面卡頓的問(wèn)題。
進(jìn)程和線(xiàn)程詳見(jiàn)1
pyqt5多線(xiàn)程的架構(gòu)一般由三個(gè)模塊實(shí)現(xiàn):
Gui.py:只存放GUI界面,一般是Qt Designer生成的代碼,無(wú)需做任何修改Thread.py:從線(xiàn)程,主要的邏輯代碼都放在這里。接收主線(xiàn)程的指令,并向主線(xiàn)程返回信號(hào)。Main.py:主線(xiàn)程,負(fù)責(zé)運(yùn)行GUI界面,向從線(xiàn)程發(fā)送指令并接收從線(xiàn)程返回的信號(hào)。
GUI模塊
Qt Designer保存的文件為Gui.ui的格式,使用下面命令轉(zhuǎn)為Gui.py。
pyuic5 Gui.ui -o Gui.py
轉(zhuǎn)換后的代碼結(jié)構(gòu)如下,我們不需要對(duì)這個(gè)代碼做任何修改。
# -*- 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): # 注意這個(gè)類(lèi)名,后面在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)題"))
# ------- 省略很多行... ------- #
不同線(xiàn)程間的信號(hào)與槽
在討論主線(xiàn)程和從線(xiàn)程之前,首先要明確線(xiàn)程間傳遞信號(hào)的方法2。

主 -> 從
主線(xiàn)程到從線(xiàn)程的信號(hào)就是最基本信號(hào)與槽的機(jī)制,使用槽函數(shù)來(lái)與從線(xiàn)程通信。一般是Wight被clicked,然后觸發(fā)槽函數(shù)。傳遞的路徑為:1.觸發(fā)信號(hào)事件 -> 2.信號(hào)clicked -> 3.槽函數(shù)接收信號(hào)并運(yùn)行。使用以下語(yǔ)句綁定槽函數(shù):
from Thread import New_thread # 從線(xiàn)程的引用
class MainWindow(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None) -> None:
super(MainWindow,self).__init__(parent)
self.setupUi(self)
# ----------------------------------------- #
# ↓↓↓↓↓↓↓ 不用管上面的代碼,主要看下面 ↓↓↓↓↓↓↓ #
# ----------------------------------------- #
self.thread = None # 先預(yù)定義一個(gè)從線(xiàn)程的實(shí)例屬性,這里無(wú)需將從線(xiàn)程實(shí)例化
# self.btn為Ui_MainWindow中定義的按鈕,這里將按鈕點(diǎn)擊的信號(hào)與槽函數(shù)self.func連接起來(lái)
self.btn.clicked.connect(self.func)
def func(self):
self.thread = New_thread() # 在槽函數(shù)中實(shí)例化從線(xiàn)程,然后就可以操作從線(xiàn)程了
self.thread.start()
pass
有時(shí)我們需要向槽函數(shù)傳遞參數(shù),一般使用偏函數(shù)或lambda,偏函數(shù)可參見(jiàn)3:
from functools import partial # 偏函數(shù)的引用
from Thread import New_thread # 從線(xiàn)程的引用
class MainWindow(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None) -> None:
super(MainWindow,self).__init__(parent)
self.setupUi(self)
# ----------------------------------------- #
# ↓↓↓↓↓↓↓ 不用管上面的代碼,主要看下面 ↓↓↓↓↓↓↓ #
# ----------------------------------------- #
self.thread = None # 先預(yù)定義一個(gè)從線(xiàn)程的實(shí)例屬性,這里無(wú)需將從線(xiàn)程實(shí)例化
# 第一種方法:這個(gè)槽函數(shù)被寫(xiě)成了偏函數(shù)的形式 partial(self.func, param1, param2)
self.btn1.clicked.connect(partial(self.func, param1=1, param2=2))
# 第二種方法:這個(gè)槽函數(shù)被寫(xiě)成了lambda的形式 lambda:self.func(param1=1, param2=2)
self.btn2.clicked.connect(lambda:self.func(param1=1, param2=2))
# 這個(gè)槽函數(shù)監(jiān)聽(tīng)了兩個(gè)信號(hào)哦
def func(self, param1, param2):
# 在槽函數(shù)中實(shí)例化從線(xiàn)程,然后就可以操作從線(xiàn)程了
self.thread = New_thread(param1, param2) # 從線(xiàn)程實(shí)例化也可以放在__init__里面,但我喜歡放在這。
self.thread.start()
pass
從 -> 主
從線(xiàn)程向主線(xiàn)程傳遞信號(hào)一般使用自定義信號(hào),觸發(fā)后,從線(xiàn)程的自定義信號(hào)傳遞給主線(xiàn)程連接的槽函數(shù)。觸發(fā)的路徑為1.從線(xiàn)程觸發(fā)信號(hào)emit -> 2.聲明信號(hào)pyqtSignal -> 3.傳遞給主線(xiàn)程連接的槽函數(shù)。使用下面代碼建立自定義信號(hào)。
先在Thread.py中定義信號(hào):
# Thread.py
from PyQt5.QtCore import QThread, pyqtSignal
class New_Thread(QThread):
# 聲明定義信號(hào),注意它必須是類(lèi)屬性。
mySignal = pyqtSignal(int,str) # 后面的參數(shù)是信號(hào)的數(shù)據(jù)類(lèi)型
def __init__(self) -> None:
super(New_Thread, self).__init__(parent)
pass
def run(self):
pass
# ------------------------------------------ #
# ↓↓↓↓↓↓↓ 上面的兩個(gè)函數(shù)不用理會(huì),看下面 ↓↓↓↓↓↓↓ #
# ------------------------------------------ #
# 下面是從線(xiàn)程的邏輯代碼
def func(self):
# ------- 省略很多邏輯代碼... ------- #
# 向主線(xiàn)程發(fā)送信號(hào)
self.mySignal.emit(1,"Hello, Pyqt5")
在主線(xiàn)程Main.py中監(jiān)聽(tīng)信號(hào)并連接到槽:
# Main.py
from Thread import New_thread # 從線(xiàn)程的引用
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() # 從線(xiàn)程實(shí)例化(也可以放在__init__里面)
self.thread.start()
# 監(jiān)聽(tīng)從線(xiàn)程發(fā)出的信號(hào),并連接到槽函數(shù)func2
# 記得嗎?mySignal發(fā)出了兩個(gè)數(shù)據(jù),一個(gè)是int類(lèi)型,一個(gè)str類(lèi)型
self.thread.mySignal.connect(self.func2)
# 槽函數(shù)接收了從線(xiàn)程的信號(hào)
def func2(self, param1:int, param2:str):
pass
主線(xiàn)程Main模塊
主線(xiàn)程的作用是維護(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 # 從線(xiàn)程的引用
# 第一個(gè)父類(lèi)是PyQt5.QtWidgets.QMainWindow(取決于你在Qt Designer選擇的窗口類(lèi)型)
# 第二個(gè)父類(lèi)是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ù)定義一個(gè)從線(xiàn)程的實(shí)例屬性,這里無(wú)需將從線(xiàn)程實(shí)例化
self.btn1.clicked.connect(self.func1) # 綁定控件的槽函數(shù),以啟動(dòng)從線(xiàn)程
self.thread.finished.connect(self.func3) # 監(jiān)聽(tīng)線(xiàn)程是否完成任務(wù),以結(jié)束從線(xiàn)程
# 省略一萬(wàn)行綁定槽函數(shù)的代碼...
# 定義槽函數(shù),這里可以放入從線(xiàn)程。
def func1(self):
self.thread = New_Thread() # 實(shí)例化一個(gè)從函數(shù)
self.thread.start()
self.thread.mySignal.connect(self.func2) # 監(jiān)聽(tīng)從線(xiàn)程的信號(hào),并綁定槽函數(shù)
# 定義響應(yīng)從線(xiàn)程信號(hào)的槽函數(shù)
def func2(self,param:int):
pass
# 定義結(jié)束從線(xiàn)程的槽函數(shù)
def func3(self):
self.thread.stop()
# 省略一萬(wàn)個(gè)槽函數(shù)...
if __name__ == '__main__':
# 任何一個(gè)qt應(yīng)用都必須有且僅有一個(gè)QApplication對(duì)象
# sys.argv是一組命令行參數(shù)的列表。
# 這行代碼就是實(shí)例化一個(gè)QApplication
app = QApplication(sys.argv)
# 主線(xiàn)程實(shí)例化
main_window = MainWindow()
# 顯示窗口
main_window.show()
# sys.exit()是Python退出進(jìn)程的函數(shù)
# QApplication.exec_()的功能是“qt程序進(jìn)入主循環(huán),直到exit()被調(diào)用”
# 沒(méi)有exec_()的話(huà),程序不會(huì)進(jìn)入主循環(huán),會(huì)閃退。沒(méi)有sys.exit()的話(huà),程序退出后進(jìn)程不會(huì)結(jié)束。
sys.exit(app.exec_())
從線(xiàn)程Thread模塊
from PyQt5.QtCore import QThread, pyqtSignal
from functools import partial
class New_Thread(QThread):
# 聲明定義信號(hào),注意它必須是類(lèi)屬性。
mySignal = pyqtSignal(int,str) # 后面的參數(shù)是信號(hào)的數(shù)據(jù)類(lèi)型
finishedSignal = pyqtSignal() # 線(xiàn)程完成的信號(hào)
def __init__(self) -> None:
super(New_Thread, self).__init__(parent)
# run()是父類(lèi)的方法,這里要重寫(xiě)run方法
# 將邏輯代碼放在run里面,當(dāng)主線(xiàn)程調(diào)用thead.start()時(shí)會(huì)自動(dòng)運(yùn)行run函數(shù)。
def run(self):
# 省略一萬(wàn)行代碼
self.finishedSignal.emit()
# 停止線(xiàn)程
def stop(self):
self.isRunning = False # isRunning是父類(lèi)的屬性,可以停止線(xiàn)程。參考文章
到此這篇關(guān)于Pyqt5實(shí)現(xiàn)多線(xiàn)程程序的使用示例的文章就介紹到這了,更多相關(guān)Pyqt5多線(xiàn)程內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- 詳解PyQt5中Thread多線(xiàn)程的使用
- Python Pyqt5多線(xiàn)程更新UI代碼實(shí)例(防止界面卡死)
- Pyqt5 實(shí)現(xiàn)多線(xiàn)程文件搜索的案例
- PyQt5多線(xiàn)程防卡死和多窗口用法的實(shí)現(xiàn)
- PyQt5中多線(xiàn)程模塊QThread使用方法的實(shí)現(xiàn)
- PYQT5開(kāi)啟多個(gè)線(xiàn)程和窗口,多線(xiàn)程與多窗口的交互實(shí)例
- 淺談PyQt5中異步刷新UI和Python多線(xiàn)程總結(jié)
- PyQt5多線(xiàn)程刷新界面防假死示例
- python3+PyQt5 創(chuàng)建多線(xiàn)程網(wǎng)絡(luò)應(yīng)用-TCP客戶(hù)端和TCP服務(wù)器實(shí)例
- 對(duì)pyqt5多線(xiàn)程正確的開(kāi)啟姿勢(shì)詳解
- PyQt5 pyqt多線(xiàn)程操作入門(mén)
- python3+PyQt5實(shí)現(xiàn)支持多線(xiàn)程的頁(yè)面索引器應(yīng)用程序
相關(guān)文章
javascript實(shí)現(xiàn)的textarea運(yùn)行框效果代碼 不用指定id批量指定
今天在寫(xiě)一個(gè)網(wǎng)頁(yè)的時(shí)候用到了N多嵌套在textarea標(biāo)簽里的代碼,定義雙擊運(yùn)行其內(nèi)的代碼段。但是每次創(chuàng)建一個(gè)這樣的可運(yùn)行的實(shí)例都要給textarea元素自定義一個(gè)id值和寫(xiě)入雙擊事件,好不麻煩。2009-12-12
微信小程序使用map組件實(shí)現(xiàn)解析經(jīng)緯度功能示例
這篇文章主要介紹了微信小程序使用map組件實(shí)現(xiàn)解析經(jīng)緯度功能,涉及微信小程序map組件結(jié)合高德地圖進(jìn)行經(jīng)緯度獲取相關(guān)操作技巧,需要的朋友可以參考下2019-01-01
解析Javascript中難以理解的11個(gè)問(wèn)題
這篇文章主要是對(duì)Javascript中難以理解的11個(gè)問(wèn)題進(jìn)行了詳細(xì)的分析介紹,需要的朋友可以過(guò)來(lái)參考下,希望對(duì)大家有所幫助2013-12-12
解決layer.open彈出框不能獲取input框的值為空的問(wèn)題
今天小編就為大家分享一篇解決layer.open彈出框不能獲取input框的值為空的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-09-09
JavaScript語(yǔ)法 JSON序列化之stringify實(shí)例詳解
這篇文章主要為大家介紹了JavaScript語(yǔ)法 JSON序列化之stringify實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10

