Qt 具備讓某個物件的訊號與符合要求的槽函數自動建立連線。弄起來也很簡單,只要呼叫這個靜態方法即可:
QMetaObject::connectSlotsByName(...);
connectSlotsByName 方法需要一個引數,此引數的指標指向一個範例,這個範例自身的訊號,以及它的子級物件的訊號都會自動連線。
不過,在用的時候要注意以下幾點,否則 connectSlotsByName 方法是不起作用的。
1、如果類是從某個 QObject 類派生的,比如常見的 QWidget 類,在類的宣告中一個定要加上 Q_OBJECT 宏。這條老周在上一篇中說過,不加這個訊號和槽不能建立連線。
2、物件一定要有 Name,即用 setObjectName 方法設定。雖然物件可以使用重複的名字,但不建議這樣做,因為 connectSlotsByName 方法只要找到一個名字匹配的物件,就會停止查詢。所以,就算你設定了 10 個名為「myButton」 的物件,結果也只能有一個會自動繫結訊號和槽,其他的同名物件會忽略。
3、一定要在所有物件都初始化完畢,包括呼叫 setObjectName 方法設定物件名稱後呼叫 connectSlotsByName 方法。這樣才會有效。
setObjectName 方法用起來很簡單,只要傳遞物件的名字即可,字串型別。名字你可以隨便取。例如
QLabel lb ... lb.setObjectName("bug");
這時候,標籤物件的名字是「bug」。
Slot 要支援被自動連線,函數(方法)也是有嚴格的命名規則的。你必須按照這個規則來,否則不會被識別。槽函數命名規則如下:
on_XXX_SSS
1、以「on」開頭,每一節用下劃線連起來。
2、XXX 是物件名,注意是物件名,就是用 setObjectName 方法設定的名稱,不是你程式碼中定義的變數名。這個得注意,不能搞錯了。
3、SSS 是訊號。
比如,按鈕的 clicked 訊號,你讓要自己寫的槽能夠被自動連線,就得這樣命名槽函數:on_mybtn_clicked。其中,「mybtn」是物件名。
------------------------------------------- 銀河分隔線 ------------------------------------------
下面咱們來動手做個例子,就好理解了。
一、先弄好 CMake 檔案。
cmake_minimum_required(VERSION 3.0.0) project(TestApp VERSION 0.1.0) find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED YES) set(CMAKE_AUTOMOC YES) add_executable(TestApp WIN32 main.cpp app.h app.cpp) target_link_libraries(TestApp PRIVATE Qt6::Core Qt6::Gui Qt6::Widgets)
這個範例有三個檔案。main.cpp 是寫 main 函數的地方。app.h 和 app.cpp 中是咱們自定義的視窗類——從 QWidget 類派生。
二、定義 MyWindow 類,基礎類別是 QWidget。
/** app.h **/ #include <QMetaObject> #include <QWidget> #include <QApplication> #include <QObject> #include <QLabel> #include <QPushButton> #include <QMessageBox> #include <QVBoxLayout> #include <QHBoxLayout> class MyWindow : public QWidget { // 這個宏很容易忘了,忘了就不能連線訊號和槽了 Q_OBJECT public: // 建構函式 MyWindow(QWidget* parent = nullptr); // 解構函式 ~MyWindow(); private: // 私有函數 void initUi(void); // 以下是用到的部件(控制元件) QPushButton *btn1; QPushButton *btn2; QLabel *lb; // 佈局 QVBoxLayout *layout; QHBoxLayout *sublayout; // 這幾個函數是用於自動繫結的槽 private slots: void on_b1_clicked(); void on_b2_clicked(); };
所有 QObject 的子類,想使用 Signal 和 Slot ,必須呼叫 Q_OBJECT 宏。這裡有兩個按鈕,on_b1_clicked 和 on_b2_clicked 都是槽。要讓兩個按鈕自動連線,必須分別設定它的 object name 為 「b1」 和 「b2」。
三、下面是 initUi 函數的實現程式碼,用於初始化表單。
void MyWindow::initUi() { // 按鈕1 btn1 = new QPushButton(this); // 設定按鈕1的文字 btn1 -> setText("左邊"); // 重要:給它個名字 btn1 -> setObjectName("b1"); // 按鈕2 btn2 = new QPushButton(this); // 設定按鈕2的文字 btn2 -> setText("右邊"); // 重要:設定名稱 btn2 -> setObjectName("b2"); // 標籤 lb = new QLabel("請點選下面的按鈕", this); // 佈局 layout = new QVBoxLayout(this); layout -> addWidget(lb, 0, Qt::AlignTop); sublayout = new QHBoxLayout(this); // 新增要佈局的元件 sublayout -> addWidget(btn1); sublayout -> addWidget(btn2); layout->addLayout(sublayout); // 視窗 this -> setWindowTitle("範例王"); this -> resize(240, 100); }
呼叫按鈕物件的 setObjectName 方法就可以為其分配名稱。注意在呼叫 QPushButton 類別建構函式時,要把當前視窗的指標傳遞給 parent 引數,使用按鈕成為 MyWindow 的子級物件。這樣後面才能做訊號與槽的自動連線。
四、在 MyWindow 類建構函式中,先呼叫 initUi ,再呼叫 connectSlotsByName 靜態方法。
MyWindow::MyWindow(QWidget *parent) : QWidget::QWidget(parent) { // 呼叫以下函數,初始化UI initUi(); // 一定要在所有東東都初始化完畢後呼叫才有效 QMetaObject::connectSlotsByName(this); }
五、下面是兩個槽函數的實現。功能簡單,用 QMessageBox 顯示彈出框。
void MyWindow::on_b1_clicked() { QMessageBox::information(this, "好訊息", "左轉是男廁", QMessageBox::Ok); } void MyWindow::on_b2_clicked() { QMessageBox::information(this, "好訊息", "右轉是女廁", QMessageBox::Ok); }
六、在 main.cpp 中寫 main 函數。
#include "app.h" int main(int argc, char *argv[]) { QApplication app(argc, argv); // 範例化視窗 MyWindow wind; // 顯示視窗 wind.show(); // 訊息迴圈 return app.exec(); }
執行結果如下面超清動畫所示。
從結果可以看到,名為「b1」的按鈕自動將 clicked 訊號連線到 on_b1_clicked 函數;名為「b2」的按鈕自動將 clicked 訊號連線到 on_b2_clicked 函數。
好了,今天的主題咱們就聊到這兒了。