執行緒與回調

      在〈執行緒與回調〉中尚無留言

執行緒的觀念不太好懂,本例以左右二個 Label,同時執行計數器的功能。左邊每隔 10 ms 計數一次,右邊每隔 100 ms 計數一次。

UI 介面下載

請使用 Qt Designer 設計如下 UI 畫面,或由 ui_thread.ui 下載,然後置於專案下的 ui 目錄下。

Thread 執行緒

Thread 中文翻譯為執行緒,每支程式最少會有一個執行緒,最多會有上千上萬個執行緒。

可以把每支程式當成是一間公司公司的第一個員工就是老闆,又稱為主執行緒,而其它的員工,就是新執行緒

每個視窗程式就是一間公司,老闆 (主執行緒) 負責視窗的監控,比如滑鼠的位置,或是按下 “x” 時關閉視窗,而主執行緒最煩重的任務就是更新視窗的畫面。

啟動新執行緒

在 MainWindow 中,就是由主執行緒負責視窗,裏面的 btn_click 方法,產生 CountThread 物件 self.t1,然後設定新執行緒 self.t1.callback 回傳訊息時要執行的方法 self.t1_callback。

最後要使用 t1.start() 啟動新執行執行 t1 的 run 方法。請注意,雖說 t1.run() 也可以執行,但這是使用主執行緒執行 run,而不是使用新執行緒。

class MainWindow(QMainWindow, Ui_MainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setupUi(self)
        self.btn.clicked.connect(self.btn_click)
    def btn_click(self):
        self.t1=CountThread()
        self.t1.callback.connect(self.t1_callback)
        self.t1.start()
    def t1_callback(self, msg):
        self.lbl1.setText(msg)
    def closeEvent(self, event):
        self.t1.runFlag=False
        self.t2.runFlag=False
        time.sleep(1)
        print("close window")
app=QApplication(sys.argv)
mainWindow=MainWindow()
mainWindow.show()
app.exec()

CounterThread

新執行緒要執行的任務寫在 CounterThread 的 run 方法中。

在類別中有一個 callback 類別變數,可以把此變數當成是一支電話,此電話由 Signal() 產生。當執行建構子時,在建構子內會將此類別變數轉成物件變數,然後賦予 emit 的功能。所以在 run 方法中,就需以 self.callback 來調用,然後用 emit 方法將訊息傳回主執行緒。

from PySide6.QtCore import QThread, Signal
class CountThread(QThread):
    callback=Signal(object)
    def __init__(self, parent=None):
        super().__init__(parent)
        self.runFlag=True
    def run(self):
        index=0
        while self.runFlag:
            index+=1
            self.callback.emit(str(index))
            self.msleep(100)
    def close(self):
        self.runFlag=False

主程式完整代碼

本例的主程式 MainWindow.py 完整代碼如下

import sys
import time

from PySide6.QtWidgets import QMainWindow, QApplication
from CountThread import CountThread
from ui.ui_thread import Ui_MainWindow
class MainWindow(QMainWindow, Ui_MainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setupUi(self)
        self.btn.clicked.connect(self.btn_click)
    def btn_click(self):
        if self.btn.text()=="開始":
            self.btn.setText("結束")
            self.t1=CountThread()
            self.t1.callback.connect(self.t1_callback)
            self.t1.start()
            self.t2=CountThread(delay=100)
            self.t2.callback.connect(self.t2_callback)
            self.t2.start()
        else:
            self.btn.setEnabled(False)
            self.t1.close()
            self.t2.close()
            time.sleep(1)
            self.btn.setText("開始")
            self.btn.setEnabled(True)
    def t1_callback(self, msg):
        if self.t1.runFlag: #防止最後一次回調
            self.lbl1.setText(msg)
    def t2_callback(self, msg):
        if self.t2.runFlag:
            self.lbl2.setText(msg)
    def closeEvent(self, event):
        self.t1.close()
        self.t2.close()
        time.sleep(1)
        print("close window")
app=QApplication(sys.argv)
mainWindow=MainWindow()
mainWindow.show()
app.exec()

CountThread 完整代碼

CountThread.py 的完整代碼如下

from PySide6.QtCore import QThread, Signal
class CountThread(QThread):
    callback=Signal(object)
    def __init__(self, parent=None, delay=10):
        super().__init__(parent)
        self.delay=delay
        self.runFlag=True
    def run(self):
        index=0
        while self.runFlag:
            index+=1
            self.callback.emit(str(index))
            self.msleep(self.delay)
    def close(self):
        self.runFlag=False

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *