zoukankan      html  css  js  c++  java
  • Qt源码学习之信号槽

    Qt源码版本

      Qt 5.12.0

    moc文件

      Qt在编译之前会分析源文件,当发现包含了Q_OBJECT宏,则会生成另外一个标准的C++源文件(包含Q_OBJECT宏实现的代码,文件名为moc_+原文件名),该文件同样进入编译系统,最终被链接到二进制代码中去。此时,Qt将自己增加的扩展转换成了标准的C++文件,moc全称是Meta-Object Compiler,也就是“元对象编译器”。

    Q_OBJECT的宏定义

      位置:qobjectdefs.h

     1 /* qmake ignore Q_OBJECT */
     2 #define Q_OBJECT 
     3 public: 
     4     QT_WARNING_PUSH 
     5     Q_OBJECT_NO_OVERRIDE_WARNING 
     6     static const QMetaObject staticMetaObject; 
     7     virtual const QMetaObject *metaObject() const; 
     8     virtual void *qt_metacast(const char *); 
     9     virtual int qt_metacall(QMetaObject::Call, int, void **); 
    10     QT_TR_FUNCTIONS 
    11 private: 
    12     Q_OBJECT_NO_ATTRIBUTES_WARNING 
    13     Q_DECL_HIDDEN_STATIC_METACALL static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **); 
    14     QT_WARNING_POP 
    15     struct QPrivateSignal {}; 
    16     QT_ANNOTATE_CLASS(qt_qobject, "")

    Moc文件分析

    测试代码

     1 #include <QObject>
     2   class CTestMoc : public QObject
     3   {
     4   Q_OBJECT
     5   public:
     6   CTestMoc(){}
     7   ~CTestMoc(){}
     8   signals:
     9   void Test1();
    10   void Test2(int iTemp);
    11   private slots:
    12   void OnTest1(){}
    13   void OnTest2(int iTemp){}
    14   };

    Moc代码

      测试代码编译之后,生成moc_ctestmoc.cpp文件,包含几个重要的数据结构。

    第一个结构

    1 struct qt_meta_stringdata_CTestMoc_t {
    2     QByteArrayData data[7];
    3     char stringdata0[44];
    4 };

      data字段是一个由数组组成的数组,数组大小取决于信号/槽个数。这个数组在调用QObject::connect时用来匹配信号名或槽名。

      stringdata存放的是字符资源,存放全部的信号名、槽名、类名。  

     1 static const qt_meta_stringdata_CTestMoc_t qt_meta_stringdata_CTestMoc = {
     2     {
     3 QT_MOC_LITERAL(0, 0, 8), // "CTestMoc"
     4 QT_MOC_LITERAL(1, 9, 5), // "Test1"
     5 QT_MOC_LITERAL(2, 15, 0), // ""
     6 QT_MOC_LITERAL(3, 16, 5), // "Test2"
     7 QT_MOC_LITERAL(4, 22, 5), // "iTemp"
     8 QT_MOC_LITERAL(5, 28, 7), // "OnTest1"
     9 QT_MOC_LITERAL(6, 36, 7) // "OnTest2"
    10 
    11     },
    12     "CTestMocTest1Test2iTempOnTest1"
    13     "OnTest2"
    14 };

      qt_meta_stringdata_CTestMoc_t是一个qt_meta_stringdata_CTestMoc的实例

      QT_MOC_LITERAL(0, 0, 8),这个宏生成一个byte数组,第一参数是索引,可以看到索引是由 0-6共7个组成,对应的是data字段的长度7;第二个参数是在stringdata字段中的开始位置;第三个参数是长度。QT_MOC_LITERAL(0, 0, 8) 索引是0, 开始位置是0,长度是8,对应的字符是"CTestMoc",后面的以此类推。

    第二个结构

     

     1 static const uint qt_meta_data_CTestMoc[] = {
     2 
     3  // content:
     4        8,       // revision
     5        0,       // classname
     6        0,    0, // classinfo
     7        4,   14, // methods
     8        0,    0, // properties
     9        0,    0, // enums/sets
    10        0,    0, // constructors
    11        0,       // flags
    12        2,       // signalCount
    13 
    14  // signals: name, argc, parameters, tag, flags
    15        1,    0,   34,    2, 0x06 /* Public */,
    16        3,    1,   35,    2, 0x06 /* Public */,
    17 
    18  // slots: name, argc, parameters, tag, flags
    19        5,    0,   38,    2, 0x08 /* Private */,
    20        6,    1,   39,    2, 0x08 /* Private */,
    21 
    22  // signals: parameters
    23     QMetaType::Void,
    24     QMetaType::Void, QMetaType::Int,    4,
    25 
    26  // slots: parameters
    27     QMetaType::Void,
    28     QMetaType::Void, QMetaType::Int,    4,
    29 
    30        0        // eod
    31 };

      该数组的前14个uint 描述的是元对象的私有信息,定义在qmetaobject_p.h文件的QMetaObjectPrivate结构体当中;这个结构体中“4, 14, // methods”描述的是信号/槽的个数和在表中的偏移量,即14个uint之后是信号/槽的信息。

      从qt_meta_data_CTestMoc这个表中可以看到每描述一个信号或槽需要5个uint。

      例如,从表的第14个uint开始描述的信号信息

      // signals: name, argc, parameters, tag, flags

      1, 0, 34, 2, 0x06,

      3, 1, 35, 2, 0x06,

      name:对应的是qt_meta_stringdata_CTestMoc 索引,例如1 对应的是Test1

      argc:参数个数

      parameters : 参数的在qt_meta_data_CTestMoc这个表中的索引位置。

      // signals: parameters

      QMetaType::Void,

      QMetaType::Void, QMetaType::Int, 4,

      void 是信号的返回值,QMetaType::Int是参数类型, 4 是参数名,在qt_meta_stringdata_CTestMoc中的索引值。

      tag:这个字段的数值对应的是qt_meta_stringdata_CTestMoc 索引,在这个moc文件里对应的是一个空字符串,具体怎么用,在源代码里没看懂。

      flags:是一个特征值,是在 enum MethodFlags 枚举中定义。

     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   MethodRevisioned = 0x80
    15   };

      可以看到,槽对应的是MethodSlot 0x08, 信号对应的是MethodSignal 和AccessPublic 相或。

    第三部分

     1 void CTestMoc::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
     2 {
     3     if (_c == QMetaObject::InvokeMetaMethod) {
     4         CTestMoc *_t = static_cast<CTestMoc *>(_o);
     5         Q_UNUSED(_t)
     6         switch (_id) {
     7         case 0: _t->Test1(); break;
     8         case 1: _t->Test2((*reinterpret_cast< int(*)>(_a[1]))); break;
     9         case 2: _t->OnTest1(); break;
    10         case 3: _t->OnTest2((*reinterpret_cast< int(*)>(_a[1]))); break;
    11         default: ;
    12         }
    13     } else if (_c == QMetaObject::IndexOfMethod) {
    14         int *result = reinterpret_cast<int *>(_a[0]);
    15         {
    16             using _t = void (CTestMoc::*)();
    17             if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&CTestMoc::Test1)) {
    18                 *result = 0;
    19                 return;
    20             }
    21         }
    22         {
    23             using _t = void (CTestMoc::*)(int );
    24             if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&CTestMoc::Test2)) {
    25                 *result = 1;
    26                 return;
    27             }
    28         }
    29     }
    30 }

      qt_metacall方法通过索引调用其它内部方法。Qt动态机制不采用指针,而由索引实现。实际调用方法的工作由编译器实现。这使得信号和槽的机制执行效率比较高。

      参数由一个指向指针数组的指针进行传递,并在调用方法时进行适当的转换。使用指针是将不同类型的参数放在一个数组的唯一办法。参数索引从1开始,因为0号代表函数返回值。

    第四部分

    1 QT_INIT_METAOBJECT const QMetaObject CTestMoc::staticMetaObject = { {
    2     &QObject::staticMetaObject,
    3     qt_meta_stringdata_CTestMoc.data,
    4     qt_meta_data_CTestMoc,
    5     qt_static_metacall,
    6     nullptr,
    7     nullptr
    8 } };

      这个静态变量保存了moc文件的信号/槽的调用索引信息。

      在信号/槽绑定的时候,就是通过这些信息建立绑定关系。

    第五部分

     1 // SIGNAL 0
     2 void CTestMoc::Test1()
     3 {
     4     QMetaObject::activate(this, &staticMetaObject, 0, nullptr);
     5 }
     6 
     7 // SIGNAL 1
     8 void CTestMoc::Test2(int _t1)
     9 {
    10     void *_a[] = { nullptr, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
    11     QMetaObject::activate(this, &staticMetaObject, 1, _a);
    12 }

      从以上代码可见,信号即函数。

    结论

    1. Qt的信号/槽的调用不是通过指针方式调用的,而是通过索引方式来调用的。
    2. 信号也是一个函数。
    3. emit某个信号,就相当于调用这个信号connect时所关联的槽函数。
    4. 所谓信号槽,实际就是观察者模式。

    注意事项

    • 信号与反应槽的定义是在类中实现的。那么,非类成员的函数,比如说一个全局函数可不可以也这样做呢?答案是不行,只有是自身定义了信号的类或其子类才可以发出该种信号。
    • 一个对象的不同信号可以连接至不同的对象。当一个信号被释放时,与之连接的反应槽将被立刻执行,就象是在程序中直接调用该函数一样。信号的释放过程是阻塞的,这意味着只有当反应槽执行完毕后该信号释放过程才返回。如果一个信号与多个反应槽连接,则这些反应槽将被顺序执行,排序过程则是任意的。因此如果程序中对这些反应槽的先后执行次序有严格要求的,应特别注意。
    • 使用信号时还应注意:信号的定义过程是在类的定义过程即头文件中实现的。为了中间编译工具moc的正常运行,不要在源文件(.cpp)中定义信号,同时信号本身不应返回任何数据类型,即是空值(void)。如果你要设计一个通用的类或控件,则在信号或反应槽的参数中应尽可能使用常规数据以增加通用性。如代码中valueChanged的参数为int型,如果它使用了特殊类型如QRangeControl::Range,那么这种信号只能与RangeControl中的反应槽连接。如前所述,反应槽也是常规函数,与未定义slots的用户函数在执行上没有任何区别。
    • 但在程序中不可把信号与常规函数连接在一起,否则信号的释放不会引起对应函数的执行。要命的是中间编译程序moc并不会对此种情况报错,C++编译程序更不会报错。初学者比较容易忽略这一点,往往是程序编好了没有错误,逻辑上也正确,但运行时就是不按自己的意愿出现结果,这时候应检查一下是不是这方面的疏忽。
    • 信号槽要求信号和槽的参数一致,所谓一致,是参数类型一致。如果不一致,允许的情况是,槽函数的参数可以比信号的少,即便如此,槽函数存在的那些参数的顺序也必须和信号的前面几个一致起来。这是因为,你可以在槽函数中选择忽略信号传来的数据(也就是槽函数的参数比信号的少),但是不能说信号根本没有这个数据,你就要在槽函数中使用(就是槽函数的参数比信号的多,这是不允许的)。
    • SIGNAL和SLOT这两个宏,将两个函数名转换成了字符串。

    测试Moc全部代码

      1 /****************************************************************************
      2 ** Meta object code from reading C++ file 'ctestmoc.h'
      3 **
      4 ** Created by: The Qt Meta Object Compiler version 67 (Qt 5.12.0)
      5 **
      6 ** WARNING! All changes made in this file will be lost!
      7 *****************************************************************************/
      8 
      9 #include "../CTestMoc/ctestmoc.h"
     10 #include <QtCore/qbytearray.h>
     11 #include <QtCore/qmetatype.h>
     12 #if !defined(Q_MOC_OUTPUT_REVISION)
     13 #error "The header file 'ctestmoc.h' doesn't include <QObject>."
     14 #elif Q_MOC_OUTPUT_REVISION != 67
     15 #error "This file was generated using the moc from 5.12.0. It"
     16 #error "cannot be used with the include files from this version of Qt."
     17 #error "(The moc has changed too much.)"
     18 #endif
     19 
     20 QT_BEGIN_MOC_NAMESPACE
     21 QT_WARNING_PUSH
     22 QT_WARNING_DISABLE_DEPRECATED
     23 struct qt_meta_stringdata_CTestMoc_t {
     24     QByteArrayData data[7];
     25     char stringdata0[44];
     26 };
     27 #define QT_MOC_LITERAL(idx, ofs, len) 
     28     Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, 
     29     qptrdiff(offsetof(qt_meta_stringdata_CTestMoc_t, stringdata0) + ofs 
     30         - idx * sizeof(QByteArrayData)) 
     31     )
     32 static const qt_meta_stringdata_CTestMoc_t qt_meta_stringdata_CTestMoc = {
     33     {
     34 QT_MOC_LITERAL(0, 0, 8), // "CTestMoc"
     35 QT_MOC_LITERAL(1, 9, 5), // "Test1"
     36 QT_MOC_LITERAL(2, 15, 0), // ""
     37 QT_MOC_LITERAL(3, 16, 5), // "Test2"
     38 QT_MOC_LITERAL(4, 22, 5), // "iTemp"
     39 QT_MOC_LITERAL(5, 28, 7), // "OnTest1"
     40 QT_MOC_LITERAL(6, 36, 7) // "OnTest2"
     41 
     42     },
     43     "CTestMocTest1Test2iTempOnTest1"
     44     "OnTest2"
     45 };
     46 #undef QT_MOC_LITERAL
     47 
     48 static const uint qt_meta_data_CTestMoc[] = {
     49 
     50  // content:
     51        8,       // revision
     52        0,       // classname
     53        0,    0, // classinfo
     54        4,   14, // methods
     55        0,    0, // properties
     56        0,    0, // enums/sets
     57        0,    0, // constructors
     58        0,       // flags
     59        2,       // signalCount
     60 
     61  // signals: name, argc, parameters, tag, flags
     62        1,    0,   34,    2, 0x06 /* Public */,
     63        3,    1,   35,    2, 0x06 /* Public */,
     64 
     65  // slots: name, argc, parameters, tag, flags
     66        5,    0,   38,    2, 0x08 /* Private */,
     67        6,    1,   39,    2, 0x08 /* Private */,
     68 
     69  // signals: parameters
     70     QMetaType::Void,
     71     QMetaType::Void, QMetaType::Int,    4,
     72 
     73  // slots: parameters
     74     QMetaType::Void,
     75     QMetaType::Void, QMetaType::Int,    4,
     76 
     77        0        // eod
     78 };
     79 
     80 void CTestMoc::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
     81 {
     82     if (_c == QMetaObject::InvokeMetaMethod) {
     83         CTestMoc *_t = static_cast<CTestMoc *>(_o);
     84         Q_UNUSED(_t)
     85         switch (_id) {
     86         case 0: _t->Test1(); break;
     87         case 1: _t->Test2((*reinterpret_cast< int(*)>(_a[1]))); break;
     88         case 2: _t->OnTest1(); break;
     89         case 3: _t->OnTest2((*reinterpret_cast< int(*)>(_a[1]))); break;
     90         default: ;
     91         }
     92     } else if (_c == QMetaObject::IndexOfMethod) {
     93         int *result = reinterpret_cast<int *>(_a[0]);
     94         {
     95             using _t = void (CTestMoc::*)();
     96             if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&CTestMoc::Test1)) {
     97                 *result = 0;
     98                 return;
     99             }
    100         }
    101         {
    102             using _t = void (CTestMoc::*)(int );
    103             if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&CTestMoc::Test2)) {
    104                 *result = 1;
    105                 return;
    106             }
    107         }
    108     }
    109 }
    110 
    111 QT_INIT_METAOBJECT const QMetaObject CTestMoc::staticMetaObject = { {
    112     &QObject::staticMetaObject,
    113     qt_meta_stringdata_CTestMoc.data,
    114     qt_meta_data_CTestMoc,
    115     qt_static_metacall,
    116     nullptr,
    117     nullptr
    118 } };
    119 
    120 
    121 const QMetaObject *CTestMoc::metaObject() const
    122 {
    123     return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject;
    124 }
    125 
    126 void *CTestMoc::qt_metacast(const char *_clname)
    127 {
    128     if (!_clname) return nullptr;
    129     if (!strcmp(_clname, qt_meta_stringdata_CTestMoc.stringdata0))
    130         return static_cast<void*>(this);
    131     return QObject::qt_metacast(_clname);
    132 }
    133 
    134 int CTestMoc::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
    135 {
    136     _id = QObject::qt_metacall(_c, _id, _a);
    137     if (_id < 0)
    138         return _id;
    139     if (_c == QMetaObject::InvokeMetaMethod) {
    140         if (_id < 4)
    141             qt_static_metacall(this, _c, _id, _a);
    142         _id -= 4;
    143     } else if (_c == QMetaObject::RegisterMethodArgumentMetaType) {
    144         if (_id < 4)
    145             *reinterpret_cast<int*>(_a[0]) = -1;
    146         _id -= 4;
    147     }
    148     return _id;
    149 }
    150 
    151 // SIGNAL 0
    152 void CTestMoc::Test1()
    153 {
    154     QMetaObject::activate(this, &staticMetaObject, 0, nullptr);
    155 }
    156 
    157 // SIGNAL 1
    158 void CTestMoc::Test2(int _t1)
    159 {
    160     void *_a[] = { nullptr, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
    161     QMetaObject::activate(this, &staticMetaObject, 1, _a);
    162 }
    163 QT_WARNING_POP
    164 QT_END_MOC_NAMESPACE
    Hunter药药
  • 相关阅读:
    js 动态创建HTML元素
    ASP.NET的几种主要文件
    【荐】Asp.net对文件夹和文件的操作类
    201920201学期 20192415 《网络空间安全专业导论》第二周学习总结 第五章
    201920201学期 20192415《网络空间安全专业导论》第一周学习总结 第二章
    201920201学期 20192415 《网络空间安全专业导论》第二周学习总结 第四章
    201920201学期 20192415 《网络空间安全专业导论》第一周学习总结 第三章
    C# 自动生成类
    div左右布局
    EFCodeFirst安装失败(包括EntityFrameWork安装)解决方案
  • 原文地址:https://www.cnblogs.com/fengyaoyao/p/10549320.html
Copyright © 2011-2022 走看看