Chinaunix首页 | 论坛 | 博客
  • 博客访问: 433186
  • 博文数量: 117
  • 博客积分: 3003
  • 博客等级: 中校
  • 技术积分: 1221
  • 用 户 组: 普通用户
  • 注册时间: 2010-08-16 14:11
文章分类

全部博文(117)

文章存档

2011年(7)

2010年(110)

我的朋友

分类: LINUX

2010-11-24 17:29:08


本篇探析signal slot的连接和调用是如何实现的。

宏SLOT,SIGNAL

在qobjectdefs.h中有这样的定义:

# define METHOD(a)   "0"#a
# define SLOT(a)     "1"#a
# define SIGNAL(a)   "2"#a

不过是在方法签名之前加了一个数字标记。因为我们既可以将signal连接到slot,也可以将signal连接到signal,所有必须要有某种方法区分一下。

QObject::connect()

  1. bool QObject::connect(const QObject *sender, const char *signal,  
  2.                       const QObject *receiver, const char *method,  
  3.                       Qt::ConnectionType type)  
  4. {  
  5.     {  
  6.         const void *cbdata[] = { sender, signal, receiver, method, &type };  
  7.         if (QInternal::activateCallbacks(QInternal::ConnectCallback, (void **) cbdata))  
  8.             return true;  
  9.     }  
  10. #ifndef QT_NO_DEBUG  
  11.     bool warnCompat = true;  
  12. #endif  
  13.     if (type == Qt::AutoCompatConnection) {  
  14.         type = Qt::AutoConnection;  
  15. #ifndef QT_NO_DEBUG  
  16.         warnCompat = false;  
  17. #endif  
  18.     }  
  19.     if (sender == 0 || receiver == 0 || signal == 0 || method == 0) {  
  20.         qWarning("QObject::connect: Cannot connect %s::%s to %s::%s",  
  21.                  sender ? sender->metaObject()->className() : "(null)",  
  22.                  (signal && *signal) ? signal+1 : "(null)",  
  23.                  receiver ? receiver->metaObject()->className() : "(null)",  
  24.                  (method && *method) ? method+1 : "(null)");  
  25.         return false;  
  26.     }  
  27.     QByteArray tmp_signal_name;  
  28.     if (!check_signal_macro(sender, signal, "connect""bind"))  
  29.         return false;  
  30.     const QMetaObject *smeta = sender->metaObject();  
  31.     const char *signal_arg = signal;  
  32.     ++signal; //skip code  
  33.     int signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal);  
  34.     if (signal_index < 0) {  
  35.         // check for normalized signatures  
  36.         tmp_signal_name = QMetaObject::normalizedSignature(signal - 1);  
  37.         signal = tmp_signal_name.constData() + 1;  
  38.         smeta = sender->metaObject();  
  39.         signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal);  
  40.         if (signal_index < 0) {  
  41.             err_method_notfound(sender, signal_arg, "connect");  
  42.             err_info_about_objects("connect", sender, receiver);  
  43.             return false;  
  44.         }  
  45.     }  
  46.     signal_index = QMetaObjectPrivate::originalClone(smeta, signal_index);  
  47.     int signalOffset, methodOffset;  
  48.     computeOffsets(smeta, &signalOffset, &methodOffset);  
  49.     int signal_absolute_index = signal_index + methodOffset;  
  50.     signal_index += signalOffset;  
  51.     QByteArray tmp_method_name;  
  52.     int membcode = extract_code(method);  
  53.     if (!check_method_code(membcode, receiver, method, "connect"))  
  54.         return false;  
  55.     const char *method_arg = method;  
  56.     ++method; // skip code  
  57.     const QMetaObject *rmeta = receiver->metaObject();  
  58.     int method_index = -1;  
  59.     switch (membcode) {  
  60.     case QSLOT_CODE:  
  61.         method_index = rmeta->indexOfSlot(method);  
  62.         break;  
  63.     case QSIGNAL_CODE:  
  64.         method_index = rmeta->indexOfSignal(method);  
  65.         break;  
  66.     }  
  67.     if (method_index < 0) {  
  68.         // check for normalized methods  
  69.         tmp_method_name = QMetaObject::normalizedSignature(method);  
  70.         method = tmp_method_name.constData();  
  71.         switch (membcode) {  
  72.         case QSLOT_CODE:  
  73.             method_index = rmeta->indexOfSlot(method);  
  74.             break;  
  75.         case QSIGNAL_CODE:  
  76.             method_index = rmeta->indexOfSignal(method);  
  77.             break;  
  78.         }  
  79.     }  
  80.     if (method_index < 0) {  
  81.         err_method_notfound(receiver, method_arg, "connect");  
  82.         err_info_about_objects("connect", sender, receiver);  
  83.         return false;  
  84.     }  
  85.     if (!QMetaObject::checkConnectArgs(signal, method)) {  
  86.         qWarning("QObject::connect: Incompatible sender/receiver arguments"  
  87.                  "\n        %s::%s --> %s::%s",  
  88.                  sender->metaObject()->className(), signal,  
  89.                  receiver->metaObject()->className(), method);  
  90.         return false;  
  91.     }  
  92.     int *types = 0;  
  93.     if ((type == Qt::QueuedConnection || type == Qt::BlockingQueuedConnection)  
  94.             && !(types = queuedConnectionTypes(smeta->method(signal_absolute_index).parameterTypes())))  
  95.         return false;  
  96. #ifndef QT_NO_DEBUG  
  97.     {  
  98.         QMetaMethod smethod = smeta->method(signal_absolute_index);  
  99.         QMetaMethod rmethod = rmeta->method(method_index);  
  100.         if (warnCompat) {  
  101.             if(smethod.attributes() & QMetaMethod::Compatibility) {  
  102.                 if (!(rmethod.attributes() & QMetaMethod::Compatibility))  
  103.                     qWarning("QObject::connect: Connecting from COMPAT signal (%s::%s)", smeta->className(), signal);  
  104.             } else if(rmethod.attributes() & QMetaMethod::Compatibility && membcode != QSIGNAL_CODE) {  
  105.                 qWarning("QObject::connect: Connecting from %s::%s to COMPAT slot (%s::%s)",  
  106.                          smeta->className(), signal, rmeta->className(), method);  
  107.             }  
  108.         }  
  109.     }  
  110. #endif  
  111.     if (!QMetaObjectPrivate::connect(sender, signal_index, receiver, method_index, type, types))  
  112.         return false;  
  113.     const_cast(sender)->connectNotify(signal - 1);  
  114.     return true;  
  115. }  

