从Qobject(QObject.h)源码中可以看到QObject::connect的定义是这样的:

  1. static bool connect(const QObject *sender, const char *signal,
  2. const QObject *receiver, const char *member, Qt::ConnectionType =
  3. #ifdef qdoc
  4. Qt::AutoConnection
  5. #else
  6. #ifdef QT3_SUPPORT
  7. Qt::AutoCompatConnection
  8. #else
  9. Qt::AutoConnection
  10. #endif
  11. #endif
  12. );
  13. inline bool connect(const QObject *sender, const char *signal,
  14. const char *member, Qt::ConnectionType type =
  15. #ifdef qdoc
  16. Qt::AutoConnection
  17. #else
  18. #ifdef QT3_SUPPORT
  19. Qt::AutoCompatConnection
  20. #else
  21. Qt::AutoConnection
  22. #endif
  23. #endif
  24. ) const;

其中第二个connect的实现其实只有一句话:

  1. { return connect(asender, asignal, this, amember, atype); }

所以对于connect函数的学习其实就是研究第一个connect函数。

我们在使用connect函数的时候一般是这样调用的:

  1. connect(sender,SIGNAL(signal()),receiver,SLOT(slot()));

这里用到了两个宏:SIGNAL() 和SLOT();通过connect声明可以知道这两个宏最后倒是得到一个const char*类型。
在qobjectdefs.h中可以看到SIGNAL() 和SLOT()的宏定义:

  1. #ifndef QT_NO_DEBUG
  2. # define QLOCATION "\0"__FILE__":"QTOSTRING(__LINE__)
  3. # define METHOD(a)   qFlagLocation("0"#a QLOCATION)
  4. # define SLOT(a)     qFlagLocation("1"#a QLOCATION)
  5. # define SIGNAL(a)   qFlagLocation("2"#a QLOCATION)
  6. #else
  7. # define METHOD(a)   "0"#a
  8. # define SLOT(a)     "1"#a
  9. # define SIGNAL(a)   "2"#a
  10. #endif

所以这两个宏的作用就是把函数名转换为字符串并且在前面加上标识符。

比如:SIGNAL(read())展开后就是"2read()";同理SLOT(read())展开后就是"1read()"。

  1. connect(sender,SIGNAL(signal()),receiver,SLOT(slot()));
  2. 实际上就是connect(sender,“2signal()”,receiver,“1slot())”;

搞明白了实际的参数就可以来看connect的真正实现过程了,在QObject.cpp文件中可以找到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. if (sender == 0 || receiver == 0 || signal == 0 || method == 0) {
  11. qWarning("QObject::connect: Cannot connect %s::%s to %s::%s",
  12. sender ? sender->metaObject()->className() : "(null)",
  13. (signal && *signal) ? signal+1 : "(null)",
  14. receiver ? receiver->metaObject()->className() : "(null)",
  15. (method && *method) ? method+1 : "(null)");
  16. return false;
  17. }
  18. QByteArray tmp_signal_name;
  19. if (!check_signal_macro(sender, signal, "connect", "bind"))
  20. return false;
  21. const QMetaObject *smeta = sender->metaObject();
  22. const char *signal_arg = signal;
  23. ++signal; //skip code
  24. int signal_index = smeta->indexOfSignal(signal);
  25. if (signal_index < 0) {
  26. // check for normalized signatures
  27. tmp_signal_name = QMetaObject::normalizedSignature(signal - 1);
  28. signal = tmp_signal_name.constData() + 1;
  29. signal_index = smeta->indexOfSignal(signal);
  30. if (signal_index < 0) {
  31. err_method_notfound(sender, signal_arg, "connect");
  32. err_info_about_objects("connect", sender, receiver);
  33. return false;
  34. }
  35. }
  36. QByteArray tmp_method_name;
  37. int membcode = extract_code(method);
  38. if (!check_method_code(membcode, receiver, method, "connect"))
  39. return false;
  40. const char *method_arg = method;
  41. ++method; // skip code
  42. const QMetaObject *rmeta = receiver->metaObject();
  43. int method_index = -1;
  44. switch (membcode) {
  45. case QSLOT_CODE:
  46. method_index = rmeta->indexOfSlot(method);
  47. break;
  48. case QSIGNAL_CODE:
  49. method_index = rmeta->indexOfSignal(method);
  50. break;
  51. }
  52. if (method_index < 0) {
  53. // check for normalized methods
  54. tmp_method_name = QMetaObject::normalizedSignature(method);
  55. method = tmp_method_name.constData();
  56. switch (membcode) {
  57. case QSLOT_CODE:
  58. method_index = rmeta->indexOfSlot(method);
  59. break;
  60. case QSIGNAL_CODE:
  61. method_index = rmeta->indexOfSignal(method);
  62. break;
  63. }
  64. }
  65. if (method_index < 0) {
  66. err_method_notfound(receiver, method_arg, "connect");
  67. err_info_about_objects("connect", sender, receiver);
  68. return false;
  69. }
  70. if (!QMetaObject::checkConnectArgs(signal, method)) {
  71. qWarning("QObject::connect: Incompatible sender/receiver arguments"
  72. "\n        %s::%s --> %s::%s",
  73. sender->metaObject()->className(), signal,
  74. receiver->metaObject()->className(), method);
  75. return false;
  76. }
  77. int *types = 0;
  78. if ((type == Qt::QueuedConnection || type == Qt::BlockingQueuedConnection)
  79. && !(types = queuedConnectionTypes(smeta->method(signal_index).parameterTypes())))
  80. return false;
  81. QMetaObject::connect(sender, signal_index, receiver, method_index, type, types);
  82. const_cast<QObject*>(sender)->connectNotify(signal - 1);
  83. return true;
  84. }

