QT connect函数的用法.docx
QT connect函数的用法QT QObject:connect函数的学习 从Qobject(QObject.h)源码中可以看到QObject:connect的定义是这样的: cpp view plaincopy 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. static bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt:ConnectionType = #ifdef qdoc Qt:AutoConnection #else #ifdef QT3_SUPPORT Qt:AutoCompatConnection #else Qt:AutoConnection #endif #endif ); inline bool connect(const QObject *sender, const char *signal, const char *member, Qt:ConnectionType type = #ifdef qdoc Qt:AutoConnection #else #ifdef QT3_SUPPORT Qt:AutoCompatConnection #else Qt:AutoConnection #endif #endif ) const; 其中第二个connect的实现其实只有一句话: cpp view plaincopy 1. return connect(asender, asignal, this, amember, atype); 所以对于connect函数的学习其实就是研究第一个connect函数。 我们在使用connect函数的时候一般是这样调用的: cpp view plaincopy 1. connect(sender,SIGNAL(signal),receiver,SLOT(slot); 这里用到了两个宏:SIGNAL 和SLOT;通过connect声明可以知道这两个宏最后倒是得到一个const char*类型。 在qobjectdefs.h中可以看到SIGNAL 和SLOT的宏定义: cpp view plaincopy 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. #ifndef QT_NO_DEBUG # define QLOCATION "0"_FILE_":"QTOSTRING(_LINE_) # define METHOD(a) qFlagLocation("0"#a QLOCATION) # define SLOT(a) qFlagLocation("1"#a QLOCATION) # define SIGNAL(a) qFlagLocation("2"#a QLOCATION) #else # define METHOD(a) "0"#a # define SLOT(a) "1"#a # define SIGNAL(a) "2"#a #endif 所以这两个宏的作用就是把函数名转换为字符串并且在前面加上标识符。 比如:SIGNAL(read)展开后就是"2read";同理SLOT(read)展开后就是"1read"。 cpp view plaincopy 1. 2. connect(sender,SIGNAL(signal),receiver,SLOT(slot); 实际上就是connect(sender,“2signal”,receiver,“1slot)”; 搞明白了实际的参数就可以来看connect的真正实现过程了,在QObject.cpp文件中可以找到connect的实现代码。 cpp view plaincopy 1. 2. 3. 4. 5. bool QObject:connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt:ConnectionType type) 6. const void *cbdata = sender, signal, receiver, method, &type ; if (QInternal:activateCallbacks(QInternal:ConnectCallback, (void *) cbdata) return true; if (sender = 0 | receiver = 0 | signal = 0 | method = 0) qWarning("QObject:connect: Cannot connect %s:%s to %s:%s", sender ? sender->metaObject->className : "(null)", (signal && *signal) ? signal+1 : "(null)", receiver ? receiver->metaObject->className : "(null)", (method && *method) ? method+1 : "(null)"); return false; QByteArray tmp_signal_name; if (!check_signal_macro(sender, signal, "connect", "bind") return false; const QMetaObject *smeta = sender->metaObject; const char *signal_arg = signal; +signal; /skip code int signal_index = smeta->indexOfSignal(signal); if (signal_index < 0) / check for normalized signatures tmp_signal_name = QMetaObject:normalizedSignature(signal - 1); signal = tmp_signal_name.constData + 1; signal_index = smeta->indexOfSignal(signal); if (signal_index < 0) err_method_notfound(sender, signal_arg, "connect"); err_info_about_objects("connect", sender, receiver); return false; QByteArray tmp_method_name; int membcode = extract_code(method); if (!check_method_code(membcode, receiver, method, "connect") return false; const char *method_arg = method; +method; / skip code 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71. 72. 73. 74. 75. 76. 77. 78. const QMetaObject *rmeta = receiver->metaObject; int method_index = -1; switch (membcode) case QSLOT_CODE: method_index = rmeta->indexOfSlot(method); break; case QSIGNAL_CODE: method_index = rmeta->indexOfSignal(method); break; if (method_index < 0) / check for normalized methods tmp_method_name = QMetaObject:normalizedSignature(method); method = tmp_method_name.constData; switch (membcode) case QSLOT_CODE: method_index = rmeta->indexOfSlot(method); break; case QSIGNAL_CODE: method_index = rmeta->indexOfSignal(method); break; if (method_index < 0) err_method_notfound(receiver, method_arg, "connect"); err_info_about_objects("connect", sender, receiver); return false; if (!QMetaObject:checkConnectArgs(signal, method) qWarning("QObject:connect: Incompatible sender/receiver arguments" "n %s:%s -> %s:%s", sender->metaObject->className, signal, receiver->metaObject->className, method); return false; int *types = 0; if (type = Qt:QueuedConnection | type = Qt:BlockingQueuedConnectio && !(types = queuedConnectionTypes(smeta->method(signal_index).p79. 80. 81. 82. 83. 84. 85. 86. n) 87. arameterTypes) 88. 89. 90. 91. 92. 93. return false; QMetaObject:connect(sender, signal_index, receiver, method_index, type, const_cast<QObject*>(sender)->connectNotify(signal - 1); return true; types); 上面是去除了debug代码的connect实现。 cpp view plaincopy 1. 2. a) const void *cbdata = sender, signal, receiver, method, &type ; if (QInternal:activateCallbacks(QInternal:ConnectCallback, (void *) cbdat return true; 3. 判断连接是否已经建立。 QInternal:ConnectCallback在qglobal.cpp中实现。 cpp view plaincopy 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. bool QInternal:activateCallbacks(Callback cb, void *parameters) Q_ASSERT_X(cb >= 0, "QInternal:activateCallback", "Callback id must be a valid id"); QInternal_CallBackTable *cbt = global_callback_table; if (cbt && cb < cbt->callbacks.size) QList<qInternalCallback> callbacks = cbt->callbackscb; bool ret = false; for (int i=0; i<callbacks.size; +i) ret |= (callbacks.at(i)(parameters); return ret; return false; QInternal_CallBackTable 定义为(qglobal.cpp) cpp view plaincopy 1. 2. 3. struct QInternal_CallBackTable QVector<QList<qInternalCallback> > callbacks; ; qInternalCallback定义为(qnamespace.h) cpp view plaincopy 1. typedef bool (*qInternalCallback)(void *);这是一个函数指针 返回值是bool,只有一个参数为void*。这个指针在调用registerCallback加入列表。 cpp view plaincopy 1. 2. if (!check_signal_macro(sender, signal, "connect", "bind") return false; 判断signal是否合法。 在QObject.cpp文件中可以找到check_signal_macro的实现 cpp view plaincopy 1. 2. 3. 4. 5. 6. 7. 8. static bool check_signal_macro(const QObject *sender, const char *signal, const char *func, const char *op) int sigcode = extract_code(signal); if (sigcode != QSIGNAL_CODE) if (sigcode = QSLOT_CODE) qWarning("Object:%s: Attempt to %s non-signal %s:%s", func, op, sender->metaObject->className, signal+1);9. 10. 11. 12. 13. 14. 15. else qWarning("Object:%s: Use the SIGNAL macro to %s %s:%s", func, op, sender->metaObject->className, signal); return false; return true; extract的实现也在QObject中,它就是去字符串第一个字符,并且只取低2位的值。 cpp view plaincopy 1. 2. 3. 4. 5. static int extract_code(const char *member) / extract code, ensure QMETHOD_CODE <= code <= QSIGNAL_CODE return (int)(*member) - '0') & 0x3); 这里又有两个宏:QSIGNAL_CODE 和QSLOT_CODE。它们也是在qobjectdefs.h文件中定义的。 cpp view plaincopy 1. 2. 3. 4. 5. #ifdef QT3_SUPPORT #define METHOD_CODE 0 / member type codes #define SLOT_CODE 1 #define SIGNAL_CODE 2 #endif 这个定义与之前的SIGNAL和SLOT的定义是对应的。 cpp view plaincopy 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. const QMetaObject *smeta = sender->metaObject; const char *signal_arg = signal; +signal; /skip code int signal_index = smeta->indexOfSignal(signal); if (signal_index < 0) / check for normalized signatures tmp_signal_name = QMetaObject:normalizedSignature(signal - 1); signal = tmp_signal_name.constData + 1; signal_index = smeta->indexOfSignal(signal); if (signal_index < 0) err_method_notfound(sender, signal_arg, "connect"); err_info_about_objects("connect", sender, receiver); return false; 获取signal的索引。 metaObject是在moc_name.cpp文件中生成的。 cpp view plaincopy 1. return QObject:d_ptr->metaObject ? QObject:d_ptr->metaObject : &staticMetaObject; 其中staticMetaObject也是在moc文件中定义的 cpp view plaincopy 1. 2. 3. 4. const QMetaObject MainWindow:staticMetaObject = &QMainWindow:staticMetaObject, qt_meta_stringdata_MainWindow, qt_meta_data_MainWindow, 0 ; qt_meta_stringdata_MainWindow就是staticconstchar类型。它记录了全部的signals和slots等的函数名、返回值和参数表的信息。 qt_meta_data_MainWindow是staticconstuint类型。它记录了每一个函数的函数名、返回值和参数表在qt_meta_stringdata_MainWindow中的索引。同时它还记录了每一个函数的类型具体在qmetaobject.cpp文件中定义。 cpp view plaincopy 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. enum MethodFlags AccessPrivate = 0x00, AccessProtected = 0x01, AccessPublic = 0x02, AccessMask = 0x03, /mask MethodMethod = 0x00, MethodSignal = 0x04, MethodSlot = 0x08, MethodConstructor = 0x0c, MethodTypeMask = 0x0c, MethodCompatibility = 0x10, MethodCloned = 0x20, MethodScriptable = 0x40 ; indexOfSignal(signal);的实现在qmetaobject.cpp中。其主要作用是利用qt_meta_stringdata_MainWindow 和qt_meta_data_MainWindow查找已经定义了的signal并返回索引。 cpp view plaincopy 1. 2. 3. 4. 5. QByteArray tmp_method_name; int membcode = extract_code(method); if (!check_method_code(membcode, receiver, method, "connect") return false; 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. const char *method_arg = method; +method; / skip code const QMetaObject *rmeta = receiver->metaObject; int method_index = -1; switch (membcode) case QSLOT_CODE: method_index = rmeta->indexOfSlot(method); break; case QSIGNAL_CODE: method_index = rmeta->indexOfSignal(method); break; if (method_index < 0) / check for normalized methods tmp_method_name = QMetaObject:normalizedSignature(method); method = tmp_method_name.constData; switch (membcode) case QSLOT_CODE: method_index = rmeta->indexOfSlot(method); break; case QSIGNAL_CODE: method_index = rmeta->indexOfSignal(method); break; if (method_index < 0) err_method_notfound(receiver, method_arg, "connect"); err_info_about_objects("connect", sender, receiver); return false; 校验method并且查找它的索引。过程与signal类似。 cpp view plaincopy 1. 2. 3. if (!QMetaObject:checkConnectArgs(signal, method) qWarning("QObject:connect: Incompatible sender/receiver arguments" "n %s:%s -> %s:%s", 4. 5. 6. 7. sender->metaObject->className, signal, receiver->metaObject->className, method); return false; 判断signal和method是否兼容,checkConnectArgs函数的在qmetaObject.cpp文件中实现。这个函数校验了signal和method的参数。当两者的参数一致或method参数比signal参数少的时候返回true,其它返回false。 cpp view plaincopy 1. 2. 3. 4. int *types = 0; if (type = Qt:QueuedConnection | type = Qt:BlockingQueuedConnection) && !(types = queuedConnectionTypes(smeta->method(signal_index).parameterTypes) return false; 如果是以发消息的方式执行method就需要对参数类型进行判断。queuedConnectionTypes在QObject.cpp实现。实际上是在QMetatype.cpp中定义了一个 static conststruct constchar * typeName;int type; types;在这里记录了全部类型和名称如;Void在Qmetatype.h中定义。 cpp view plaincopy 1. es); QMetaObject:connect(sender, signal_index, receiver, method_index, type, typ调用QMetaObject的connect函数,再次不详细写出。 cpp view plaincopy 1. const_cast<QObject*>(sender)->connectNotify(signal - 1); 最后调用虚函数connectNotify表示connect已经执行完成。