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

来源:互联网 发布:新浪 算法工程师 编辑:程序博客网 时间:2024/05/21 17:16

    因为下一篇关于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中信号的定义:

[cpp] view plaincopy
  1. siganls:  
  2. void mySignal();  
  3. void mySignal( int x );  
  4. void mySignal( int x, int y );  

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

Qt中槽的定义:

[cpp] view plaincopy
  1. public slots:  
  2.   
  3. void mySlot();  
  4.   
  5. void mySlot( int x );  
  6.   
  7. <span style="font-family:SimSun;font-size:18px;">不同类型的slot有不同的操作权限,具体看slot是publicprotected还是private。</span>  

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

[cpp] view plaincopy
  1. QObeject::connect( obj1, SIGNAL( mySignal() ), obj2, SLOT( mySlot() ) );  
  2.   
  3. QObeject::connect( obj1, SIGNAL( mySignal() ), obj2, SIGNAL( mySignal2() ) );  

Qt中信号与槽的断开:

[cpp] view plaincopy
  1. QObeject::disconnect( obj1, SIGNAL( mySignal() ), obj2, SLOT( mySlot() ) );  
  2. QObeject::disconnect( obj1, SIGNAL( mySignal() ), obj2, SIGNAL( mySignal2() ) );  

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

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

[cpp] view plaincopy
  1. //tsignal.h  
  2. ...  
  3. class TsignalApp:public QMainWindow  
  4. {  
  5. Q_OBJECT  
  6. ...  
  7. //信号声明区  
  8. signals:  
  9. //声明信号mySignal()  
  10. void mySignal();  
  11. //声明信号mySignal(int)  
  12. void mySignal(int x);  
  13. //声明信号mySignalParam(int,int)  
  14. void mySignalParam(int x,int y);  
  15.   
  16. //槽声明区  
  17. public slots:  
  18. //声明槽函数mySlot()  
  19. void mySlot();  
  20. //声明槽函数mySlot(int)  
  21. void mySlot(int x);  
  22. //声明槽函数mySignalParam (int,int)  
  23. void mySignalParam(int x,int y);  
  24. }  
  25. ...  
  26.   
  27. //tsignal.cpp  
  28. ...  
  29. TsignalApp::TsignalApp()  
  30. {  
  31. ...  
  32. //将信号mySignal()与槽mySlot()相关联  
  33. connect(this,SIGNAL(mySignal()),SLOT(mySlot()));  
  34. //将信号mySignal(int)与槽mySlot(int)相关联  
  35. connect(this,SIGNAL(mySignal(int)),SLOT(mySlot(int)));  
  36. //将信号mySignalParam(int,int)与槽mySlotParam(int,int)相关联  
  37. connect(this,SIGNAL(mySignalParam(int,int)),SLOT(mySlotParam(int,int)));  
  38. }  
  39.   
  40. // 定义槽函数mySlot()  
  41. void TsignalApp::mySlot()  
  42. {  
  43. QMessageBox::about(this,"Tsignal", "This is a signal/slot sample without  
  44. parameter.");  
  45. }  
  46.   
  47. // 定义槽函数mySlot(int)  
  48. void TsignalApp::mySlot(int x)  
  49. {  
  50. QMessageBox::about(this,"Tsignal", "This is a signal/slot sample with one  
  51. parameter.");  
  52. }  
  53.   
  54. // 定义槽函数mySlotParam(int,int)  
  55. void TsignalApp::mySlotParam(int x,int y)  
  56. {  
  57. char s[256];  
  58. sprintf(s,"x:%d y:%d",x,y);  
  59. QMessageBox::about(this,"Tsignal", s);  
  60. }  
  61. void TsignalApp::slotFileNew()  
  62. {  
  63. //发射信号mySignal()  
  64. emit mySignal();  
  65. //发射信号mySignal(int)  
  66. emit mySignal(5);  
  67. //发射信号mySignalParam(5,100)  
  68. emit mySignalParam(5,100);  
  69. }  
原创粉丝点击