搜尋此網誌

2017年2月28日 星期二

PyQt5 教學 Part 4: 執行緒的使用

在前一章我們學習到如何觸發事件,
但是在撰寫視窗的程式時,我們常常會需要一次能夠執行很多不同的功能,
例如我們需要製作一個定期任務,每隔一秒就必須把資訊傳回到主視窗。

接著開始介紹如何使用多執行緒,
依照慣例先去教學文件裡面,尋找可以使用的資源。
Qthread的文件



可以找到三個可用的Public Slots(信號槽)

void quit()
void start()
void terminate()


quit() 終止執行緒
start() 開始執行緒
terminate() 強制終止執行緒

下面我們開始來示範

4.thread.py
import sys
import time
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QLineEdit, QVBoxLayout, QFormLayout, QProgressBar
from PyQt5.QtCore import QThread, pyqtSignal


class TutorialThread(QThread):
        set_max = pyqtSignal(int)
        update = pyqtSignal(int)

        def __init__(self):
                QThread.__init__(self)

        def __del__(self):
                self.wait()

        def run(self):
                self.update.emit(100)
                for index in range(1, 101):
                        self.update.emit(index)
                        time.sleep(0.5)


class MainWindow(QWidget):

        def __init__(self):
                super(self.__class__, self).__init__()
                self.setupUi()
                self._tutorial_thread = TutorialThread()
                self._tutorial_thread.set_max.connect(self.set_max)
                self._tutorial_thread.update.connect(self.set_value)
                self.show()

        def setupUi(self):
                self.setWindowTitle("執行緒的使用")

                self.button_start = QPushButton()
                self.button_start.setText("開始")
                self.button_stop = QPushButton()
                self.button_stop.setText("結束")

                self.progress_bar = QProgressBar()

                self.line = QLineEdit()

                form_layout = QFormLayout()
                form_layout.addRow(self.button_start, self.line)
                form_layout.addRow(self.button_stop)
                form_layout.addRow(self.progress_bar)

                h_layout = QVBoxLayout()
                h_layout.addLayout(form_layout)

                self.setLayout(h_layout)

                self.button_start.clicked.connect(self.start)
                self.button_stop.clicked.connect(self.stop)

        def start(self):
                self.line.setText("觸發後可以修改")
                self._tutorial_thread.start()

        def stop(self):
                self._tutorial_thread.terminate()

        def set_max(self, data):
                self.progress_bar.setMaximum(data)

        def set_value(self, data):
                self.progress_bar.setValue(data)

        if __name__ == "__main__":
                app = QApplication(sys.argv)
                MainWindow = MainWindow()
                sys.exit(app.exec_())





執行結果如下


在這一章節,我們多新增了一個class TutorialThread(類別),還有信號pyqtSignal。
MainWindow 就跟Part 3差不多,只新增了執行緒的物件跟QProgressBar。

所以列一下本節的重點
  • 執行緒的類別繼承
  • 進度條的物件設置
  • 如何新增信號
  • 為何要特別使用執行緒



class TutorialThread(QThread):
    def __init__(self)
        QThread.__init__(self)
    def __del__(self):
        self.wait()
    def run(self):
我們先架構了解Thread的物件,開始了解。
剛開始我們先制定一個類別叫做 TutorialThread 並且他繼承了QThread這個物件,
因此 TutorialThread 就等於是一個有 Thread功能的物件。

在初始化 __init__的地方我們必須先執行QThread.__init__(self)
執行這一行的時候,就會去找 QThread 並且執行裡面的 __init__來做初始化。


在刪除物件的時候會使用到 __del__ 這個保留的函式,
self.wait()的意思是說等待到執行緒執行完畢,
所以組裝起來就是,刪除這個物件時必須等到執行緒執行完畢。

最後 run 的地方就是我們程式要執行的地方了。


set_max = pyqtSignal(int)
update = pyqtSignal(int)

self.update.emit(100)
self.update.emit(index)

再來就是介紹如何新增信號以及信號的運作,
前兩行就是製作信號的變數了,並且在括號內(int)是設定要傳入的參數型態。

後兩行的地方則是發送信號,

因此這兩行就會收到信號了。

self._tutorial_thread.set_max.connect(self.set_max)
self._tutorial_thread.update.connect(self.set_value)


QProgressBar 如果不懂的話,可以去官方文件來對照。

為什麼要使用執行緒?
因為當不使用執行緒在跑迴圈或是抓資料的時候畫面就會卡住,
甚至在連使用 Python 內建的 threading 的時候也是一樣,
這是因為物件會被主畫面給鎖定,因此也不建議在不同的視窗中修改主視窗的物件。

而我在文字觸發的時候,顯示"觸發後可以修改",
其實就是因為在不使用執行緒的時候是無法修改的哦!

註:QThread 的 terminate其實是不建議使用的哦,因為在執行一半的時候會被直接停止,很容易引發 程式的Crush,
因此可以設定一個變數記號跟程式說我要先暫停或是先停止。

本章範例
如果有任何疑問或指教,歡迎底下回覆,謝謝。

PyQt5 教學 Part 3: 信號觸發
PyQt5 教學 Part 5: 信號觸發