以往的程式設計,在 UI 的控制上,大多是使用 C++ 的 Interface 或是註冊 Callback Function 的方式,將元件的動作和程序串連在一起,如 Visual Studio 裡的 Click
這種方式蠻直覺的,在使用上可以很快的入手,需要在元件上有動作時,只要在相對應的 Event 上填上函式名稱即可。
而 QT 裡對於跨物件的溝通,設計了另一種方式:Signal and Slot,個人覺的是更通用,且更有彈性,但在學習初期,有點小麻煩,尤其是使用 Creator 來建立 UI 時,對於 Signal and Slot 的使用邏輯常常會搞混。
以前一個 Hello World 為例,在完全不用寫 Code 的情況下,畫面上放了一個 Button
但此時的 Hello World 的 Button 是沒有作用的,若希望在按下後能有動作,則需要利用 Signal and Slot 的方式來串連後續的動作
先回到 UI 的設計表單,在 Button 上點右鍵,然後選擇 "跳到信號槽" ,其實就是英文的 Signal
選擇後會出現此元件目前擁有的 Signal
再選擇要串連的 Signal ,例如選擇 clicked() <== 被按下後的信號
此時 Creator 就會創建出新的函式,就是所謂的 Slot,然後編譯時就直接將 Signal 和 Slot 串連
建立的規則為
A:主要 Class 的名稱,因為目前的 Class 是 MainWindow ,所以就會把這個 Slot 依附在 MainWindow 裡
B:此元件的名稱,目前使用 Button 元件的初使名稱,所以會以 pushButton 為名
C:此元件動作 (Signal) 的名稱
在這函式裡印出 "Hello World Button be Clicked" 的訊息,來試試看是否能正常運作。
執行後,按下 Hello World Button,可以看到訊息輸出
如此就能透過 UI 上的動作來執行程序,進行後續的動作。
就這麼簡單。
但為何我一開始會說,在使用上會有些混亂,主要的原因是,用這個方式,功能是正常的,但從程式裡,看不到 Signal 和 Slot 的串連方式。
查了一下 QT Signal / Slot 的使用方式,正常應該還有一個 connect 以及一個 emit
官網上的小例子
建立一個 Counter 物件,然後再宣告 slot 和 signal
當setValue 被使用時(目的為,當這個物件的值被修改時),就會靠 emit 產生 valueChanged 的信號通知 connect 到這個信號的 slot,所以在使用上會有一個 connect 的連接動作
這流程很明確,也很清楚,但透過 Creator 建立的 Button 元件,在串連上就沒那麼清楚了,雖然在使用上沒有影響,但基於程式是不會無中生有的,所以,就試著去了解看看,到底 Creator 做了什麼事。
從mainwindow.ui 裡可以看到 pushButton 被定義
然後,就沒有了……XD
再去翻翻看專案底下是否還有其他檔案
嗯,就這麼單純…….XD
HelloWorld.pro 是專案檔,用來記錄這個專案裡的所有檔案以及相關編譯參數
而另一個檔案 HelloWorld.pro.user 是用來記錄Qt環境的設定。
那要尋找的 connect 到底在那裡呢?
既然編譯完的執行檔能正常運作,那肯定會有中間不為人知的檔案一同編譯,不然不合理啊…
所以,就直衝編譯時產生的目錄下看看
果然看到要編譯 UI 時生成的中間檔案 ui_mainwindow.h
從名字猜測,應該是針對有註冊的 Slot 來㝷找相對應物件的信號,然後進行串連
再往下一層目錄看看是否還有更明顯的資訊
果然在 moc_mainwindow.cpp 裡找到明確的流程了
這樣就串起來了…
當然,中間還有許多 QT 內部的流程,但這部份就太細節了,至少能夠看到明確的函式呼叫,至少就比較踏實了,不過,這似乎也証明了一件事,就是 slot 的名字不能亂取,因為 QT 是利用函式名來串連元件的信號的,因此,就來實驗看看是否真的如此。
把原本的 slot "on_pushButton_clicked" 改成 "on_pushButton_new_clicked"
編譯時出現了一行訊息
看樣子沒錯,QT是利用 slot 去找對應的元件,現在找不到 pushButton_new 這個元件
但還是能正常執行
不過,按鈕就沒有反應了….
經由這一連串的分析以及實驗,應該就比較能理解 QT 裡元件的 Signal 是怎麼串連到 Slot 了…
後續在使用上,真的就比較踏實了,並且在 debug 的分析上,也能有明確的方向。
沒有留言:
張貼留言