Qt 信号与槽深入 #
信号与槽基础回顾 #
基本概念 #
text
┌─────────────────────────────────────────────────────────────┐
│ 信号与槽模型 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ 发送者 │ │ 接收者 │ │
│ │ │ │ │ │
│ │ ┌───────┐ │ 信号发射 │ ┌───────┐ │ │
│ │ │ 信号 │──┼────────────────────┼─>│ 槽 │ │ │
│ │ └───────┘ │ │ └───────┘ │ │
│ │ │ │ │ │
│ └─────────────┘ └─────────────┘ │
│ │
│ 特点: │
│ - 信号:对象状态改变时发射 │
│ - 槽:响应信号的函数 │
│ - 连接:建立信号与槽的关联 │
│ │
└─────────────────────────────────────────────────────────────┘
定义信号和槽 #
cpp
class Sender : public QObject
{
Q_OBJECT
public:
explicit Sender(QObject *parent = nullptr);
signals:
void simpleSignal();
void signalWithInt(int value);
void signalWithString(const QString &text);
void signalWithMultiple(int a, const QString &b, double c);
};
class Receiver : public QObject
{
Q_OBJECT
public:
explicit Receiver(QObject *parent = nullptr);
public slots:
void onSimpleSignal();
void onSignalWithInt(int value);
void onSignalWithString(const QString &text);
private slots:
void onInternalSignal();
void anyMemberFunction(int value); // Qt 5+ 任意成员函数
};
连接方式详解 #
Qt 5+ 函数指针语法(推荐) #
cpp
Sender *sender = new Sender;
Receiver *receiver = new Receiver;
// 基本连接
connect(sender, &Sender::simpleSignal,
receiver, &Receiver::onSimpleSignal);
// 带参数的信号
connect(sender, &Sender::signalWithInt,
receiver, &Receiver::onSignalWithInt);
// 连接到任意成员函数
connect(sender, &Sender::signalWithInt,
receiver, &Receiver::anyMemberFunction);
// 连接到 Lambda
connect(sender, &Sender::signalWithInt, [](int value) {
qDebug() << "Lambda received:" << value;
});
// 连接到 Lambda(捕获上下文)
connect(sender, &Sender::signalWithInt, [receiver](int value) {
receiver->someMethod(value);
});
// 连接到普通函数
void globalFunction(int value) {
qDebug() << "Global function:" << value;
}
connect(sender, &Sender::signalWithInt, globalFunction);
处理信号重载 #
cpp
class MyObject : public QObject
{
Q_OBJECT
signals:
void valueChanged(int value);
void valueChanged(const QString &value);
};
// 使用函数指针区分重载
connect(obj, QOverload<int>::of(&MyObject::valueChanged),
[](int value) { qDebug() << "Int:" << value; });
connect(obj, QOverload<const QString&>::of(&MyObject::valueChanged),
[](const QString &value) { qDebug() << "String:" << value; });
Qt 旧式语法(不推荐) #
cpp
// 旧式语法(运行时检查)
connect(sender, SIGNAL(signalWithInt(int)),
receiver, SLOT(onSignalWithInt(int)));
// 问题:
// ❌ 编译时不检查参数类型
// ❌ 参数不匹配时运行时才报错
// ❌ 重构困难
连接类型 #
连接类型枚举 #
cpp
// 自动连接(默认)
// 根据发送者和接收者是否在同一线程决定连接类型
connect(sender, &Sender::signal, receiver, &Receiver::slot,
Qt::AutoConnection);
// 直接连接
// 槽函数在信号发射时立即执行(同一线程)
connect(sender, &Sender::signal, receiver, &Receiver::slot,
Qt::DirectConnection);
// 队列连接
// 槽函数在接收者所在线程的事件循环中执行
connect(sender, &Sender::signal, receiver, &Receiver::slot,
Qt::QueuedConnection);
// 阻塞队列连接
// 发送者等待槽函数执行完成(跨线程)
connect(sender, &Sender::signal, receiver, &Receiver::slot,
Qt::BlockingQueuedConnection);
// 唯一连接
// 确保连接只建立一次
connect(sender, &Sender::signal, receiver, &Receiver::slot,
Qt::UniqueConnection);
连接类型选择 #
text
┌─────────────────────────────────────────────────────────────┐
│ 连接类型选择指南 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 发送者和接收者在同一线程: │
│ └── 使用 AutoConnection(默认)或 DirectConnection │
│ │
│ 发送者和接收者在不同线程: │
│ ├── 需要异步处理:QueuedConnection │
│ ├── 需要同步等待:BlockingQueuedConnection │
│ └── 一般情况:AutoConnection(自动选择队列连接) │
│ │
│ 避免重复连接: │
│ └── 使用 UniqueConnection │
│ │
└─────────────────────────────────────────────────────────────┘
跨线程信号槽 #
工作线程示例 #
cpp
class Worker : public QObject
{
Q_OBJECT
public:
explicit Worker(QObject *parent = nullptr);
public slots:
void doWork(int parameter) {
// 在工作线程中执行
for (int i = 0; i < 100; ++i) {
emit progressChanged(i);
QThread::msleep(50);
}
emit workFinished();
}
signals:
void progressChanged(int percent);
void workFinished();
};
class Controller : public QObject
{
Q_OBJECT
public:
Controller() {
Worker *worker = new Worker;
worker->moveToThread(&workerThread);
connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
connect(this, &Controller::startWork, worker, &Worker::doWork);
connect(worker, &Worker::progressChanged, this, &Controller::onProgress);
connect(worker, &Worker::workFinished, this, &Controller::onFinished);
workerThread.start();
}
~Controller() {
workerThread.quit();
workerThread.wait();
}
signals:
void startWork(int parameter);
private slots:
void onProgress(int percent) {
qDebug() << "Progress:" << percent;
}
void onFinished() {
qDebug() << "Work finished";
}
private:
QThread workerThread;
};
线程安全注意事项 #
cpp
// 跨线程访问数据需要同步
class SharedData : public QObject
{
Q_OBJECT
public:
void setData(int value) {
QMutexLocker locker(&m_mutex);
m_data = value;
}
int data() const {
QMutexLocker locker(&m_mutex);
return m_data;
}
private:
mutable QMutex m_mutex;
int m_data = 0;
};
// 使用信号槽跨线程传递数据(线程安全)
connect(sender, &Sender::dataReady, receiver, &Receiver::processData,
Qt::QueuedConnection); // 数据会被复制
发射信号 #
emit 关键字 #
cpp
class Counter : public QObject
{
Q_OBJECT
public:
int value() const { return m_value; }
void setValue(int value) {
if (m_value != value) {
m_value = value;
emit valueChanged(value); // 发射信号
}
}
signals:
void valueChanged(int newValue);
private:
int m_value = 0;
};
直接调用信号 #
cpp
// Qt 5+ 可以直接调用信号(不使用 emit)
sender->signalWithInt(42);
// 通过元对象系统调用
QMetaObject::invokeMethod(sender, "signalWithInt", Q_ARG(int, 42));
断开连接 #
断开方式 #
cpp
// 断开特定连接
QMetaObject::Connection conn = connect(sender, &Sender::signal, receiver, &Receiver::slot);
disconnect(conn);
// 断开发送者和接收者之间的所有连接
disconnect(sender, nullptr, receiver, nullptr);
// 断开发送者的特定信号
disconnect(sender, &Sender::signal, nullptr, nullptr);
// 断开接收者的特定槽
disconnect(nullptr, nullptr, receiver, &Receiver::slot);
// 断开特定信号和槽的连接
disconnect(sender, &Sender::signal, receiver, &Receiver::slot);
高级用法 #
信号转信号 #
cpp
class Relay : public QObject
{
Q_OBJECT
public:
Relay(QObject *parent = nullptr) : QObject(parent) {
connect(this, &Relay::inputSignal, this, &Relay::outputSignal);
}
signals:
void inputSignal(int value);
void outputSignal(int value);
};
Lambda 捕获注意事项 #
cpp
// 捕获 this(注意对象生命周期)
connect(sender, &Sender::signal, this, [this](int value) {
this->processValue(value); // 如果 this 被删除会崩溃
});
// 使用 QPointer 安全捕获
QPointer<QObject> safePtr = this;
connect(sender, &Sender::signal, [safePtr](int value) {
if (safePtr) {
safePtr->processValue(value);
}
});
// 使用上下文对象(Qt 5.10+)
connect(sender, &Sender::signal, this, [this](int value) {
this->processValue(value);
}); // this 被删除时自动断开连接
延迟调用 #
cpp
// 使用 QTimer 延迟发射信号
QTimer::singleShot(1000, []() {
qDebug() << "Delayed execution";
});
// 使用 QMetaObject::invokeMethod 延迟调用
QMetaObject::invokeMethod(receiver, "slotFunction", Qt::QueuedConnection);
性能优化 #
减少不必要的连接 #
cpp
// 使用 UniqueConnection 避免重复连接
connect(sender, &Sender::signal, receiver, &Receiver::slot,
Qt::UniqueConnection);
// 及时断开不再需要的连接
disconnect(sender, &Sender::signal, receiver, &Receiver::slot);
避免信号循环 #
cpp
// 错误示例:信号循环
connect(spinBox, QOverload<int>::of(&QSpinBox::valueChanged),
slider, &QSlider::setValue);
connect(slider, &QSlider::valueChanged,
spinBox, QOverload<int>::of(&QSpinBox::setValue)); // 循环!
// 解决方案:使用信号阻塞
spinBox->blockSignals(true);
slider->setValue(value);
spinBox->blockSignals(false);
下一步 #
现在你已经深入理解了信号与槽机制,接下来学习 事件系统,了解 Qt 的事件处理机制!
最后更新:2026-03-29