1. 路徑計算解決

  2. 程式碼功能簡介

  3. PyQt5 線程 QThread 教學

  4. PyQt5 信號槽教學

  5. PyQt5 進度條教學

  6. 倉儲python-solvespace已更新

書籤:

PyQt 線程

PyQt 信號槽

PyQt 進度條

路徑計算解決

煩惱多日的路徑問題解決了,是靠觀察程式碼產生結果觀察出判斷式有問題。

在 Qt 表格的回傳值中不可以這樣用,因為永遠不會發生:

if table_point.item(i, 3).checkState()==True:
    ...

必須這樣用:

if table_point.item(i, 3).checkState():
    ...

Pyslvs 界面

程式碼 Demo

這個程式碼使用 matplotlib 繪出圖形,最上端可以設定運算的對象。

按下「Copy」鈕可以複製到剪貼簿,「Help」鈕會連到之前寫的程式庫網頁。

PyQt 的剪貼簿用法很簡單:

clipboard = QApplication.clipboard()
clipboard.setText(self.script.toPlainText())

「Save」可以存成 Python 程式碼,執行過確認沒問題。

路徑追蹤

追加解析度功能,預設 5 度,最大 45 度,最小到 0.5 度。

表單也經過篩選,不會加入固定點了。

按下「Apply」鈕後會開始執行運算,而且進度條會顯示運算進度(進度無誤)。

中途隨時可以停止執行,只是一旦停止就會關閉小視窗且刪除暫存資料,要重新計算。

執行完畢後,就會自動關閉視窗,呈現路徑圖。

接著就能調整畫布,用內建的存檔功能存成圖片!

關於路徑追蹤的視窗,其實花了不少功夫研究 Qt 的功能,將 Python 的教學寫在下面。


PyQt 線程

需要執行序的原因是,程式語言通常會將複雜運算的程序優先度提高,相對畫面的處理較簡單,因此會將「複雜運算」處理完後才會更新畫面顯示。

若是不想讓視窗凍結,使用排程功能就十分重要,提醒使用者正在運算,亦可以避免輸入過多指令時,因為正在處理「複雜運算」,而無法理會使用者的呼叫。

線程功能並非平行運算,而是拖慢所有進度,在執行序之間來回處理,以因應多方面需求。

Python3 雖然有內建 threading_thread 的模組,但是還滿難使用與管理的,所以利用 Qt 的功能來替代之。

可以想像成 QThread 是另一個元件,也是使用 class的方式繼承,基本架構如下:

class WorkerThread(QThread):
    #這裡是信號(括弧中填入傳出參數類型,沒有就不用)
    #必須擺外面
    done = pyqtSignal(list)

    #初始化
    def __init__(self, parent = None):
        QThread.__init__(self, parent)
        self.stoped = False
        self.mutex = QMutex()

    #執行序(可以很多個)
    def run(self):
        ...
        #完成,發出信號(帶一個 list 參數)
        self.done.emit(nPath)

    #中止序
    def stop(self):
        with QMutexLocker(self.mutex): self.stoped = True

QThread 跟外界溝通的方式是靠信號跟從外部定值(這樣可以從 self 直接讀值)。

Window 或 Widget 可以如下使用:

class Path_Track_show(QDialog, Ui_Dialog):
    def __init__(self, parent=None):
        super(Path_Track_show, self).__init__(parent)
        ...
        #定義線程
        self.work = WorkerThread()
        #連接啟動信號
        self.buttonBox.button(QDialogButtonBox.Apply).clicked.connect(self.start)
        #連接強制結束訊號
        self.buttonBox.button(QDialogButtonBox.Cancel).clicked.connect(self.stop)
        #連接完成信號
        self.work.done.connect(self.finish)

    #啟動
    def start(self):
        #傳入值(或任何前置作業)
        self.work.Run_list = self.Run_list
        ...
        #啟動
        self.work.start()
        #關閉按鈕(有關GUI項目)
        #會同時執行work和之後的項目
        self.buttonBox.button(QDialogButtonBox.Apply).setEnabled(False)
        ...

    #強制結束
    def stop(self): self.work.stop()

    #完成
    #信號槽接收值(帶一個 list 參數)
    @pyqtSlot(list)
    def finish(self, Path):
        self.Path_data = Path
        #接收後關閉
        self.accept()

PyQt 信號槽

信號是 Qt 元件用來溝通的方式,通常是「函式」使用「信號物件」發送訊息給連結的「信號槽」,而配戴此信號槽的函式就會在接收訊息的時候開始執行。

特別注意 PyQt4 和 PyQt5 的信號連接方式有所不同。

在一個 class 配置「信號物件」:

class a(parentA):
    done = pyqtSignal()

這個物件必須擺在 class 中,不過不行在任何函式裡(包括初始化函式 __init__)。

呼叫方式是跟其他同 class 底下的函式同個位階(如 self.donea.done)。

接著在一個函式配置一個信號槽:

class b(parentB):
    @pyqtSlot()
    def b_1(self):
        ...

然後在需要連接的時段連結它們(亦可以在時機內斷開連結),例如 class b 的初始化:

class b(parentB):
    def __init__(self, parent=None):
        ...
        self.Come_from_A = a()
        self.Come_from_A.done.connect(self.b_1)

最後就是發送信號的時機,剛才的信號物件是 class a 內的函式都可調用的:

class a(parentA):
    def a_1(self):
        ...
        self.done.emit()

這樣 class b 內的 b_1 就會執行了。

這些括弧內都是可以帶入參數的,由於 Qt 是 C++ 寫成的,@pyqtSlot() 的括弧中是填參數類型。


PyQt 進度條

知道線程和信號槽的利用後,處理時控制進度條物件就易如反掌了。

首先在執行前算一下進度條跑滿後的格數(若是迴圈,會在執行一次時加 1,加到滿):

self.progressBar.setRange(0, limit)

接著在線程的初始化中設置一個整數 progress_Signal,從零開始。

self.progress = 0

執行時的迴圈,用另一個函式發送訊息與增加進度值:

def run(self):
    for i in range(_range_):
        self.progress_going()

def progress_going(self):
        self.progress = self.progress+1
        self.progress_Signal.emit(self.progress)

接收端函式(也許是另一個 class):

@pyqtSlot(int)
def progressbar_change(self, val): self.progressBar.setValue(val)

連接(在初始化就可以了):

self.work = WorkerThread()
self.work.progress_Signal.connect(self.progressbar_change)

最重要的功能做完了,最終也學會了難懂的信號槽(以前都是編譯器幫忙產生的),剩下的功能應該也能順利做完。

雖然最終沒有在暑假內完成專題的項目,但是也學會許東西,培養出快速統整資料並架構化的能力。

覺得自己需要多練習語言表達的部份,因為可能統整資料十分明瞭,在口語上卻節奏太快,順著聽者的思路呈現才是最重要的方向。



P.S Roger 用 wordpress 架了一個存文章的網站:http://roger-blog.tk/


Comments

comments powered by Disqus