上面是去除了debug代码的connect实现。

  1. const void *cbdata[] = { sender, signal, receiver, method, &type };
  2. if (QInternal::activateCallbacks(QInternal::ConnectCallback, (void **) cbdata))
  3. return true;

判断连接是否已经建立。
QInternal::ConnectCallback在qglobal.cpp中实现。

  1. bool QInternal::activateCallbacks(Callback cb, void **parameters)
  2. {
  3. Q_ASSERT_X(cb >= 0, "QInternal::activateCallback()", "Callback id must be a valid id");
  4. QInternal_CallBackTable *cbt = global_callback_table();
  5. if (cbt && cb < cbt->callbacks.size()) {
  6. QList<qInternalCallback> callbacks = cbt->callbacks[cb];
  7. bool ret = false;
  8. for (int i=0; i<callbacks.size(); ++i)
  9. ret |= (callbacks.at(i))(parameters);
  10. return ret;
  11. }
  12. return false;
  13. }

QInternal_CallBackTable 定义为(qglobal.cpp)

  1. struct QInternal_CallBackTable {
  2. QVector<QList<qInternalCallback> > callbacks;
  3. };

qInternalCallback定义为(qnamespace.h)

  1. typedef bool (*qInternalCallback)(void **);这是一个函数指针 返回值是bool,只有一个参数为void**。这个指针在调用registerCallback加入列表。
  1. if (!check_signal_macro(sender, signal, "connect", "bind"))
  2. return false;

判断signal是否合法。

在QObject.cpp文件中可以找到check_signal_macro的实现

  1. static bool check_signal_macro(const QObject *sender, const char *signal,
  2. const char *func, const char *op)
  3. {
  4. int sigcode = extract_code(signal);
  5. if (sigcode != QSIGNAL_CODE) {
  6. if (sigcode == QSLOT_CODE)
  7. qWarning("Object::%s: Attempt to %s non-signal %s::%s",
  8. func, op, sender->metaObject()->className(), signal+1);
  9. else
  10. qWarning("Object::%s: Use the SIGNAL macro to %s %s::%s",
  11. func, op, sender->metaObject()->className(), signal);
  12. return false;
  13. }
  14. return true;
  15. }

extract的实现也在QObject中,它就是去字符串第一个字符,并且只取低2位的值。

  1. static int extract_code(const char *member)
  2. {
  3. // extract code, ensure QMETHOD_CODE <= code <= QSIGNAL_CODE
  4. return (((int)(*member) - '0') & 0x3);
  5. }

这里又有两个宏:QSIGNAL_CODE 和QSLOT_CODE。它们也是在qobjectdefs.h文件中定义的。

  1. #ifdef QT3_SUPPORT
  2. #define METHOD_CODE   0                        // member type codes
  3. #define SLOT_CODE     1
  4. #define SIGNAL_CODE   2
  5. #endif

这个定义与之前的SIGNAL和SLOT的定义是对应的。

  1. const QMetaObject *smeta = sender->metaObject();
  2. const char *signal_arg = signal;
  3. ++signal; //skip code
  4. int signal_index = smeta->indexOfSignal(signal);
  5. if (signal_index < 0) {
  6. // check for normalized signatures
  7. tmp_signal_name = QMetaObject::normalizedSignature(signal - 1);
  8. signal = tmp_signal_name.constData() + 1;
  9. signal_index = smeta->indexOfSignal(signal);
  10. if (signal_index < 0) {
  11. err_method_notfound(sender, signal_arg, "connect");
  12. err_info_about_objects("connect", sender, receiver);
  13. return false;
  14. }
  15. }