忽略细节,只关注主要的流程,这段代码的做的事情就是将signal在sender的meta system中的signal索引找出,以及接受者方法(signal或slot)在receiver的meta system中的索引找出来。在委托QMetaObjectPrivate::connect()执行连接。

  1. bool QMetaObjectPrivate::connect(const QObject *sender, int signal_index,  
  2.                                  const QObject *receiver, int method_index, int type, int *types)  
  3. {  
  4.     QObject *s = const_cast(sender);  
  5.     QObject *r = const_cast(receiver);  
  6.     QOrderedMutexLocker locker(signalSlotLock(sender),  
  7.                                signalSlotLock(receiver));  
  8.     if (type & Qt::UniqueConnection) {  
  9.         QObjectConnectionListVector *connectionLists = QObjectPrivate::get(s)->connectionLists;  
  10.         if (connectionLists && connectionLists->count() > signal_index) {  
  11.             const QObjectPrivate::Connection *c2 =  
  12.                 (*connectionLists)[signal_index].first;  
  13.             while (c2) {  
  14.                 if (c2->receiver == receiver && c2->method == method_index)  
  15.                     return false;  
  16.                 c2 = c2->nextConnectionList;  
  17.             }  
  18.         }  
  19.         type &= Qt::UniqueConnection - 1;  
  20.     }  
  21.     QObjectPrivate::Connection *c = new QObjectPrivate::Connection;  
  22.     c->sender = s;  
  23.     c->receiver = r;  
  24.     c->method = method_index;  
  25.     c->connectionType = type;  
  26.     c->argumentTypes = types;  
  27.     c->nextConnectionList = 0;  
  28.     QT_TRY {  
  29.         QObjectPrivate::get(s)->addConnection(signal_index, c);  
  30.     } QT_CATCH(...) {  
  31.         delete c;  
  32.         QT_RETHROW;  
  33.     }  
  34.     c->prev = &(QObjectPrivate::get(r)->senders);  
  35.     c->next = *c->prev;  
  36.     *c->prev = c;  
  37.     if (c->next)  
  38.         c->next->prev = &c->next;  
  39.     QObjectPrivate *const sender_d = QObjectPrivate::get(s);  
  40.     if (signal_index < 0) {  
  41.         sender_d->connectedSignals[0] = sender_d->connectedSignals[1] = ~0;  
  42.     } else if (signal_index < (int)sizeof(sender_d->connectedSignals) * 8) {  
  43.         sender_d->connectedSignals[signal_index >> 5] |= (1 << (signal_index & 0x1f));  
  44.     }  
  45.     return true;  
  46. }  

同样忽略细节,这段代码首先在connecttype要求UniqueConnection的时候检查一下是不是有重复的连接。然后创建一个 QObjectPrivate::Connection结构,这个结构包含了sender,receiver,接受方法的method_index,然后 加入到某个连接存储表中;连接存储表可能是一种hash结构,signal_index就是key。可以推测,当signal方法被调用时,一定会到这个 结构中查找所有连接到该signal的connection,connection保存了receiver和method index,由上一篇可知,可以很容易调用到receiver的对应method。

阅读(1750) | 评论(0) | 转发(2) |
给主人留下些什么吧!~~