信号槽是QT中用于对象间通讯的一种机制,也是QT的核心机制。在GUI编程中,咱们常常须要在改变一个组件的同时,通知另外一个组件作出响应。例如:
编程
一开始咱们的Find按钮是未激活的,用户输入要查找的内容后,查找按钮就被激活,这就是输入框与Find按钮这两个组件间通讯的例子。
安全
早期,对象间的通讯采用回调来实现。回调其实是利用函数指针来实现,当咱们但愿某件事发生时处理函数可以得到通知,就须要将回调函数的指针传递给处理函数,这样处理函数就会在合适的时候调用回调函数。回调有两个明显的缺点:app
在QT中,咱们有回调技术以外的选择,也便是信号槽机制。所谓的信号与槽,其实都是函数。当特定事件被触发时(如在输入框输入了字符)将发送一个信号,而与该信号创建的链接槽,则能够接收到该信号并作出反应(激活Find按钮)。
QT组件预约义了不少信号和槽,而在GUI编程中,咱们习惯于继承那些组件,继承后添加咱们本身的槽,以便以咱们的方式来处理信号。槽和普通的C++成员函数几乎是同样的,它能够是虚函数,能够被重载,能够是共有、私有或是保护的,也一样能够被其余成员函数调用。它的函数参数也能够是任意类型的。惟一不一样的是:槽还能够和信号链接在一块儿。
与回调不一样,信号槽机制是类型安全的。这体如今信号的函数签名与槽的函数签名必须匹配上,才可以发生信号的传递。实际上,槽的参数个数能够比信号的参数个数少,由于槽可以忽略信号形参中多出来的参数。信号和槽是松耦合的:发出信号的类不关心哪些类将接收它的信号。QT的信号槽机制吧哦这里在正确的时间,槽可以接收到信号的参数并调用。信号和槽均可以有任意个数的参数,它们都是类型安全的。函数
首先咱们要知道的是,全部继承自QObject或者它的子类(如QWidget)均可以包含信号槽。咱们写的类须继承自QObject(或其子类)。全部包含了信号槽的类都必须在声明的上部含有Q_OBJECT宏。
一个基于QObject的C++简单类:this
//MyStr.h # ifndef MYSTR # define MYSTR #include<QObject> #include<QString> class MyStr :public QObject { Q_OBJECT //必须包含的宏 public: MyStr (){m_value = "zero";} QString value() const {return m_value;} public slots : void setValue(QString value ); signals: //信号 void valueChanged(QString newValue); private: QString m_value; }; #endif
# define signals public
Signal的代码会由 moc 自动生成,开发人员必定不能在本身的C++代码中实现它。设计
反之,槽应该由编程人员来实现,下面提供MyStr::setVaule()的一种可能实现指针
#include"MyStr.h" void MyStr::setValue(QString value) { if(value != m_value) { m_value = value; emit valueChanged(value); } }
setValue函数首先比较新参的值与数据成员的值是不是同样的(后面有解释为什么这样作),若是不是,则设置好数据成员m_value的值,而后,把信号valueChanged()发送出去。发送给谁?类并无写,这并非类设计者所关心的,也不是类所关心的,它只管把信号发送出去就行。而后,咱们再来设置谁来接收这个信号。code
int main(int argc, char *argv[]) { MyStr a; MyStr b; QObject::connect(&a,SIGNAL(valueChanged(QString)),&b,SLOT(setValue(QString))); a.setValue("this is A"); return 0; }
咱们定义了两个类对象a/b,使用 QObject::connect()函数指定了发送方、信号、接收方、槽等信息,connect函数的格式以下:对象
QObject::connect( 发送方, SIGNAL(...), 接收方, SLOT(..) );
当咱们调用a的成员函数setValue时,该函数除了把a.m_value设置为"this is A",也把信号valueChanged()发送出去,被b.setValue所接收,从而,把b.m_value设置为"this is A",同时b.setValue又把valueChanged信号发射出去,然而该信号并无对象接收,由于咱们没有创建以b为发送方的任何链接。此时你应该明白,为什么在emit前须要判断value != m_value,由于若是没有此步骤,且恰巧设置了blog
QObject::connect(&b,SIGNAL(valueChanged(QString)),&a,SLOT(setValue(QString)));
则b的信号被a接收,a又发送信号被b接收,如此进入死循环。
int main(int argc, char *argv[]) { QApplication app(argc, argv); MyStr a; MyStr b; QObject::connect(&a,SIGNAL(valueChanged(QString)),&b,SLOT(setValue(QString))); a.setValue("this is A"); QLabel* label = new QLabel; label->setText( b.value()); label->show(); return app.exec(); }
咱们使用label输出来看看b是否接收到a的信号,若是是,则b的内容应该是"this is A",输出在label上,程序运行结果:
这个例子展现了对象之间通讯的一种方式。对象间能够一块儿工做,而不须要知道彼此的任何信息。为了达到通讯的目的,只须要将它们链接起来,而这只须要经过 调用 QObject::connect() 函数指定一些简单信息就好。
要把信号成功链接到槽,它们的参数必须具备相同的顺序和相同的类型,或者容许信号的参数比槽多,槽会自动忽略掉多出来的参数而进行调用。
使用QObject::connect能够把一个信号链接到多个槽,而当信号发射时,将按声明联系时的顺序依次调用槽。
MyStr a; MyStr b; MyStr c; //信号链接到两个槽 QObject::connect(&a,SIGNAL(valueChanged(QString)),&b,SLOT(setValue(QString))); QObject::connect(&a,SIGNAL(valueChanged(QString)),&c,SLOT(setValue(QString))); a.setValue("this is A"); //依次调用b.setValue()、c.setValue()
一样的,可让多个信号链接到同一个槽上 ,并且其中的每个信号的发送,都会调用了那个槽。
MyStr a; MyStr b; MyStr c; //两个信号链接到同一个槽 QObject::connect(&a,SIGNAL(valueChanged(QString)),&c,SLOT(setValue(QString))); QObject::connect(&b,SIGNAL(valueChanged(QString)),&c,SLOT(setValue(QString))); //下面的操做皆会调用到槽c.setValue() a.setValue("this is A"); b.setValue("this is B");
当发射第一个信号的时候,也会把第二个信号一个发送出去。
MyStr a; MyStr b; MyStr c; //两个信号相链接 QObject::connect(&a,SIGNAL(valueChanged(QString)),&b,SIGNAL(valueChanged(QString))); //再创建b与c的链接 QObject::connect(&b,SIGNAL(valueChanged(QString)),&c,SLOT(setValue(QString))); //下面的操做同时发送了信号a.valueChanged与b.valueChanged a.setValue("this is A"); //从而信号b.valueChanged被槽c.setValue所接收
//移除b 与 c之间的链接 QObject::disconnect(&b,SIGNAL(valueChanged(QString)),&c,SLOT(setValue(QString)));
实际上当对象被delete时,其关联的全部连接都会失效,QT会自动移除和这个对象的全部连接。
感谢您耐心的阅读。