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 表达式)更加简洁和安全,推荐在新代码中使用。

相关推荐
qq_380619162 分钟前
如何在phpMyAdmin中处理特殊字符账号名的授权_反引号的正确包裹
jvm·数据库·python
2201_756847335 分钟前
HTML函数在老旧浏览器运行慢是硬件问题吗_软硬协同分析【教程】
jvm·数据库·python
晓纪同学7 分钟前
EffctiveC++_第三章_资源管理
开发语言·c++·算法
志栋智能7 分钟前
当巡检遇上超自动化:一场运维质量的系统性升级
运维·服务器·网络·数据库·人工智能·机器学习·自动化
Micro麦可乐8 分钟前
Redis只会用来做缓存?解锁Redis非缓存的九个应用场景,90%程序员不知道的隐藏技能
数据库·redis·缓存·消息队列·分布式锁·延迟队列·布隆过滤器
21号 112 分钟前
10.Redis 缓存
数据库·redis·缓存
蚊子码农14 分钟前
每日一题--C语言指针与内存泄漏:一道小问题的深度复盘
c语言·开发语言
Fanfanaas14 分钟前
Linux 系统编程 进程篇(一)
linux·运维·服务器·c语言·开发语言·网络·学习
星辰徐哥18 分钟前
ARP缓存表:作用、查看方法与刷新技巧
开发语言·缓存·php
雨墨✘20 分钟前
CSS如何提高团队协作效率_推广BEM规范减少样式沟通成本
jvm·数据库·python