信号与槽机制
# 07.信号和槽机制
# 目录介绍
- 7.1 基本概念
- 7.1.1 信号与槽
- 7.1.2 信号(Signal)
- 7.1.3 槽(Slot)
- 7.1.4 连接(Connect)
- 7.1.5 优点和缺点
- 7.2 信号与槽使用
- 7.2.1 声明信号与槽
- 7.2.2 连接信号与槽
- 7.2.3 Lambda表达式
- 7.3 信号与槽特性
- 7.3.1 自动连接
- 7.3.2 参数传递
- 7.3.3 多对多连接
- 7.3.4 断开连接
- 7.4 跨线程通信
- 7.4.1 连接类型
- 7.4.2 跨线程通信案例
- 7.5 信号槽和事件
- 7.5.1 两者关系
- 7.5 信号槽综合案例
- 7.5.1 项目结构
- 7.5.2 代码实现
- 7.5.3 运行结果
- 7.5.4 代码解析
- 7.5.5 断开连接
- 7.5.6 综合案例总结
# 7.1 基本概念
Qt 的信号与槽机制是其核心特性之一,用于实现对象之间的通信。信号与槽机制是 Qt 对观察者模式的实现,具有类型安全、松耦合和跨线程通信等优点。
# 7.1.1 信号与槽
功能:实现对象之间的松耦合通信。核心类:
QObject:提供信号与槽的基础支持。QSignalMapper:将多个信号映射到单个槽。QMetaObject::connect:动态连接信号与槽。
信号与槽机制,核心概念:
- 信号(Signal):对象发出的事件(如按钮点击)。
- 槽(Slot):响应信号的函数。
- 信号与槽通过 QObject::connect 函数连接,实现对象间的通信。
# 7.1.2 信号(Signal)
信号是类中声明的特殊成员函数,用于在特定事件发生时发出通知。
信号不需要实现,只需声明。信号可以带参数,用于传递数据。
# 7.1.3 槽(Slot)
槽是普通的成员函数,用于响应信号。
槽需要实现。槽可以带参数,参数类型必须与信号匹配。
# 7.1.4 连接(Connect)
使用 QObject::connect() 函数将信号与槽连接起来。当信号发出时,连接的槽函数会被自动调用。
# 7.1.5 优点和缺点
松耦合:对象间无需直接调用彼此的方法。
灵活性:一个信号可以连接多个槽,一个槽可以响应多个信号。
# 7.2 信号与槽使用
# 7.2.1 声明信号与槽
在类中使用 signals 和 slots 关键字声明信号和槽。
#include <QObject>
#include <QDebug>
class MyClass : public QObject {
Q_OBJECT // 必须包含 Q_OBJECT 宏
public:
explicit MyClass(QObject *parent = nullptr) : QObject(parent) {}
signals:
void mySignal(int value); // 声明信号
public slots:
void mySlot(int value) { // 声明槽
qDebug() << "Slot received value:" << value;
}
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 7.2.2 连接信号与槽
QObject::connect() 是用于连接信号(Signal)与槽(Slot)的核心函数。它的作用是将一个对象的信号与另一个对象的槽函数关联起来,当信号被触发时,槽函数会自动执行。
函数原型,以及参数详细介绍
static bool QObject::connect(
const QObject *sender, // 发送信号的对象
const char *signal, // 信号的名称
const QObject *receiver, // 接收信号的对象
const char *slot, // 槽函数的名称
Qt::ConnectionType type = Qt::AutoConnection // 连接类型
);
2
3
4
5
6
7
sender,类型:const QObject *;含义:发送信号的对象指针。示例:button(按钮对象)。signal,类型:const char *;含义:信号的名称,需要使用SIGNAL()宏进行封装。示例:SIGNAL(clicked())(按钮的点击信号)。receiver,类型:const QObject *;含义:接收信号的对象指针。示例:this(当前对象)。slot,类型:const char *;含义:槽函数的名称,需要使用SLOT()宏进行封装。示例:自定义的槽函数type,类型:Qt::ConnectionType;含义:连接类型,决定信号与槽的执行方式。默认值为Qt::AutoConnection。可选值:
Qt::AutoConnection:自动选择连接方式(默认)。Qt::DirectConnection:信号触发时立即执行槽函数(同步)。Qt::QueuedConnection:将槽函数放入事件队列,稍后执行(异步)。Qt::BlockingQueuedConnection:阻塞发送线程,直到槽函数执行完毕。Qt::UniqueConnection:确保连接是唯一的,避免重复连接。
返回值,类型:bool。含义:连接是否成功。如果成功返回 true,否则返回 false。
# 7.2.3 Lambda表达式
Lambda 表达式可以作为槽函数使用,简化代码。
QObject::connect(&sender, &MyClass::mySignal, [](int value) {
qDebug() << "Lambda received value:" << value;
});
2
3
# 7.3 信号与槽特性
# 7.3.1 自动连接
信号与槽的连接是自动的,当信号发出时,槽函数会被立即调用。
# 7.3.2 参数传递
信号与槽的参数类型必须匹配,但槽的参数可以比信号少。
QObject::connect(&sender, &MyClass::mySignal, &receiver, [](int value) {
qDebug() << "Lambda received value:" << value;
});
2
3
# 7.3.3 多对多连接
一个信号可以连接多个槽,一个槽也可以接收多个信号。
QObject::connect(&sender, &MyClass::mySignal, &receiver1, &MyClass::mySlot);
QObject::connect(&sender, &MyClass::mySignal, &receiver2, &MyClass::mySlot);
2
# 7.3.4 断开连接
使用 QObject::disconnect() 断开信号与槽的连接。
QObject::disconnect(&sender, &MyClass::mySignal, &receiver, &MyClass::mySlot);
# 7.4 跨线程通信
Qt 的信号与槽机制支持跨线程通信,通过 Qt::ConnectionType 参数指定连接类型。
# 7.4.1 连接类型
Qt::AutoConnection(默认):如果信号和槽在同一线程,使用直接连接;否则,使用队列连接。Qt::DirectConnection:槽函数在信号发出的线程中立即执行。Qt::QueuedConnection:槽函数在接收对象的线程中异步执行。Qt::BlockingQueuedConnection:槽函数在接收对象的线程中同步执行,发送线程会阻塞直到槽函数执行完毕。
# 7.4.2 跨线程通信案例
#include <QCoreApplication>
#include <QThread>
#include <QDebug>
class Worker : public QObject
{
Q_OBJECT
public slots:
void doWork(int value) {
qDebug() << "Worker thread ID:" << QThread::currentThreadId();
qDebug() << "Received value:" << value;
}
};
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
QThread workerThread;
Worker worker;
worker.moveToThread(&workerThread); // 将 worker 移动到新线程
workerThread.start();
// 跨线程连接
QObject::connect(&app, &QCoreApplication::aboutToQuit, &worker, &Worker::deleteLater);
QObject::connect(&app, &QCoreApplication::aboutToQuit, &workerThread, &QThread::quit);
QObject::connect(&workerThread, &QThread::finished, &workerThread, &QThread::deleteLater);
// 发送信号
emit worker.doWork(42);
return app.exec();
}
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
29
30
31
32
33
34
35
# 7.5 信号槽和事件
# 7.5.1 两者关系
信号槽机制深度依赖事件循环:
信号发射 → 生成元调用事件 → 加入事件队列 → 事件循环处理 → 执行槽函数
# 7.5 信号槽综合案例
以下是一个比较全面的 Qt 信号与槽使用案例,涵盖了信号与槽的基本用法、带参数的信号与槽、Lambda 表达式作为槽、跨线程通信、以及信号与槽的断开连接等场景。
# 7.5.1 项目结构
SignalSlotExample/
├── main.cpp
├── MyClass.h
├── MyClass.cpp
├── Worker.h
├── Worker.cpp
2
3
4
5
6
# 7.5.2 代码实现
(1)MyClass.h
#ifndef MYCLASS_H
#define MYCLASS_H
#include <QObject>
#include <QDebug>
#include <QThread>
#include "Worker.h"
class MyClass : public QObject {
Q_OBJECT
public:
explicit MyClass(QObject *parent = nullptr);
signals:
void signalWithoutArgs(); // 无参数的信号
void signalWithArgs(int value, const QString &text); // 带参数的信号
public slots:
void slotWithoutArgs(); // 无参数的槽
void slotWithArgs(int value, const QString &text); // 带参数的槽
private:
Worker *worker;
QThread workerThread;
};
#endif // MYCLASS_H
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
(2)MyClass.cpp
#include "MyClass.h"
MyClass::MyClass(QObject *parent) : QObject(parent) {
// 创建 Worker 对象
worker = new Worker();
// 将 Worker 移动到子线程
worker->moveToThread(&workerThread);
// 连接信号与槽
// 1. 无参数的信号与槽
connect(this, &MyClass::signalWithoutArgs, this, &MyClass::slotWithoutArgs);
// 2. 带参数的信号与槽
connect(this, &MyClass::signalWithArgs, this, &MyClass::slotWithArgs);
// 3. 跨线程的信号与槽
connect(this, &MyClass::signalWithArgs, worker, &Worker::doWork);
// 4. 使用 Lambda 表达式作为槽
connect(this, &MyClass::signalWithoutArgs, []() {
qDebug() << "Lambda slot called!";
});
// 启动子线程
workerThread.start();
}
void MyClass::slotWithoutArgs() {
qDebug() << "Slot without args called!";
}
void MyClass::slotWithArgs(int value, const QString &text) {
qDebug() << "Slot with args called! Value:" << value << "Text:" << text;
}
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
29
(3)Worker.h
#ifndef WORKER_H
#define WORKER_H
#include <QObject>
#include <QDebug>
#include <QThread>
class Worker : public QObject {
Q_OBJECT
public slots:
void doWork(int value, const QString &text);
};
#endif // WORKER_H
2
3
4
5
6
7
8
9
10
11
12
13
14
(4)Worker.cpp
#include "Worker.h"
void Worker::doWork(int value, const QString &text) {
qDebug() << "Worker is running in thread:" << QThread::currentThreadId();
qDebug() << "Received value:" << value << "Text:" << text;
}
2
3
4
5
6
(5)main.cpp
#include <QCoreApplication>
#include "MyClass.h"
int main(int argc, char *argv[]) {
QCoreApplication a(argc, argv);
MyClass myClass;
// 发出无参数的信号
emit myClass.signalWithoutArgs();
// 发出带参数的信号
emit myClass.signalWithArgs(42, "Hello, Qt!");
return a.exec();
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 7.5.3 运行结果
运行程序后,输出如下:
Slot without args called!
Lambda slot called!
Slot with args called! Value: 42 Text: "Hello, Qt!"
Worker is running in thread: 0x7f8b8b5c1700
Received value: 42 Text: "Hello, Qt!"
2
3
4
5
# 7.5.4 代码解析
- 无参数的信号与槽:
signalWithoutArgs信号触发slotWithoutArgs槽和 Lambda 表达式。
- 带参数的信号与槽:
signalWithArgs信号触发slotWithArgs槽和Worker::doWork槽。
- 跨线程通信:
Worker对象在子线程中运行,signalWithArgs信号通过Qt::QueuedConnection自动跨线程调用Worker::doWork。
- Lambda 表达式作为槽:
- 使用 Lambda 表达式作为槽函数,简化代码。
# 7.5.5 断开连接
如果需要断开信号与槽的连接,可以使用 disconnect 函数:
disconnect(this, &MyClass::signalWithoutArgs, this, &MyClass::slotWithoutArgs);
# 7.5.6 综合案例总结
这个案例展示了 Qt 信号与槽的多种用法,包括:
- 无参数的信号与槽。
- 带参数的信号与槽。
- 使用 Lambda 表达式作为槽。
- 跨线程的信号与槽通信。
- 信号与槽的断开连接。