QT从入门到精通(二) ——信号与槽机制

Qt 的信号与槽机制 (Signal and Slot)是 Qt 框架 中用于对象间通信的核心机制之一。它允许对象之间进行松耦合的事件驱动式通信,尤其适合 GUI 应用程序 中的事件处理。


1. 基本概念

信号 (Signal)

  • 当对象的状态发生变化时,它会发出一个信号
  • 信号是一种声明,不需要实现。
  • 信号不需要知道谁会接收它,也就是松耦合的设计。

槽 (Slot)

  • 槽是一个普通的函数,可以用来接收信号。
  • 当信号发出时,与之关联的槽函数将被自动调用。
  • 槽可以是成员函数、普通函数或 lambda 表达式。

连接 (Connect)

  • 信号和槽通过 QObject::connect() 进行连接。
  • 当信号发出时,Qt 会自动调用与其连接的槽。

2. 信号与槽的工作原理

Qt 的信号与槽机制基于 元对象系统(Meta-Object System),它使用以下特性:

  • Q_OBJECT 宏:类中必须包含此宏,表示该类支持信号与槽。
  • moc 工具:Qt 的元对象编译器,会处理信号与槽相关的代码。

Qt 的 事件循环 负责在对象间传递信号,确保槽在正确的上下文中执行。


3. 信号与槽的定义与使用

1) 定义一个信号与槽

要使用信号与槽,必须满足以下条件:

  • 类需要继承自 QObject
  • 在类声明中加入 Q_OBJECT 宏。
  • 使用 signals 关键字声明信号。
  • 使用 slots 关键字声明槽函数。

示例代码:

定义类:
cpp 复制代码
#include <QObject>
#include <QDebug>

class MyObject : public QObject {
    Q_OBJECT

public:
    explicit MyObject(QObject *parent = nullptr) : QObject(parent) {}

signals:
    void mySignal(int value); // 声明一个信号

public slots:
    void mySlot(int value) {  // 定义一个槽
        qDebug() << "Received signal with value:" << value;
    }
};
连接信号与槽:
cpp 复制代码
#include <QCoreApplication>

int main(int argc, char *argv[]) {
    QCoreApplication a(argc, argv);

    MyObject obj1, obj2;

    // 连接信号与槽
    QObject::connect(&obj1, &MyObject::mySignal, &obj2, &MyObject::mySlot);

    // 触发信号
    emit obj1.mySignal(42);

    return a.exec();
}

输出结果:

复制代码
Received signal with value: 42

2) 连接方式

QObject::connect 有多种形式,支持不同的连接模式。

语法:

cpp 复制代码
QObject::connect(sender, SIGNAL(signalName(params)), receiver, SLOT(slotName(params)));

现代写法(推荐): 使用函数指针和 lambda 表达式。

cpp 复制代码
QObject::connect(sender, &ClassName::signalName, receiver, &ClassName::slotName);

Lambda 表达式示例:

cpp 复制代码
QObject::connect(&obj1, &MyObject::mySignal, [](int value) {
    qDebug() << "Lambda slot received value:" << value;
});

3) 信号与槽的重载

如果信号或槽有多个重载版本,必须在连接时指定函数指针。

示例:

cpp 复制代码
class Example : public QObject {
    Q_OBJECT
signals:
    void valueChanged(int);
    void valueChanged(double);
};

Example obj;
QObject::connect(&obj, static_cast<void (Example::*)(int)>(&Example::valueChanged), 
                 &obj, [](int value) { qDebug() << "Int version called:" << value; });

4. 信号与槽的特点

  1. 松耦合
    • 信号的发送者和槽的接收者无需了解彼此的存在。
  2. 类型安全
    • 信号与槽的参数必须匹配,否则编译器会报错。
  3. 多对多关系
    • 一个信号可以连接多个槽。
    • 多个信号可以连接到同一个槽。
  4. 线程安全
    • 跨线程连接时,信号会安全地传递到接收线程。

5. 信号与槽的连接类型

在多线程程序中,Qt 提供了不同的连接类型:

连接类型:

  1. Qt::AutoConnection(默认)

    • 如果信号和槽在同一线程中,则为直接连接
    • 如果在不同线程中,则为队列连接
  2. Qt::DirectConnection

    • 信号发出后,槽会立即执行,位于发送线程。
  3. Qt::QueuedConnection

    • 信号会进入接收线程的事件队列,由接收线程执行。
  4. Qt::BlockingQueuedConnection

    • 发送线程阻塞,直到槽函数执行完毕。仅适用于跨线程。

6. 注意事项

  1. 使用 Q_OBJECT
    • 类必须继承自 QObject,并包含 Q_OBJECT 宏,否则信号与槽不会工作。
  1. moc 工具的支持

    • 需要使用 Qt 的 Meta-Object Compiler (moc) 进行预处理,否则信号与槽无法解析。
  2. 参数匹配

    • 信号与槽的参数个数和类型必须兼容。信号的参数可以多于槽的参数,但前者必须兼容后者。
  3. 析构时自动断开连接

    • 当对象被销毁时,Qt 会自动断开与该对象相关的所有连接。

总结

Qt 的信号与槽机制是一种强大且灵活的事件处理机制,它提供了类型安全松耦合 的通信方式,广泛用于 Qt 应用程序中的 组件间交互事件处理

现代 C++ 写法(函数指针和 Lambda 表达式)更加简洁和安全,推荐在新代码中使用。

相关推荐
-雷阵雨-2 分钟前
MySQL——数据类型
数据库·mysql
星竹晨L14 分钟前
【C++】深入理解list底层:list的模拟实现
开发语言·c++
LB211219 分钟前
Redis 黑马skyout
java·数据库·redis
豐儀麟阁贵26 分钟前
Java知识点储备
java·开发语言
TDengine (老段)27 分钟前
TDengine 浮点数新编码 BSS 用户手册
大数据·数据库·物联网·时序数据库·iot·tdengine·涛思数据
TDengine (老段)31 分钟前
TDengine 数学函数 ASIN() 用户手册
大数据·数据库·sql·物联网·时序数据库·tdengine·涛思数据
豐儀麟阁贵35 分钟前
2.3变量与常量
java·开发语言
什么半岛铁盒2 小时前
C++11 多线程与并发编程
c语言·开发语言·c++
Kiri霧6 小时前
Linux下的Rust 与 C 的互操作性解析
c语言·开发语言·rust
雪芽蓝域zzs6 小时前
uniapp AES 加密解密
开发语言·uni-app·c#