获取signal的索引。

metaObject()是在moc_name.cpp文件中生成的。

  1. return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject;

其中staticMetaObject也是在moc文件中定义的

  1. const QMetaObject MainWindow::staticMetaObject = {
  2. { &QMainWindow::staticMetaObject, qt_meta_stringdata_MainWindow,
  3. qt_meta_data_MainWindow, 0 }
  4. };

qt_meta_stringdata_MainWindow(具体名字和类名有关)就是staticconstchar[]类型。它记录了全部的signals和slots等的函数名、返回值和参数表的信息。

qt_meta_data_MainWindow(具体名字和类名有关)是staticconstuint[]类型。它记录了每一个函数的函数名、返回值和参数表在qt_meta_stringdata_MainWindow中的索引。同时它还记录了每一个函数的类型具体在qmetaobject.cpp文件中定义。

  1. enum MethodFlags  {
  2. AccessPrivate = 0x00,
  3. AccessProtected = 0x01,
  4. AccessPublic = 0x02,
  5. AccessMask = 0x03, //mask
  6. MethodMethod = 0x00,
  7. MethodSignal = 0x04,
  8. MethodSlot = 0x08,
  9. MethodConstructor = 0x0c,
  10. MethodTypeMask = 0x0c,
  11. MethodCompatibility = 0x10,
  12. MethodCloned = 0x20,
  13. MethodScriptable = 0x40
  14. };

indexOfSignal(signal);的实现在qmetaobject.cpp中。其主要作用是利用qt_meta_stringdata_MainWindow 和qt_meta_data_MainWindow查找已经定义了的signal并返回索引。

  1. QByteArray tmp_method_name;
  2. int membcode = extract_code(method);
  3. if (!check_method_code(membcode, receiver, method, "connect"))
  4. return false;
  5. const char *method_arg = method;
  6. ++method; // skip code
  7. const QMetaObject *rmeta = receiver->metaObject();
  8. int method_index = -1;
  9. switch (membcode) {
  10. case QSLOT_CODE:
  11. method_index = rmeta->indexOfSlot(method);
  12. break;
  13. case QSIGNAL_CODE:
  14. method_index = rmeta->indexOfSignal(method);
  15. break;
  16. }
  17. if (method_index < 0) {
  18. // check for normalized methods
  19. tmp_method_name = QMetaObject::normalizedSignature(method);
  20. method = tmp_method_name.constData();
  21. switch (membcode) {
  22. case QSLOT_CODE:
  23. method_index = rmeta->indexOfSlot(method);
  24. break;
  25. case QSIGNAL_CODE:
  26. method_index = rmeta->indexOfSignal(method);
  27. break;
  28. }
  29. }
  30. if (method_index < 0) {
  31. err_method_notfound(receiver, method_arg, "connect");
  32. err_info_about_objects("connect", sender, receiver);
  33. return false;
  34. }

校验method并且查找它的索引。过程与signal类似。

  1. if (!QMetaObject::checkConnectArgs(signal, method)) {
  2. qWarning("QObject::connect: Incompatible sender/receiver arguments"
  3. "\n        %s::%s --> %s::%s",
  4. sender->metaObject()->className(), signal,
  5. receiver->metaObject()->className(), method);
  6. return false;
  7. }

判断signal和method是否兼容,checkConnectArgs函数的在qmetaObject.cpp文件中实现。这个函数校验了signal和method的参数。当两者的参数一致或method参数比signal参数少(method与signal前几个参数一致)的时候返回true,其它返回false。

  1. int *types = 0;
  2. if ((type == Qt::QueuedConnection || type == Qt::BlockingQueuedConnection)
  3. && !(types = queuedConnectionTypes(smeta->method(signal_index).parameterTypes())))
  4. return false;

如果是以发消息的方式执行method就需要对参数类型进行判断。queuedConnectionTypes在QObject.cpp实现。实际上是在QMetatype.cpp中定义了一个

static conststruct { constchar * typeName;int type;} types[];在这里记录了全部类型和名称如({"void",QMetaType::Void});Void在Qmetatype.h中定义。

  1. QMetaObject::connect(sender, signal_index, receiver, method_index, type, types);

调用QMetaObject的connect函数,再次不详细写出。

  1. const_cast<QObject*>(sender)->connectNotify(signal - 1);

最后调用虚函数connectNotify表示connect已经执行完成。

