分享

QT信号槽

 quasiceo 2013-03-08

QT信号槽

分类: QT 99人阅读 评论(0) 收藏 举报

</qt4GUI教程>

2.2深入介绍信号和槽

信号和槽机制是Qt编程的基础。它可以让应用程序编程人员把这些互不了解的对象绑定在一起。

Qt的元对象系统

Qt的主要成就之一就是使用了一种机制对C++进行了扩展,并且使用这种机制创建了独立的软件组件。这些组件可以绑定在一起,但任何一个组件对于它所要连接的组件的情况事先都一无所知。

这种机制称为元对象系统(meta-object system),它提供了关键的两项技术:信号---槽以及内省(introspection)内省功能对于实现信号和槽是必需的,并且允许应用程序的开发人员在运行时获得有关QObject子类的"元信息",包括一个含有对象的类名以及它所支持的信号和槽的列表。

标准C++没有对Qt的元对象系统所需要的动态元信息提供支持。Qt通过提供一个独立的moc工具解决了这个问题。Moc解析Q_OBJECT类的定义并且通过C++函数来提供可使用的信息。

这一机制是这样工作的:

Q_OBJECT宏声明了在每一个QObject子类中必须实现的一些内省函数:metaObject(),tr(),qt_metacall(),以及其他一些函数。

Qtmoc工具生成了角于由Q_OBJECT声明的所有函数和所有信号的实现。

connnect() disconnect()这样的QObject的成员函数使用这些内省函数来完成它们的工作。

</qt4GUI教程>

  1. //<网上代码>  
  2. #include "qobject.h"    
  3. class Informer:public QObject    
  4. {    
  5. Q_OBJECT;    
  6. public:         
  7. void notify()    
  8. {    
  9. for(int i=0;i<=10;i++)    
  10. {    
  11. emit send(i);    
  12. }    
  13. };    
  14. signals:    
  15. void send(int status);    
  16. };   
  17. class Receiver:public QObject    
  18. {    
  19. Q_OBJECT;    
  20. private:    
  21. int total;    
  22. public:    
  23. Receiver(){this->total=0;};    
  24. public slots:    
  25. void count(int i)    
  26. {    
  27. printf("Counting: %d+%d=%d\n",this->total,i,this->total+i);    
  28. this->total=this->total+i;    
  29. };    
  30. };   
  31. #include "main.moc"  
  32. #include <iostream>  
  33. int main(int argc, char* argv[])    
  34. {    
  35. Informer *s=new Informer();    
  36. Receiver *r1=new Receiver();    
  37. QObject::connect(s,SIGNAL(send(int)),r1,SLOT(count(int)));    
  38. s->notify();    
  39. delete s,r1;    
  40. return 0;    
  41. }    
  42. //</网上代码>  

核心问题1:Informer如何在不知道Receiver的情况下,调用Receiver的方法

这个例子中,要调用的函数为Receiver中的void count(int i)方法。调用过程如下

  1. void Informer::send(int _t1)  
  2. {  
  3.     void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };  
  4.     QMetaObject::activate(this, &staticMetaObject, 0, _a);  
  5. }  
  6. void QMetaObject::activate(QObject *sender, const QMetaObject *m, int local_signal_index,  
  7.                            void **argv)  
  8. {  
  9.  metacall(receiver, QMetaObject::InvokeMetaMethod, method, argv ? argv : empty_argv);  
  10. }  
  11. int QMetaObject::metacall(QObject *object, Call cl, int idx, void **argv)  
  12. {  
  13.      return object->qt_metacall(cl, idx, argv);  
  14. }  
  15. int Receiver::qt_metacall(QMetaObject::Call _c, int _id, void **_a)  
  16. {  
  17.     _id = QObject::qt_metacall(_c, _id, _a);  
  18.     if (_id < 0)  
  19.         return _id;  
  20.     if (_c == QMetaObject::InvokeMetaMethod) {  
  21.         switch (_id) {  
  22.         case 0: count((*reinterpret_castint(*)>(_a[1]))); break;  
  23.         default: ;  
  24.         }  
  25.         _id -= 1;  
  26.     }  
  27.     return _id;  
  28. }  

    问题的核心变成了怎么样得到receiver这个对象。Receiver来源于QObject * const receiver = c->receiver; c来源于QObjectPrivate::Connection *c = connectionLists->at(signal_index).first;

connectionLists来源于

QObjectConnectionListVector *connectionLists = sender->d_func()->connectionLists;

也就是说,receiver对象被保存在sender对象中。

现在要解决的问题是,receiver对象是如何被保存到sender对象中的。查看源码,发现在QObject::connect中向connectionLists添加元素。connect函数已包含sender对象和receiver对象,只要将receiver对象添到senderconnectionLists中即可。在日后的使用中,便能找到recevier对象了。

核心问题2:触发信号函数时系统如何找到与信号函数匹配的接收函数

这个问题可分为两个小问题:

(1)Reciver对象可能有多个方法,qt如何知道要调用哪个函数.

(2)如何将参数传给reciver对应的函数

突破口在这个段代码中

void QMetaObject::activate(QObject *sender, const QMetaObject *m, int local_signal_index,

                           void **argv)

{

//....................

 metacall(receiver, QMetaObject::InvokeMetaMethod, method, argv ? argv : empty_argv);

//....................

}

metacall函数声明如下

static int metacall(QObject *, Call, int, void **);

这个函数又调用了

 return object->qt_metacall(cl, idx, argv);

Objectreceiver对象,mocreceiver生成了qt_metacall函数。在这个函数内部,通过idx找出要调用的函数,参数地址在argv数组中。通过argv,加上强转,就可以得到参数了。

示例代码如下

  1. int Receiver::qt_metacall(QMetaObject::Call _c, int _id, void **_a)  
  2. {  
  3.     _id = QObject::qt_metacall(_c, _id, _a);  
  4.     if (_id < 0)  
  5.         return _id;  
  6.     if (_c == QMetaObject::InvokeMetaMethod) {  
  7.         switch (_id) {  
  8.         case 0: count((*reinterpret_castint(*)>(_a[1]))); break;  
  9.         default: ;  
  10.         }  
  11.         _id -= 1;  
  12.     }  
  13.     return _id;  
  14. }  

通过上面的代码,完成了调用过程。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多