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 函數。
好了,今天的主題咱們就聊到這兒了。