从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. 小爬虫。爬取网站多页的通知标题并存取在txt文档里。

    爬取网页中通知标题的内容展示: this is  1  page!<精算学综合>科目考试参考大纲2016年上半年研究生开题报告评议审核结果公示[答辩]2016下半年研究生论文答辩及学位评定 ...

  2. js 图片base64

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name ...

  3. maya 写UI 用到的工具

    import os cmds.window() scrollLayout = cmds.scrollLayout(w=150) cmds.gridLayout( numberOfColumns=30, ...

  4. 字符串还可以这样初始化--uboot篇

  5. 判断是否为ie(包含ie11)

    if (!!window.ActiveXObject || "ActiveXObject" in window) { alert("IsIE"); }

  6. JSP环境配置

    为免以后忘记,记下了. Jdk在C盘,tomcat在D盘. 1.JAVA_HOME C:\Program Files\Java\jdk1.7.0_07 2.CATALINA_HOME D:\apach ...

  7. DPI/PPI/dp/sp/px/pt 移动设计手册

    转自DPI/PPI/dp/sp/px/pt 移动设计手册 做移动设计的同学,不管是原生app或者web app,应该对字体字号都是很头痛的问题.根本原因是,我们用唯一分辨率的电脑,设计各个不同尺寸大小 ...

  8. 【POJ2478】Farey Seque

    题意: 就是求2~n的所有欧拉函数值的和,这里就用到了快速求欧拉函数的方法.(不能暴力求了,不然必定TLE啊) 说说欧拉筛法,感觉十分机智啊~~ 对于上述代码的几个问题: 1.问:为什么i%prime ...

  9. Python 入门教程 10 ---- Student Becomes the Teacher

    第一节 1 练习 1 设置三个的字典分别为lloyd,alice,tyler 2 对每一个的字典的key都设置为"name","homework" , &quo ...

  10. 关于android布局的两个属性dither和tileMode

    首先,两个单词的中文意思分别是dither(抖动)和tileMode(平铺) 1,先来介绍tileMode(平铺) 它的效果类似于 让背景小图不是拉伸而是多个重复(类似于将一张小图设置电脑桌面时的效果 ...