QT QObject::connect函数的学习的更多相关文章

  1. 很多人以为 connect 和 disconnect 应该像 new 和 delete 一样成对出现 这是错误的(只要 sender 或 receiver 其中之一不存在了,connect 会自动失效。QObject::connect 函数是线程安全的)

    其实我写文章也是边查资料边编辑的 有时候是怕自己的阐述不严谨,有时候是怕自己重复造轮子 就像有些人不停的教大家QLabel QDialog QWidget 个人是不屑的 命令模式 用 Qt's Und ...

  2. QT运行出错:QObject::connect: Parentheses expected以及QObject::connect: No such slot ***

    我在QGraphicsScene子类中添加了item的弹出菜单,并连接Action到槽函数,结果槽函数不起作用,输出:QObject::connect: No such slot ***  C++ C ...

  3. Qt error ------ no matching function for call to QObject::connect(QSpinBox*&, <unresolved overloaded function type>, QSlider*&, void (QAbstractSlider::*)(int))

    connect(ui->spinBox_luminosity,&QSpinBox::valueChanged, ui->horizontalSlider_luminosity, & ...

  4. 【QT】跨线程的信号槽(connect函数)

    线程的信号槽机制需要开启线程的事件循环机制,即调用QThread::exec()函数开启线程的事件循环. Qt信号-槽连接函数原型如下: bool QObject::connect ( const Q ...

  5. Qt - 错误总结 - QObject::connect: Cannot queue arguments of type 'PVCI_CAN_OBJ' (Make sure 'PVCI_CAN_OBJ' is registered using qRegisterMetaType().)

    背景:一个线程通过signal-slot发送PVCI_CAN_OBJ类型的值到主线程中, 错误提示: QObject::connect: Cannot queue arguments of type ...

  6. Qt解决:Qobject::connect queue arguments of type ‘xxxx’,Make sure ‘xxxx’ is registered using qRegister

    解决方法:在调用connect之前,通过 qRegisterMetaType() 注册你connect函数里对象的类型代码如下: typedef QString CustomString;//你自己定 ...

  7. Qt 信号和槽异常: QObject::connect: No Such slot baseClassName::subClassfunction() in ......

    2019-08-14起笔 小熊的情况描述: 父类A继承自QWidget,所以父类A自动添加了Q_OBJECT.  子类B继承自父类A,子类B没有添加Q_OBJECT.在子类B中给动态创建的控件添加事件 ...

  8. Qt和c/c++connect函数冲突解决方法

    在使用c/c++的connect函数时在前面写::connect()这样就可以解决了

  9. Qt: The State Machine Framework 学习

    State Machine,即为状态机,是Qt中一项非常好的框架.State Machine包括State以及State间的Transition,构成状态和状态转移.通过状态机,我们可以很方便地实现很 ...

随机推荐

  1. 0xc000000f: Error attempting to read the boot configuration data

    Get the fix to “0xc000000f: error attempting to read the boot configuration data” boot error for Win ...

  2. iOS:实现表格填充和选择操作

    功能:创建一个列表,用数组填充表格,并支持选择列表行 // // main.m // Hello // // Created by lishujun on 14-8-28. // Copyright ...

  3. int和Integer——个人学习

    1.首先要知道Java的八大基本数据类型:short.int.long.float.double.char.byte.boolean. 2.这八种基本数据类型对应的包装类分别为:Short.Integ ...

  4. 使用spm build 批量打包压缩seajs 代码

    一,安装环境 1.安装spm spm工具是基于node(nodejs的服务平台)的,因此我们需要先安装 node 和 npm 下载地址:http://nodejs.org/#download.下载完成 ...

  5. [CF Round #294 div2] E. A and B and Lecture Rooms 【树上倍增】

    题目链接:E. A and B and Lecture Rooms 题目大意 给定一颗节点数10^5的树,有10^5个询问,每次询问树上到xi, yi这两个点距离相等的点有多少个. 题目分析 若 x= ...

  6. 玩SSH,SFTP

    更改SFTP的本地路径,记得前面要加l哟,应该表示local的意思.如lls,lcd. 证书SSH更安全.就是多服务布置有些烦琐~~

  7. Hibernate 缓存 关于注解方式

    要引入 import org.hibernate.annotations.Cache; 在类前面添加: @Cache(usage= CacheConcurrencyStrategy.NONSTRICT ...

  8. gridview数据导出到word和excel以及excel的导入

    using System;using System.Collections.Generic;using System.Linq;using System.Web;using System.Web.UI ...

  9. Spring MVC 解读——<context:component-scan/>

    转自:http://my.oschina.net/HeliosFly/blog/203149 作者:GoodLoser. Spring MVC 解读---<context:component-s ...

  10. oracle的存储过程语法(转)

    1.ORA-00942: table or view does not exist 指的你要操作的表尚未存在,需要先create出来先. 2.ORA-00922: missing or invalid ...