信号(Signal)与槽(Slot)-Qt中的典型机制

    因为下一篇关于Boost的文章会涉及到事件处理的问题,里面用的是信号和槽的机制,先拿Qt里的这个机制预研一下。诶,Boost这是够厉害的,什么先进就包含什么!要知道我以前一直以为这是Qt的专利呢。当然,这也是大多数厉害的开源软件库的高人之处,像Qt这种GUI库也包含了很多数据库,字符处理等内容,多学学吧!

    signal/slot是Qt对象以及其派生类对象之间的一种高效通信接口,它是Qt的核心特性,也是区别与其他工具包的重要地方。它完全独立于标准的C/C++语言,因此用正确的处理好信号和槽,必须借助于一个成为MOC(Meta Object Compiler)的qt工具,该工具是一个C++预处理程序,能为高层次的事件处理自动生成所需要的附加代码。

尽管它的机制很像回调函数,但是这里要注意它和与回调函数间的不同,回调函数传递的是函数指针,很容易造成程序崩溃,另一方面,回调方式紧紧的绑定了图形用户接口的功能元素,因此很难开发进行独立的分类。而signal/slot机制也能携带任意数量和任意参数,并且不会像回调函数那样产生core dumps。

    信号signal和槽Slot是用来在对象间通讯的方法,当一个特定事件发生的时候,signal会被emit出来,slot调用是用来响应相应的signal的。QT对象已经包含了许多预定义的 signal,但我们总是可以在派生类中添加新的signal。QT对象中也已经包含了许多预定义的slog,但我们可以在派生类中添加新的slot来处理我们感兴趣的signal。

    signal 和 slot 机制是类型安全的,signal 和 slot必须互相匹配(实际上,一个solt的参数可以比对应的signal的参数少,因为它可以忽略多余的参数)。signal 和 slot是松散的配对关系,发出signal的对象不关心是那个对象链接了 这个signal,也不关心是那个或者有多少slot链接到了这个 signal。QT的signal 和 slot机制保证了,如果一个signal和slot相链接,slot会在正确的时机被调用,并且是使用正确的参数。Signal和slot都可以携带任何数量和类型的参数,他们都是类型安全的。

    所有从QObject直接或者间接继承出来的类都能包含信号和槽,当一个对象的状态发生变化的时候,信号就可以被emit出来,这可能是某个其它的 对象所 关心的。这个对象并不关心有那个对象或者多少个对象链接到这个信号了,这是真实的信息封装,它保证了这个对象可以作为一个软件组件来被使用。

    槽(slot)是用来接收信号的,但同时他们也是一个普通的类成员函数,就象一个对象不关心有多少个槽链接到了它的某个信号,一个对象也不关心一个槽链接了多少个信号。这保证了用QT创建的对象是一个真实的独立的软件组件。

    一个信号可以链接到多个槽,一个槽也可以链接多个信号。同时,一个信号也可以链接到另外一个信号。所有使用了信号和槽的类都必须包含 Q_OBJECT 宏,而且这个类必须从QObject类派生(直接或者间接派生)出来,

    当一个signal被emit出来的时候,链接到这个signal的slot会立刻被调用,就好像是一个函数调用一样。当这件事情发生的时候,signal和slot机制与GUI的事件循环完全没有关系,当所有链接到这个signal的slot执行完成之后,在 emit 代码行之后的代码会立刻被执行。当有多个slot链接到一个signal的时候,这些slot会一个接着一个的、以随机的顺序被执行。

    Signal 代码会由 moc 自动生成,开发人员一定不能在自己的C++代码中实现它,并且,它永远都不能有返回值。Slot其实就是一个普通的类函数,并且可以被直接调用,唯一特殊的地方是它可以与signal相链接。C++的预处理器更改或者删除 signal, slot, emit 关键字,所以,对于C++编译器来说,它处理的是标准的C++源文件。

    此外,用户可以将N多个信号和单个槽相连接,或者将将N个槽和单个信号连接,甚至是一个信号和另外一个信号连接。这样,当信号发射时,所以与之相连的信号或者槽都会按一定的次序(没有预定的顺序,也就是说执行的先后顺序是随机的)执行,当所有与之相连的信号和槽返回后,emit才会返回。

    下面是Qt中关于信号和槽的函数,Qt中信号的定义:

