1、深入浅出信号与槽
一个事实
在实际的项目开发中,大多数时候是直接将组件中预定
义的信号连接到槽函数;信号发射时槽函数被调用。
深度的思考
信号是怎么来的?又是如何发射的?
Qt中信号(SIGNAL)的本质
-信号只是一个特殊的成员函数声明
• 函数的返回值是void类型
• 函数只能声明不能定义
-信号必须使用signals关键字进行声明
• 函数的访问属性自动被设置为protected
• 只能通过emit关键字调用函数(发射信号)
信号定义示例
信号与槽的对应关系
-一个信号可以连接到多个槽(一对多)
-多个信号可以连接到一个槽(多对一)
-一个信号可以连接到另一个信号(转嫁)
-连接可以被disconnect函数删除(移除)
2、编程实验
信号与槽的对应关系 65-1.pro
自定义信号所在类 TestSignal.h
#include <QObject>
class TestSignal : public QObject
{
Q_OBJECT
public:
void send(int i)
{
emit testSignal(i);
}
signals:
void testSignal(int v);
};
自定义槽函数所在类 RxClass.h
#include <QObject>
#include <QDebug>
class RxClass : public QObject
{
Q_OBJECT
protected slots:
void mySlot(int v)
{
qDebug() << "void mySlot(int v)";
qDebug() << "Sender: " << sender()->objectName();
qDebug() << "Receiver: " << this->objectName();
qDebug() << "Value: " << v;
qDebug() << endl;
}
};
main.cpp
#include <QtCore/QCoreApplication>
#include <QDebug>
#include "TestSignal.h"
#include "RxClass.h"
void emit_signal()
{
qDebug() << "emit_signal()" << endl;
TestSignal t;
RxClass r;
t.setObjectName("t"); //设置发送信号对象的对象名
r.setObjectName("r");
//信号连接到一个槽函数
QObject::connect(&t, SIGNAL(testSignal(int)), &r, SLOT(mySlot(int)));
for(int i=0; i<3; i++)
{
t.send(i);
}
}
void one_to_multi()
{
qDebug() << "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(mySlot(int)));
QObject::connect(&t, SIGNAL(testSignal(int)), &r2, SLOT(mySlot(int)));
t.send(100);
}
void multi_to_one()
{
qDebug() << "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(mySlot(int)));
QObject::connect(&t2, SIGNAL(testSignal(int)), &r, SLOT(mySlot(int)));
t1.send(101);
t2.send(102);
}
void signal_to_signal()
{
qDebug() << "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(mySlot(int)));
t1.send(101);//t2接收t1的信号,接着将t2发送信号到槽,值为101,发送者t2
t2.send(102);//然后t2再发出自己的信号,值102
}
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 / disconnect无效
信号与槽的意义
-最大限度的弱化了类之间的耦合关系
-在设计阶段,可以减少不必要的接口类(抽象类)
-在开发阶段,对象间的交互通过信号与槽动态绑定
3、小结
信号只是一个特殊的成员函数声明
信号必须使用signals关键字进行声明
信号与槽的可以存在多种对应关系
信号与槽机制使得类间关系松散,提高类的可复用性