一个事实
在实际的项目开发中,大多数时候是直接将组件中预定义的信号连接到槽函数,信号发射时槽函数被调用。
深度的思考
信号是怎么来的?又是如何发射的?
Qt中信号(SIGNAL)的本质
—信号只是一个特殊的成员函数声明
函数的返回值是void类型
函数只能声明不能定义
—信号必须使用signals关键字进行声明
函数的访问属性自动被设置为protected
只能通过emit关键字调用函数(发射信号)
信号定义示例
class Test : public QObject //只有Qt类才能定义信号 { Q_OBJECT //必须使用宏Q_OBJECT signals: //使用signals声明信号函数,访问级别为protected void testSignal(int v); //信号只能声明不能定义 public: void test(int i) { emit testSignal(i); //通过emit发射信号 } };
#ifndef TESTSIGNAL_H #define TESTSIGNAL_H #include <QObject> class TestSignal : public QObject { Q_OBJECT public: void sendSignal(int i) { emit testSignal(i); } signals: void testSignal(int v); }; #endif // TESTSIGNAL_H
#ifndef RXSIGNAL_H #define RXSIGNAL_H #include <QObject> #include <QDebug> class RxSignal : 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 // RXSIGNAL_H
#include <QCoreApplication> #include <QDebug> #include "RxSignal.h" #include "TestSignal.h" void emit_signal() { qDebug() << "emit_signal() " << endl; RxSignal rx; TestSignal tx; rx.setObjectName("rx"); tx.setObjectName("tx"); QObject::connect(&tx, SIGNAL(testSignal(int)), &rx, SLOT(mySlot(int))); for(int i=0; i<3; i++) { tx.sendSignal(i); } } int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); emit_signal(); return a.exec(); }
信号与槽的对应关系
一个信号可以连接到多个槽(一对多)
多个信号可以连接到一个槽(多对一)
一个信号可以连接到另一个信号(转嫁)
连接可以被disconnect函数删除(移除)
实验一:一个信号被映射到两个槽函数上面
#include <QCoreApplication>
#include <QDebug>
#include "RxSignal.h"
#include "TestSignal.h"
void emit_signal()
{
qDebug() << "emit_signal() " << endl;
RxSignal rx;
TestSignal tx;
rx.setObjectName("rx");
tx.setObjectName("tx");
QObject::connect(&tx, SIGNAL(testSignal(int)), &rx, SLOT(mySlot(int)));
for(int i=0; i<3; i++)
{
tx.sendSignal(i);
}
}
//将一个信号连接到两个槽函数上面
void one_to_multiple()
{
qDebug() << "one_to_multiple() " << endl;
RxSignal rx1;
RxSignal rx2;
TestSignal tx;
rx1.setObjectName("rx1");
rx2.setObjectName("rx2");
tx.setObjectName("tx");
QObject::connect(&tx, SIGNAL(testSignal(int)), &rx1, SLOT(mySlot(int)));
QObject::connect(&tx, SIGNAL(testSignal(int)), &rx2, SLOT(mySlot(int)));
tx.sendSignal(100);
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
//emit_signal();
one_to_multiple();
return a.exec();
}
#include <QCoreApplication> #include <QDebug> #include "RxSignal.h" #include "TestSignal.h" void emit_signal() { qDebug() << "emit_signal() " << endl; RxSignal rx; TestSignal tx; rx.setObjectName("rx"); tx.setObjectName("tx"); QObject::connect(&tx, SIGNAL(testSignal(int)), &rx, SLOT(mySlot(int))); for(int i=0; i<3; i++) { tx.sendSignal(i); } } //将一个信号连接到两个槽函数上面 void one_to_multiple() { qDebug() << "one_to_multiple() " << endl; RxSignal rx1; RxSignal rx2; TestSignal tx; rx1.setObjectName("rx1"); rx2.setObjectName("rx2"); tx.setObjectName("tx"); QObject::connect(&tx, SIGNAL(testSignal(int)), &rx1, SLOT(mySlot(int))); QObject::connect(&tx, SIGNAL(testSignal(int)), &rx2, SLOT(mySlot(int))); tx.sendSignal(100); } int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); //emit_signal(); one_to_multiple(); return a.exec(); }
实验二:将两个信号映射到一个槽函数上面
#include <QCoreApplication> #include <QDebug> #include "RxSignal.h" #include "TestSignal.h" void emit_signal() { qDebug() << "emit_signal() " << endl; RxSignal rx; TestSignal tx; rx.setObjectName("rx"); tx.setObjectName("tx"); QObject::connect(&tx, SIGNAL(testSignal(int)), &rx, SLOT(mySlot(int))); for(int i=0; i<3; i++) { tx.sendSignal(i); } } //将一个信号连接到两个槽函数上面 void one_to_multiple() { qDebug() << "one_to_multiple() " << endl; RxSignal rx1; RxSignal rx2; TestSignal tx; rx1.setObjectName("rx1"); rx2.setObjectName("rx2"); tx.setObjectName("tx"); QObject::connect(&tx, SIGNAL(testSignal(int)), &rx1, SLOT(mySlot(int))); QObject::connect(&tx, SIGNAL(testSignal(int)), &rx2, SLOT(mySlot(int))); tx.sendSignal(100); } //将两个信号连接到一个槽函数上面 void multiple_to_one() { qDebug() << "multiple_to_one() " << endl; RxSignal rx; TestSignal tx1; TestSignal tx2; rx.setObjectName("rx"); tx1.setObjectName("tx1"); tx2.setObjectName("tx2"); QObject::connect(&tx1, SIGNAL(testSignal(int)), &rx, SLOT(mySlot(int))); QObject::connect(&tx2, SIGNAL(testSignal(int)), &rx, SLOT(mySlot(int))); tx1.sendSignal(100); tx2.sendSignal(200); } int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); multiple_to_one(); return a.exec(); }
实验三:信号到信号的映射
#include <QCoreApplication> #include <QDebug> #include "RxSignal.h" #include "TestSignal.h" void emit_signal() { qDebug() << "emit_signal() " << endl; RxSignal rx; TestSignal tx; rx.setObjectName("rx"); tx.setObjectName("tx"); QObject::connect(&tx, SIGNAL(testSignal(int)), &rx, SLOT(mySlot(int))); for(int i=0; i<3; i++) { tx.sendSignal(i); } } //将一个信号连接到两个槽函数上面 void one_to_multiple() { qDebug() << "one_to_multiple() " << endl; RxSignal rx1; RxSignal rx2; TestSignal tx; rx1.setObjectName("rx1"); rx2.setObjectName("rx2"); tx.setObjectName("tx"); QObject::connect(&tx, SIGNAL(testSignal(int)), &rx1, SLOT(mySlot(int))); QObject::connect(&tx, SIGNAL(testSignal(int)), &rx2, SLOT(mySlot(int))); tx.sendSignal(100); } //将两个信号连接到一个槽函数上面 void multiple_to_one() { qDebug() << "multiple_to_one() " << endl; RxSignal rx; TestSignal tx1; TestSignal tx2; rx.setObjectName("rx"); tx1.setObjectName("tx1"); tx2.setObjectName("tx2"); QObject::connect(&tx1, SIGNAL(testSignal(int)), &rx, SLOT(mySlot(int))); QObject::connect(&tx2, SIGNAL(testSignal(int)), &rx, SLOT(mySlot(int))); tx1.sendSignal(100); tx2.sendSignal(200); } //信号到信号的映射 void signal_to_signal() { qDebug() << "signal_to_signal() " << endl; RxSignal rx; TestSignal tx1; TestSignal tx2; rx.setObjectName("rx"); tx1.setObjectName("tx1"); tx2.setObjectName("tx2"); QObject::connect(&tx1, SIGNAL(testSignal(int)), &tx2, SIGNAL(testSignal(int))); QObject::connect(&tx2, SIGNAL(testSignal(int)), &rx, SLOT(mySlot(int))); tx1.sendSignal(100); tx2.sendSignal(200); } int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); signal_to_signal(); return a.exec(); }
从打印结果看,当tx1发送信号的时候,就会转嫁到tx2上面。信号参数会被原封不动的搬过来,将从tx1中得到的参数包裹到它自己的参数里面,,最后tx2就将它作为自己的信号发送出去了。
不可忽视的军规
1.Qt类只能在头文件中声明
2.信号与槽的原型应该完全相同
3.信号参数多于槽参数时,多余的参数被忽略(这只是个例外,将这条规则忘记吧,只记住第2条军规就行了)
4.槽函数的返回值必须是void类型
5.槽函数可以像普通成员函数一样被调用
6.信号与槽的访问属性对于connect/disconnect无效(在类的外部可以直接调用类的成员函数,破坏了面向的封装性。但是它所带来的好处,远大于它的破坏。)
信号与槽的意义
最大限度的弱化了类之间的耦合关系
在设计阶段,可以减少不必要的接口类(抽象类)
在开发阶段,对象间的交互通过信号与槽动态绑定
小结:
信号只是一个特殊的成员函数声明
信号必须使用signals关键字进行声明
信号与槽可以存在多种对应关系
信号与槽机制使得类间关系松散,提高类的可复用性