用 VS Code 搞 Qt6:讓信號和槽自動建立連接

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 函數。

好了,今天的主題咱們就聊到這兒了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章