一、QThread 的背景和概念
Python語言下的GUI編程常用的庫是PyQt5,而其中的QThread則是經常用到的一個類。QThread是封裝了線程(thread)的Qt庫中的類。線程是我們常說的一個程序中的獨立流程,它是一個包含指令序列的執行單元,是一個進程內部的一個相對獨立的、可調度的執行單元。有了線程可以實現多任務並行執行,提高程序的執行效率。但實現多線程程序不是一件容易的事情,有許多難以解決的問題,而PyQt5中的QThread類就提供了一個相對較簡單的線程實現方式。
在使用QThread之前,需要了解一些與多線程有關的概念,包括:sleep、信號(Signal)和槽(Slot)等概念。sleep是使線程進入睡眠狀態,暫停執行一段時間後重新執行。信號和槽是在不同線程間通訊的機制,其中信號是在發生某個特定事件時發送的一種消息,而槽則是由線程處理該消息並執行相應的動作。
import time, threading
def func():
for i in range(5):
time.sleep(1)
print('func executed')
t = threading.Thread(target=func)
t.start()
二、QThread 的基本用法
QThread類的應用可以分為兩步:第一步是創建QThread對象,第二步是在QThread對象中創建一個自定義的類繼承QThread,並重寫其run方法。run方法就是我們想要在線程中執行的代碼。在自定義類中可以使用信號和槽機制與主線程(或其他線程)交互。
下面是一個簡單的QThread使用示例,先看代碼:
import sys
from PyQt5.QtCore import QThread, pyqtSignal
from PyQt5.QtWidgets import QApplication, QWidget, QLabel
class Worker(QThread):
signal_output = pyqtSignal(str)
def __init__(self, parent=None):
super(Worker, self).__init__(parent)
def run(self):
for i in range(3):
self.signal_output.emit(f"Worker thread output {i}")
self.sleep(1)
class MainWidget(QWidget):
def __init__(self, parent=None):
super(MainWidget, self).__init__(parent)
self.label = QLabel(self)
self.worker = Worker(self)
self.worker.signal_output.connect(self.handle_output)
self.worker.start()
def handle_output(self, msg):
self.label.setText(msg)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = MainWidget()
w.show()
sys.exit(app.exec_())
這段代碼的功能是創建一個主線程的QWidget窗口,其中包含一個QLabel標籤。在程序運行時,會啟動一個子線程(即Worker類),每隔一秒發送一個信號並emit(激活)一個自定義的信號signal_output,發送內容是字符串“Worker thread output i”,其中 i 從 0 開始每次加 1。而界面則會在接收到信號後把字符串顯示到QLabel標籤上。
三、QThread 的高級應用
除了基本的使用外,QThread還有一些高級用法,例如如何優雅地停止線程、如何在線程中使用QTimer、QThreadPool等等。
下面是一個使用QTimer的例子,在子線程中每隔1秒將數據+1並輸出,同時還有一個按下按鈕可以暫停線程:
import sys
from PyQt5.QtCore import QThread, QTimer, pyqtSlot, pyqtSignal
from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QVBoxLayout, QPushButton
class Worker(QThread):
signal_output = pyqtSignal(int)
def __init__(self, parent=None):
super(Worker, self).__init__(parent)
self.timer = QTimer(self)
self.timer.timeout.connect(self._process_data)
self.iteration = 0
self.is_running = True
def _process_data(self):
self.iteration += 1
self.signal_output.emit(self.iteration)
def run(self):
self.timer.start(1000)
while self.is_running:
self.sleep(1)
self.timer.stop()
def stop(self):
self.is_running = False
class MainWidget(QWidget):
def __init__(self, parent=None):
super(MainWidget, self).__init__(parent)
self.label = QLabel(self)
self.button = QPushButton("Stop", self)
self.layout = QVBoxLayout(self)
self.layout.addWidget(self.label)
self.layout.addWidget(self.button)
self.worker = Worker(self)
self.worker.signal_output.connect(self.handle_output)
self.button.clicked.connect(self.worker.stop)
self.worker.start()
@pyqtSlot(int)
def handle_output(self, value):
self.label.setText(f"Worker thread output {value}")
if __name__ == '__main__':
app = QApplication(sys.argv)
w = MainWidget()
w.show()
sys.exit(app.exec_())
這段代碼採用QTimer作為定時器,每隔1秒觸發一次_process_data函數,將iteration的數值加1,並將數值emit到自定義的信號signal_output中。同時,在外部有一個按鈕可以調用worker的stop方法,以便優雅地停止線程。
四、總結
QThread是PyQt5庫中常用的多線程編程類,可以輕鬆地實現多線程效果。在應用QThread時需要注意一些線程概念和信號槽機制,並且如果需要更高級的應用(例如使用QTimer、QThreadPool等)則需要對QThread類更加深入地了解。
原創文章,作者:JHLE,如若轉載,請註明出處:https://www.506064.com/zh-hant/n/141651.html