一个事实
在实际的项目开发中,大多数时候是将组件中预定义的信号连接到槽函数;信号发射的时候槽函数被调用。
深度的思考
信号是怎么来的?又是如何发射的?
Qt 中 (SIGNAL) 的本质
信号只是一个特殊的成员函数声明
- 函数的返回值是 void 类型
- 函数只能声明不能定义
信号必须使用 signals 关键字进行声明
- 函数的访问属性自动被设置为 protected
- 只能通过 emit 关键字调用函数 (发射信号)
信号定义示例
信号与槽的对应关系
一个信号可以连接到多个槽 (一对多)
多个信号可以连接到一个槽 (多对一)
一个信号可以连接到另一个信号 (转嫁)
连接可以被 disconnect 函数删除 (移除)
信号与槽的对应关系
RxClass.h
#ifndef RXCLASS_H
#define RXCLASS_H
#include <QObject>
#include <QDebug>
class RxClass : public QObject
{
Q_OBJECT
protected slots:
void mySlots(int v)
{
qDebug() << "void mySlots(int v)";
qDebug() << "send: " << sender()->objectName();
qDebug() << "receive: " << this->objectName();
qDebug() << "value: " << v << endl;
}
signals:
};
#endif // RXCLASS_H
TestSignal.h
#ifndef TESTSIGNAL_H
#define TESTSIGNAL_H
#include <QObject>
class TestSignal : public QObject
{
Q_OBJECT
public:
void send(int v)
{
emit testSignal(v);
}
signals:
void testSignal(int i);
};
#endif // TESTSIGNAL_H
main.cpp
#include <QCoreApplication>
#include <QDebug>
#include "TestSignal.h"
#include "RxClass.h"
void emit_signal()
{
qDebug() << "void emit_signal()" << endl;
TestSignal t;
RxClass r;
t.setObjectName("t");
r.setObjectName("r");
QObject::connect(&t, SIGNAL(testSignal(int)), &r, SLOT(mySlots(int)));
for(int i = 0; i < 3; i++)
{
t.send(i);
}
}
void one_to_multi()
{
qDebug() << "void one_to_multi()" << endl;
TestSignal t;
RxClass r1;
RxClass r2;
t.setObjectName("t");
r1.setObjectName("r1");
r2.setObjectName("r2");
QObject::connect(&t, SIGNAL(testSignal(int)), &r1, SLOT(mySlots(int)));
QObject::connect(&t, SIGNAL(testSignal(int)), &r2, SLOT(mySlots(int)));
t.send(10);
}
void multi_to_one()
{
qDebug() << "void multi_to_one()" << endl;
TestSignal t1;
TestSignal t2;
RxClass r;
t1.setObjectName("t1");
t2.setObjectName("t2");
r.setObjectName("r");
QObject::connect(&t1, SIGNAL(testSignal(int)), &r, SLOT(mySlots(int)));
QObject::connect(&t2, SIGNAL(testSignal(int)), &r, SLOT(mySlots(int)));
t1.send(10);
t2.send(11);
}
void signal_to_signal()
{
qDebug() << "void signal_to_signal()" << endl;
TestSignal t1;
TestSignal t2;
RxClass r;
t1.setObjectName("t1");
t2.setObjectName("t2");
r.setObjectName("r");
QObject::connect(&t1, SIGNAL(testSignal(int)), &t2, SIGNAL(testSignal(int)));
QObject::connect(&t2, SIGNAL(testSignal(int)), &r, SLOT(mySlots(int)));
t1.send(10);
t2.send(11);
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
//emit_signal();
//one_to_multi();
//multi_to_one();
signal_to_signal();
return a.exec();
}
不可忽视的军规
1. Qt 类只能在头文件中声明
2. 信号与槽的原型应该完全相同
3. 信号参数多于槽函数时,多余的参数被忽略
4. 槽函数的返回值必须是 void 类型
5. 槽函数可以像普通成员函数一样被调用
6. 信号与槽的访问属性对于 connect 和 disconne 无效(仅限于使用 SIGNAL 和 SLOT,使用函数地址的时候不适用)
信号与槽的意义
最大程度的弱化了类之间的耦合关系
在设计阶段,可以减少不必要的接口类 (抽象类)
在开发阶段,对象间的交互通过信号与槽动态绑定