Qt中的信号与槽机制解析

注:要想使用Qt的核心机制信号与槽,就必须在类的私有数据区声明Q_OBJECT宏,然后会有moc编译器负责读取这个宏进行代码转化,从而使Qt这个特有的机制得到使用。

所谓信号槽,简单来说,就像是插销一样:一个插头和一个插座。当某种事件发生之后,比如,点击一下鼠标,或者按下某个按键,此时,这个组件就回发出一个信号。如果有一个槽,正好对应上这个信号,那么,这个槽函数就回被调用。

槽函数和普通的c++成员函数没有很大的区别,它们也可以是virtual的;可以被重写;可以使public、protected或者private的;可以被其他c++函数调用;参数可以是任何类型。槽函数可以和一个信号相连接,当这个信号发生时,它就被自动调用

连接信号与槽是connect()函数,原型如下:

bool QObject::connect ( const QObject * sender, const QMetaMethod & signal, const QObject * receiver, const QMetaMethod & method, Qt::ConnectionType type = Qt::AutoConnection ) [static]

它可以把一个对象(sender)发送的信号(signal)和接收者(receiver)的槽函数(method)关联起来,这样当信号产生时与之关联的槽函数就会被执行。在connect函数里面我们用到了Qt提供的两个宏SIGNAL()和SLOT();这是Qt要求的,要关联信号和槽必须借助于这两个宏,两个宏的定义如下:

#define SLOT(name) "1"#name

#define SIGNAL(name) "2"#name

通过这两个宏,就可以把我们传递进去的槽和信号的名字转化成字符串,并在这两个字符串前面加上附加的字符。

如:

connect(ui->pushButton, SIGNAL(clicked()), this, SLOT(myslots()));

经过moc处理后就变成:

connect(ui->pushButton, "2clicked()", this, "1myslots()");

connect函数的最后一个参数type可以指定传递信号的方式,它是Qt::ConnectionType枚举类型常量,常用连接类型如下表所列。

Constant Value Description
Qt::AutoConnection 0                                                     当信号发送者和接收者处于同一线程内时,这个类型等同于DirectConnection,反之等同于QueuedConnection,这个类型也是connect函数的默认连接类型
Qt::DirectConnection 1 信号一旦发射,与之关联的槽函数立即执行
Qt::QueuedConnection 2 当信号产生,信号会暂时被缓冲到一个消息队列中,等待接收者的事件循环处理去队列中获取消息,然后执行和信号关联的槽函数,这种方式既可以在同一线程内传递消息也可以跨线程操作
Qt::BlockingQueuedConnection                                                                                                                                     4 这种类型类似于QueuedConnection,但是它只能应用于跨线程操作即发送者和接收者处于不同的线程中的情况,并且信号发送者线程会阻塞等待接收者的槽函数执行结束
Qt::AutoCompatConnection 3 当兼容Qt3程序是的默认连接类型

一个信号可以和多个槽相连(槽会一个接一个地被调用,但是调用的顺序是不确定的);

多个信号可以连接一个槽(只要任意一个信号产生,这个槽就回被调用);

一个信号可以连接到另一个信号;

槽可以被取消链接:

bool QObject::disconnect ( const QObject * sender, const QMetaMethod & signal, const QObject * receiver, const QMetaMethod & method ) [static]

这种情况并不经常出现,因为当一个对象delete之后,Qt自动取消所有连接到这个对象上面的槽。

要连接在一起的信号和槽,如果带了参数,那么这些参数的顺序和类型都要一致,并且信号带的参数的个数可以多于槽,信号在发射的时候可以把参数传递给槽函数接收,这也是不同对象交互数据的一个方法,信号可以发送多个类型的数据给槽函数,但是槽函数不一定需要全部接收处理,这样槽函数参数的个数可以少于或者等于信号参数的个数,但一定不能多于所连接信号的参数个数。为了正确的连接信号槽,信号和槽的参数类型以及出现的顺序必须相同

下面举两个例子,一个例子是使用connect函数连接信号槽,另个例子是使用Qt Creator的designer来设置信号槽(本质还是connect,只是图形化)。

例子一:

复制代码
 1 #include <QAPPlication>
 2 #include <QPushButton>
 3 
 4 int main (int argc, char *argv[])
 5 {
 6       QApplication app(argc, argv);  
 7       QPushButton *button = new QPushButton("Close");
 8       QObject::connect(button, SIGNAL(clicked()), button, SLOT(close()));
 9       button->show();
10 
11      return app.exec();
12 }    
复制代码

输入以下指令编译:

qmake -project

qmake

make

然后运行。

转:http://www.cnblogs.com/heguoyuan/p/4279597.html

=========================

 因为最近在研究Boost,而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 plain copy
  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 plain copy
  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 plain copy
  1. QObeject::connect( obj1, SIGNAL( mySignal() ), obj2, SLOT( mySlot() ) );  
  2.   
  3. QObeject::connect( obj1, SIGNAL( mySignal() ), obj2, SIGNAL( mySignal2() ) );  

Qt中信号与槽的断开:

[cpp]  view plain copy
  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 plain copy
  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. }  
转:http://blog.csdn.net/juana1/article/details/6633184

另可参考;http://www.ibm.com/developerworks/cn/linux/guitoolkit/qt/signal-slot/

  • 6
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值