相信有过一点QT开发经验的人来说,对于QT中的信号并不陌生。在一个实际的QT项目当中,大多时候是直接将组件中预定义的信号连接到槽函数,当信号发送的时候槽函数就被调用。那么QT中的信号具体又是怎么来的呢?信号又是如何发射的呢?
QT中信号(SIGNAL)的本质
-信号只是一个特殊的成员函数的声明
.函数的返回值是void类型
.函数只能声明不能定义
-信号必须使用signals关键字进行声明
.函数的访问属性自动被设置为protected
.只能通过emit关键字调用函数(发射信号)
下面来看一下信号定义的实例:
class Test:public QObject //只有Qt类才能定义信号
{
Q_OBJECT //必须使用宏 Q_OBJECT
signals: // 使用signal声明信号函数
void testSignal(int v); // 访问级别自动被设置为protected
// 信号只能声明不能定义
public:
void test(int i)
{
emit testSignal(i); // 通过emit发射信号
}
};
信号与槽函数的映射方式:
1.可以一个信号映射一个槽函数,例如:A对象的signal1 映射到 B对象的slot1
2.可以多个信号映射一个槽函数,例如:A对象的signal1 和 D对象的signal3 映射到 B对象的slot1
3.可以一个信号映射到多个槽函数,例如:E对象的signal5 映射到 C对象的 slots3 和 B对象的 slot2
4.还可以存在信号从一个对象转嫁到另外一个对象。例如:D对象的 signal4 映射到 E对象的 signal6,然后转嫁到 C对象的 slots4
5.信号与槽函数的连接可以被 disconnect函数删除 (移除)
下面通过实际的程序实例来验证上面的原理:
#ifndef TESTSIGNAL_H
#define TESTSIGNAL_H
#include <QObject>
class TestSignal : public QObject
{
Q_OBJECT
public:
void send(int i)
{
emit testSignal(i);
}
signals:
void testSignal(int i);
};
#endif // TESTSIGNAL_H
#ifndef RXCLASS_H
#define RXCLASS_H
#include <QObject>
#include <QDebug>
class RxClass : public QObject
{
Q_OBJECT
public:
protected slots:
void mySlot(int v)
{
qDebug()<<"void mySlot(int v)";
qDebug()<<"sender:"<<sender()->objectName();
qDebug()<<"Receiver:"<<this->objectName();
qDebug()<<"value:"<<v;
qDebug()<<endl;
}
};
#endif // RXCLASS_H
#include <QtCore/QCoreApplication>
#include "TestSignal.h"
#include "RxClass.h"
void emit_signal()
{
qDebug()<<"void emit_signal()";
TestSignal t;
t.setObjectName("t");
RxClass r;
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_multi()
{
qDebug()<<"void one_multi()";
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)));
for(int i=0;i<3;i++)
{
t.send(i);
}
}
void multi_one()
{
qDebug()<<"void multi_one()";
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(100);
t2.send(101);
}
void signal_to_signal()
{
qDebug()<<"void signal_to_signal()";
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(100);
t2.send(101);
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
//emit_signal();
//one_multi();
//multi_one();
signal_to_signal();
return a.exec();
}
在用QT开发的时候不可忽视的军规:
1.Qt类只能在头文件中声明 (一个普通的C++类,并没有这种要求)
2.信号与槽函数的原型应该完全相同
3.信号参数多余槽函数参数时候,多余的槽函数被忽略
4.槽函数的返回值必须是void类型
5.槽函数可以像普通成员函数一样被调用
6.信号与槽的访问属性对于connect/disconnect 无效 ,这一点有点破坏面向对象的封装性,但是却更有利于实际开发的工作。
信号与槽的意义:
-最大限度的弱化了类之间的耦合关系
-在设计阶段,可以减少不必要的接口类(抽象类)
-在开发阶段,对象间的交互通过信号与槽动态绑定