关于信号与槽
信号和槽的机制是类型安全的:一个信号的签名必须与它的接收槽的签名相匹配。(实际上一个槽的签名可以比它接收的信号的签名少,因为它可以忽略额外的签名。)因为签名是一致的,编译器就可以帮助我们检测类型不匹配。信号和槽是宽松地联系在一起的:一个发射信号的类不用知道也不用注意哪个槽要接收这个信号。Qt的信号和槽的机制可以保证如果你把一个信号和一个槽连接起来,槽会在正确的时间使用信号的参数而被调用。信号和槽可以使用任何数量、任何类型的参数。
建立信号与槽的连接
信号与槽的类必须是QObject的派生类,在类中必须加Q_OBJECT的宏定义。连接信号与槽时调用如下函数:
QObject::connect(&data, SIGNAL(ValueChange(Subject*)), &view, SLOT(Update(Subject* )));
connect 是一个静态函数
data 是信号类的对象
ValueChange 是定义的信号,其参数与槽的参数必须匹配
view 是槽类的对象
Update 是定义的槽
SIGNAL,SLOT 是宏定义,具体的不清楚。
解除已经建立好连接的信号和槽
如果要解除已经建立好连接的信号和槽,可以使用disconnect()函数。
bool QObject::disconnect ( const QObject * sender, const char * signal,
const Object * receiver, const char * member )
这个函数断开发射者中的信号和接收者中的槽函数之间的关联。有以下三种情况:
1、断开某个对象与其它对象的任何连接:
disconnect(object, 0, 0, 0);或object->disconnect();
2、断开某个信号与其它任何槽的连接:
disconnect(object, SIGNAL(signal()), 0, 0);或object->disconnect(SIGNAL(signal()));
3、断开两个对象之间的任何关联:
disconnect(object, 0, receiver, 0);或object->disconnect(receiver);
信号和槽的注意事项
1) 信号和槽必须是QObject 的派生类的成员函数。
2) 如果使用多重继承,QObject必须是第一父类。
3) 在类声明中必须出现Q_OBJECT语句。
4) 信号和槽的机制是非常有效的,但是它不像“真正的”回调那样快。信号和 槽稍微有些慢,这是因为它们所提供的灵活性。
5) 信号和槽机制与普通函数的调用一样,如果使用不当的话,在程序执行时有 可能形成死循环,所以,在定义槽函数时一定要注意避免间接形成无限循环, 即在槽中再次发射所接收到的同样的信号 。
6) 信号可以和多个槽相关联的话,那当这个信号被发射时,与之相关联的槽的 执行顺序将是随机的,且顺序不能指定 。
7) 信号也可以与另外一个信号连接。
8) 宏定义不能用在signal和slot的参数中 。
9) 信号不能用于模板。
10) 信号和槽的参数不能是函数指针。
11) 信号和槽不能有缺省参数值 。
12) 友元声明不能位于信号和槽的声明区域内 。
13) 信号和槽不能被重定义。
代码示例
产生信号的类:
#ifndef SUBJECT_H
#define SUBJECT_H
#include <QObject>
#include <string>
using namespace std;
class Subject : public QObject /*与信号和槽相关的类必须从此类派生*/
{
Q_OBJECT //声明
public:
Subject(QObject * parent = 0);// QObject类的构造函数可以有个参数parent
virtual ~Subject();
void SetState(string state);
string GetState();
signals: /*标记开始声明信号*/
void ValueChange(Subject* sub); /*声明一个信号*/
private:
string m_state;
};
#endif // SUBJECT_H
SUBJECT源文件:
#include <string>
#include "Subject.h"
Subject::Subject(QObject * parent /*= 0*/)
:QObject(parent) /*父类的构造函数*/
{
m_state = "";
}
Subject::~Subject()
{}
void Subject::SetState(string state)
{
if ( m_state != state)
{
m_state = state;
emit ValueChange(this);
}
}
string Subject::GetState()
{
return m_state;
}
槽相关类:
#ifndef OBSERVER_H
#define OBSERVER_H
#include <QObject>
#include "Subject.h"
class Observer:public QObject
{
Q_OBJECT
public:
Observer(QObject * parent = 0);
~Observer();
virtual void PrintInfo(Subject* sub);
public slots: /**标记开始声明槽/
void Update(Subject* sub);/*声明一个槽,就是一个函数*/
};
#endif // OBSERVER_H
OBSERVER实现源文件:
#include <iostream>
#include "Observer.h"
Observer::Observer(QObject * parent /*= 0*/)
:QObject(parent)
{}
Observer::~Observer()
{}
void Observer::PrintInfo(Subject* sub)
{
string state;
state = sub->GetState();
cout<<" state = "<< state<<endl;
}
void Observer::Update(Subject* sub)
{
this->PrintInfo(sub);
}
最后在主函数中建立信号与槽的连接
#include <QtCore/QCoreApplication>
#include <QObject>
#include "Observer.h"
#include "Subject.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Subject data;
Observer view;
QObject::connect(&data, SIGNAL(ValueChange(Subject*)), &view, SLOT(Update(Subject* )));
data.SetState("change the value");
return a.exec();
}
当然,信号与槽的连接可以放在其他任何地方,只要知道对应信号与槽对象的指针。可以放在槽对应类的构造函数,构造时传递一个信号类,然后建立连接。