马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
Qt 中的信号(Signals)详解
Qt 的信号与槽(Signals & Slots)机制是其焦点特性之一,用于实现对象间的松耦合通讯。信号与槽允许一个对象在特定变乱发生时关照其他对象,而无需知道罗致者的具体信息。以下是信号的具体剖析:
一、信号的根本概念
- 信号(Signal)
- 由类中的 signals 地域声明的特别成员函数,无实当代码。
- 当对象状态变革或变乱发生时,通过 emit 关键字触发信号。
- 示例:
- class Counter : public QObject {
- Q_OBJECT
- public:
- Counter() : m_value(0) {}
- void increment() {
- m_value++;
- emit valueChanged(m_value); // 触发信号
- }
- signals:
- void valueChanged(int newValue); // 信号声明
- private:
- int m_value;
- };
复制代码
- 槽(Slot)
- 平凡的成员函数,用于相应信号。
- 需在类声明中使用 slots 修饰符(Qt4/Qt5 兼容),或直接声明为公有函数(Qt5 支持)。
- 示例:
- class Display : public QObject {
- Q_OBJECT
- public slots:
- void updateDisplay(int value) {
- qDebug() << "当前值:" << value;
- }
- };
复制代码
- 毗连(Connection)
- 通过 QObject::connect() 将信号与槽绑定。
- 语法(Qt5 风格):
- connect(sender, &Sender::signal, receiver, &Receiver::slot);
复制代码
第一个参数是发送对象,第二参数是信号范例 ,第三个是信号罗致者,第四个是收到信 号时调用的槽函数。
二、信号与槽的底层原理
- 元对象体系(Meta-Object System)
- 依靠 Qt 的元对象编译器(moc)天生 moc_*.cpp 代码。
- 信号和槽的声明需包罗 Q_OBJECT 宏,以启用元对象功能(如动态属性、反射)。
- 信号触发过程
- 当调用 emit signal() 时,Qt 通过元对象体系查找全部毗连的槽。
- 槽的调用方式取决于毗连范例(直接毗连或队列毗连)。
- 示例代码天生(简化):
- // moc 生成的代码(伪代码)
- void Counter::valueChanged(int newValue) {
- QMetaObject::activate(this, &staticMetaObject, 0, &newValue);
- }
复制代码
- 毗连范例
- **Qt::AutoConnection(默认)**:主动选择直接或队列毗连(根据线程关系)。
- **Qt:
irectConnection**:立刻在发送者线程调用槽(同步)。
- **Qt:
ueuedConnection**:将槽调用封装为变乱,由罗致者线程处理惩罚(异步)。
- **Qt::BlockingQueuedConnection**:类似队列毗连,但发送者线程会等候槽实行完成。
三、信号与槽的使用方法
- 根本毗连示例
- Counter *counter = new Counter;
- Display *display = new Display;
- // 连接信号与槽
- QObject::connect(counter, &Counter::valueChanged, display, &Display::updateDisplay);
- // 触发信号
- counter->increment(); // 输出:当前值:1
复制代码 - Lambda 表达式作为槽
- connect(counter, &Counter::valueChanged, [](int value) {
- qDebug() << "Lambda 处理:" << value;
- });
复制代码 - 信号与信号的毗连
- QPushButton *button = new QPushButton;
- connect(button, &QPushButton::clicked, counter, &Counter::increment);
复制代码 - 断开毗连
- disconnect(counter, &Counter::valueChanged, display, &Display::updateDisplay);
复制代码
四、高级用法
- 跨线程通讯
使用 Qt: ueuedConnection 安全地在差别线程间通报数据:
- // 在工作线程触发信号
- class Worker : public QObject {
- Q_OBJECT
- signals:
- void resultReady(const QString &data);
- };
- // 主线程接收数据
- QThread workerThread;
- Worker *worker = new Worker;
- worker->moveToThread(&workerThread);
- QObject::connect(worker, &Worker::resultReady, mainWindow, &MainWindow::handleResult, Qt::QueuedConnection);
- workerThread.start();
复制代码 - 携带额外参数
信号和槽的参数可以不完全匹配,但需满足范例兼容性:
- signals:
- void progress(int percent, const QString &status);
- // 槽函数仅接收部分参数
- connect(worker, &Worker::progress, ui->progressBar, &QProgressBar::setValue);
复制代码 - QSignalMapper(旧版Qt)
用于将多个信号映射到同一槽并携带标识(Qt5 后保举使用 Lambda 替换):
- QSignalMapper *mapper = new QSignalMapper;
- connect(button1, &QPushButton::clicked, mapper, &QSignalMapper::map);
- mapper->setMapping(button1, 1);
- connect(mapper, &QSignalMapper::mappedInt, this, &MyClass::handleButton);
复制代码
五、信号与变乱的区别
特性信号与槽变乱触发方式显式调用 emit由体系或 Qt 变乱循环主动触发用途对象间的业务逻辑通讯处理惩罚底层输入、绘图、定时器等体系变乱传播机制一对一或一对多毗连可冒泡到父对象或被变乱过滤器拦截线程安全支持跨线程(通过队列毗连)需手动处理惩罚线程间变乱通报 六、常见标题与调试
- 信号未触发
- 查抄 connect 是否乐成(返回 true)。
- 确保信号已正确声明在 signals 地域,且类包罗 Q_OBJECT 宏。
- 槽未实行
- 确认罗致者对象未被提前烧毁。
- 查抄毗连范例(如跨线程需使用队列毗连)。
- 内存走漏
- 克制循环引用(如对象相互毗连但未断开)。
- 使用 QPointer 或智能指针管理罗致者生命周期。
- 性能优化
- 淘汰高频信号(如实时数据更新)的槽处理惩罚耗时。
- 归并多个信号为单一参数布局体。
七、总结
Qt 的信号与槽机制通过元对象体系实现高效的对象间通讯,具有以下上风:
- 松耦合:发送者无需知道罗致者的存在。
- 范例安全:编译时查抄参数范例。
- 跨线程支持:简化多线程编程。
实用场景:
- 用户界面交互(如按钮点击相应)。
- 模块间数据通报(如网络哀求结果回传)。
- 异步任务状态关照(如进度更新)。
通过公道使用信号与槽,可以构建清晰、可维护的 Qt 应用步调架构。
|