siganls:
void mySignal();
void mySignal( int x );
void mySignal( int x, int y );

    其中signals是Qt的关键字,而不是C/C++的关键字。此外信号与一般函数的区别是,它的所有返回值都是void,并且它没有函数实现体,它的函数体是moc自动生成的。

Qt中槽的定义:

public slots:

void mySlot();

void mySlot( int x );

不同类型的slot有不同的操作权限,具体看slot是public、protected还是private。

Qt中信号与信号或者与槽的连接:

QObeject::connect( obj1, SIGNAL( mySignal() ), obj2, SLOT( mySlot() ) );

QObeject::connect( obj1, SIGNAL( mySignal() ), obj2, SIGNAL( mySignal2() ) );

Qt中信号与槽的断开:

QObeject::disconnect( obj1, SIGNAL( mySignal() ), obj2, SLOT( mySlot() ) );
QObeject::disconnect( obj1, SIGNAL( mySignal() ), obj2, SIGNAL( mySignal2() ) );

这种机制GUI控件的操作来说很是方便,当然也要用的恰当,用的规范和科学。

    下面是一个简单的样例程序,程序中定义了三个信号和三个槽函数,然后将信号与槽进行了关联,每个槽函数都只是弹出一个窗口(widget),信号和槽函数的声明一般位于头文件中,同时在类声明的开始位置必须加上Q_OBJECT语句,这条语句是不可缺少的,它将告诉编译器在编译之前必须先应用 moc工具进行扩展。关键字signals指出随后开始信号的声明,这里signals用的是复数形式而非单数,siganls没有public、 private、protected等属性,这点不同于slots。另外,signals、slots关键字是QT自己定义的,不是C++中的关键字。信号的声明类似于函数的声明而非变量的声明,左边要有类型,右边要有括号,如果要向槽中传递参数的话,在括号中指定每个形式参数的类型,当然,形式参数的个数可以多于一个。

//tsignal.h
...
class TsignalApp:public QMainWindow
{
Q_OBJECT
...
//信号声明区
signals:
//声明信号mySignal()
void mySignal();
//声明信号mySignal(int)
void mySignal(int x);
//声明信号mySignalParam(int,int)
void mySignalParam(int x,int y);

//槽声明区
public slots:
//声明槽函数mySlot()
void mySlot();
//声明槽函数mySlot(int)
void mySlot(int x);
//声明槽函数mySignalParam (int,int)
void mySignalParam(int x,int y);
}
...

//tsignal.cpp
...
TsignalApp::TsignalApp()
{
...
//将信号mySignal()与槽mySlot()相关联
connect(this,SIGNAL(mySignal()),SLOT(mySlot()));
//将信号mySignal(int)与槽mySlot(int)相关联
connect(this,SIGNAL(mySignal(int)),SLOT(mySlot(int)));
//将信号mySignalParam(int,int)与槽mySlotParam(int,int)相关联
connect(this,SIGNAL(mySignalParam(int,int)),SLOT(mySlotParam(int,int)));
}

// 定义槽函数mySlot()
void TsignalApp::mySlot()
{
QMessageBox::about(this,"Tsignal", "This is a signal/slot sample without
parameter.");
}

// 定义槽函数mySlot(int)
void TsignalApp::mySlot(int x)
{
QMessageBox::about(this,"Tsignal", "This is a signal/slot sample with one
parameter.");
}

// 定义槽函数mySlotParam(int,int)
void TsignalApp::mySlotParam(int x,int y)
{
char s[256];
sprintf(s,"x:%d y:%d",x,y);
QMessageBox::about(this,"Tsignal", s);
}
void TsignalApp::slotFileNew()
{
//发射信号mySignal()
emit mySignal();
//发射信号mySignal(int)
emit mySignal(5);
//发射信号mySignalParam(5,100)
emit mySignalParam(5,100);
}

你可能感兴趣的:(object,qt,工具,compiler,编译器,Signal)