亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Pyqt5實(shí)現(xiàn)多線程程序的使用示例

 更新時間:2023年11月23日 15:53:34   作者:唐BiuBiu  
Pyqt常常使用主從架構(gòu)(Master-Workers 架構(gòu))來避免界面卡死的情況,本文主要介紹了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)文章希望大家以后多多支持腳本之家! 

相關(guān)文章

最新評論