執行緒的觀念不太好懂,本例以左右二個 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