文章

自定义信号和槽

信号链式传递是信号触发槽再发信号,实现多级响应;信号转发是接收信号后原样或处理后发出新信号,解耦模块通信。

自定义信号和槽

自定义信号和槽

信号链式传递

在 Qt 中,信号链式传递(Signal Chaining / Signal Relay)是指:

一个对象接收到信号后,在对应的槽函数中再次发出另一个信号,从而实现多个对象之间信号的逐级传递响应链式处理

这种模式可以让多个模块间解耦通信,适用于状态同步、事件广播、控制流分层等复杂场景。

基础示意图:

1
2
3
4
5
6
7
Object A  ---emit---> signalA()
                  |
                  v
Object B  <--slotA()--- 接收信号后 emit signalB()
                  |
                  v
Object C  <--slotB()--- 最终处理

自定义信号示例

widget.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE

class Widget : public QWidget {
    Q_OBJECT

public:
    Widget(QWidget* parent = nullptr);
    ~Widget();

signals: // 信号默认强制规定为公有类型,这样才能保证其他对象能接收到信号
    void sendMsg(QString str); // 信号只需要声明,而不要写实体代码

public slots:
    void buttonClicked();

private:
    Ui::Widget* ui;
};
#endif // WIDGET_H

widget.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include "widget.h"
#include "./ui_widget.h"

Widget::Widget(QWidget* parent) : QWidget(parent), ui(new Ui::Widget) {
    ui->setupUi(this);
    // 关联按钮的点击事件到 buttonClicked
    connect(ui->pushButton, &QPushButton::clicked, this, &Widget::buttonClicked);
}

Widget::~Widget() {
    delete ui;
}

void Widget::buttonClicked(){
    // 用 emit 发信号
    emit sendMsg(tr("这是发送的信息"));
}

点击按钮后会发送一个自定义的信号 sendMsg(QString str)

showmsg.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#ifndef SHOWMSG_H
#define SHOWMSG_H

#include <QObject>

class ShowMsg : public QObject {
    Q_OBJECT
public:
    explicit ShowMsg(QObject* parent = nullptr);

public slots:
    void recvMsg(QString str);
};

#endif // SHOWMSG_H

showmsg.cpp

1
2
3
4
5
6
7
8
9
10
#include "showmsg.h"
#include <QMessageBox>

ShowMsg::ShowMsg(QObject* parent) : QObject{parent} {
}

void ShowMsg::recvMsg(QString str){
    // 第一个参数是父窗口指针,设置为 NULL,代表没有父窗口,就是在系统桌面直接弹窗
    QMessageBox::information(NULL, tr("Show"), str);
}

recvMsg(QString str) 负责接收信号并弹窗。

main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include "showmsg.h"
#include "widget.h"

#include <QApplication>

int main(int argc, char* argv[]) {
    QApplication a(argc, argv);
    Widget w;

    ShowMsg s;
    // 关联,信号里的字符串参数会自动传递给槽函数
    // 在 main 函数里,需要手动加 QObject:: 前缀来调用 connect 函数
    QObject::connect(&w, &Widget::sendMsg, &s, &ShowMsg::recvMsg);

    w.show();
    return a.exec();
}

信号转发

在 Qt 中,信号转发(Signal Forwarding)是指:

一个对象接收到信号后,不做任何业务逻辑处理,而是原样或稍作处理后再次发出一个信号,从而把事件“转发”给其他对象或上层模块。

这是一种常用的“事件中继机制”,能让对象之间保持良好的解耦

方式一:信号直接转发信号(推荐)

1
connect(sender, &Sender::signalA, this, &ThisClass::signalB);

要求:signalAsignalB 的参数 完全一致

方式二:槽函数中转发出另一个信号(可以做参数处理)

如果想对信号参数稍作处理,必须手写槽函数,再用 emit 发出新信号。

1
2
3
4
5
6
7
8
9
10
11
class Forwarder : public QObject {
    Q_OBJECT
public slots:
    void onSignalA(int value) {
        // 做一点处理,然后发出另一个信号
        emit signalB(value + 1);
    }

signals:
    void signalB(int value);
};

方式三:lambda 转发(用于临时逻辑、轻量场景)

在 C++11 及以上,可以用 lambda 捕获参数,转发出去:

1
2
3
QObject::connect(&objA, &A::signalA, [&objB](int v) {
    emit objB.signalB(v);  // 假设 signalB 是 public 信号
});

信号直接转发信号示例

widget.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE

class Widget : public QWidget {
    Q_OBJECT

public:
    Widget(QWidget* parent = nullptr);
    ~Widget();

signals:
    void sendVoid(); // 没有参数,所以能和按钮的 clicked 信号匹配,实现信号到信号的关联

private:
    Ui::Widget* ui;
};
#endif // WIDGET_H

widget.cpp

1
2
3
4
5
6
7
8
9
10
11
12
#include "widget.h"
#include "./ui_widget.h"

Widget::Widget(QWidget* parent) : QWidget(parent), ui(new Ui::Widget) {
    ui->setupUi(this);
    // 第四个参数是信号,直接关联到自定义的信号,而不需要槽函数中转
    connect(ui->pushButton, &QPushButton::clicked, this, &Widget::sendVoid);
}

Widget::~Widget() {
    delete ui;
}

showvoid.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#ifndef SHOWVOID_H
#define SHOWVOID_H

#include <QObject>

class ShowVoid : public QObject {
    Q_OBJECT
public:
    explicit ShowVoid(QObject* parent = nullptr);

signals:

public slots:
    void recvVoid();
};

#endif // SHOWVOID_H

showvoid.cpp

1
2
3
4
5
6
7
8
9
#include "showvoid.h"
#include <QMessageBox>

ShowVoid::ShowVoid(QObject* parent) : QObject{parent} {
}

void ShowVoid::recvVoid(){
    QMessageBox::information(NULL, tr("Show"), tr("Just void."));
}

main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include "showvoid.h"
#include "widget.h"

#include <QApplication>

int main(int argc, char* argv[]) {
    QApplication a(argc, argv);
    Widget w;

    ShowVoid s;
    // clicked 发给 sendVoid,sendVoid 再发给 recvVoid
    QObject::connect(&w, &Widget::sendVoid, &s, &ShowVoid::recvVoid);

    w.show();
    return a.exec();
}
本文由作者按照 CC BY 4.0 进行授权