Qt 的信号和槽机制(Signals and Slots)是其核心特性之一,用于对象间的通信,特别是在 GUI 编程中,信号和槽机制极大地简化了事件驱动编程。该机制提供了一种松耦合的方式,使得一个对象可以发出信号(Signal),并且其他对象可以响应这些信号(Slot),而不需要显式的调用。

信号和槽机制的核心概念

  1. 信号(Signal)

    • 信号是对象发出的消息,用于通知事件的发生。
    • 信号可以在类中通过 signals: 关键字定义。信号并没有实际的函数实现,仅仅是一个声明,表明该信号在某些条件下会被发射。
    • 信号可以携带参数,参数的类型会在槽函数中对应的接收。
  2. 槽(Slot)

    • 槽是一个函数,用于响应信号。当信号发射时,槽函数会被自动调用。槽可以被认为是信号的接收器。
    • 槽函数可以在类中通过 slots: 关键字声明,实际上它是一个普通的 C++ 函数,可以有返回值,但通常返回 void
    • 槽函数的参数类型和数量必须与信号的相匹配。
  3. 连接(connect)

    • 通过 QObject::connect() 函数,可以将信号和槽连接起来。当信号发射时,所有与该信号相连的槽函数都会被调用。
    • 可以连接一个信号到多个槽,也可以连接多个信号到同一个槽。信号与槽之间的连接是松耦合的,即发射信号的对象并不知道槽的存在。

信号和槽的定义与使用

信号和槽的声明
  • 信号和槽的声明需要在类的声明中通过 signals:slots: 关键字显式定义。
#include <QObject>

class MyObject : public QObject {
    Q_OBJECT  // 启用信号槽机制

public:
    explicit MyObject(QObject *parent = nullptr);

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

public slots:
    void mySlot(int value);    // 声明槽函数
};
信号与槽的连接
  • 在对象之间建立信号和槽的连接,使用 QObject::connect() 函数。可以将对象 A 的信号连接到对象 B 的槽上。
#include <QDebug>

MyObject obj1, obj2;

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

// 触发信号
emit obj1.mySignal(42);  // 当信号被发射时,obj2 的 mySlot() 会被调用

信号和槽的规则

  1. 自动参数匹配

    • 槽的参数可以少于信号的参数,但槽的参数必须与信号的参数类型兼容。例如,信号发送两个参数,槽函数只接收一个参数也是合法的。
  2. 自动连接(Auto Connection)

    • Qt 会根据当前线程的上下文来选择信号和槽的调用方式:
      • 直接连接(Direct Connection):在同一个线程内,信号发射时,槽函数立即被调用。
      • 队列连接(Queued Connection):在不同线程之间,信号发射时,槽函数会被添加到接收者对象所属线程的事件队列中,稍后由该线程处理。
  3. 一个信号多个槽

    • 一个信号可以连接多个槽,当信号发射时,所有槽都会按连接的顺序被依次调用。
  4. 一个槽多个信号

    • 一个槽也可以连接多个信号,任何一个信号发射时,槽都会被调用。
  5. 断开连接(disconnect)

    • 使用 QObject::disconnect() 可以断开信号与槽的连接。
QObject::disconnect(&obj1, &MyObject::mySignal, &obj2, &MyObject::mySlot);

使用现代 C++11 语法的信号和槽

在 Qt 5 中,引入了基于 C++11 lambda 表达式的信号和槽语法,使得编写信号和槽更加简洁。使用 lambda 表达式,可以直接在 connect() 中传递一个匿名函数作为槽函数,而不需要在类中显式声明槽。

QObject::connect(&obj1, &MyObject::mySignal, [](int value){
    qDebug() << "Lambda received value:" << value;
});

emit obj1.mySignal(42);  // 会触发 lambda 输出 "Lambda received value: 42"

示例代码

#include <QObject>
#include <QDebug>

class MyObject : public QObject {
    Q_OBJECT

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

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

public slots:
    void mySlot(int value) {   // 槽函数实现
        qDebug() << "Slot received value:" << value;
    }
};

int main() {
    MyObject obj1, obj2;

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

    // 发射信号
    emit obj1.mySignal(42);  // 会调用 obj2 的槽函数,输出 "Slot received value: 42"

    return 0;
}

总结

Qt 的信号和槽机制为对象间提供了灵活而强大的通信方式,避免了直接调用函数的耦合。它不仅适用于 GUI 编程中的事件处理,在多线程编程、组件通信中也有广泛应用。

三个(信号、槽函数、connect连接)

10-04 13:05