• 6、Qt Meta Object system 学习


    原文地址:http://blog.csdn.net/ilvu999/article/details/8049908

    使用 meta object system

    • 继承自 QOject
    • 类定义中添加 Q_OBJECT 宏
    • 使用 moc 程序对包含该宏的文件进行处理

    采用 qmake 进行处理时,如果头文件xxx.h内包含 Q_OBJECT 宏,将生成 moc_xxx.cpp 文件。如果xxx.cpp文件内包含宏,将生成 xxx.moc 文件(这时,我们需要在xxx.cpp文件内添加 #include"xxx.moc")

    Q_OBJECT宏

    包括两个方面,

    • 该宏在C++中的展开,有编译预处理器完成
    • moc 程序对该宏的处理

    宏定义

    复制代码
     1 #define Q_OBJECT 
     2 public: 
     3     Q_OBJECT_CHECK 
     4     static const QMetaObject staticMetaObject; 
     5     Q_OBJECT_GETSTATICMETAOBJECT 
     6     virtual const QMetaObject *metaObject() const; 
     7     virtual void *qt_metacast(const char *); 
     8     QT_TR_FUNCTIONS 
     9     virtual int qt_metacall(QMetaObject::Call, int, void **); 
    10 private: 
    11     Q_DECL_HIDDEN static const QMetaObjectExtraData staticMetaObjectExtraData; 
    12     Q_DECL_HIDDEN static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **);
    复制代码

    而宏 QT_TR_FUNCTIONS 将展开为我们使程序国际化经常使用的 tr 与 trUtf8函数

    复制代码
    1 #  define QT_TR_FUNCTIONS 
    2     static inline QString tr(const char *s, const char *c = 0) 
    3         { return staticMetaObject.tr(s, c); } 
    4     static inline QString trUtf8(const char *s, const char *c = 0) 
    5         { return staticMetaObject.trUtf8(s, c); } 
    6     static inline QString tr(const char *s, const char *c, int n) 
    7         { return staticMetaObject.tr(s, c, n); } 
    8     static inline QString trUtf8(const char *s, const char *c, int n) 
    9         { return staticMetaObject.trUtf8(s, c, n); }
    复制代码

    moc 处理

    Q_OBJECT 为我们添加了这么多成员函数,而这些函数我们有没有自己去实现,那么其定义在哪儿呢? 这就是 moc 为我们做的,自动生成这些成员函数的定义,放于生成的 xxx.moc 或 moc_xxx.cpp 文件内

    注意:两个文件的区别(如果你用cmake或其他工具的话,这点可能很重要)

    • 生成的 moc_xxx.cpp 中会自动包含 xxx.h 头文件,所以它可以被独立编译成目标文件(.o 或 .obj)
    • 生成的 xxx.moc 是不会包含 xxx.cpp 的(要是包含就出问题了,能发现吧?),因此 xxx.moc 中没有类定义,它无法被独立编译,只能被 include 到 xxx.cpp 中。

    QMetaObject

    既然 Q_OBJECT 展开成与 QMetaObject 有关的成员函数,看一下QMetaObject 都提供哪些常用功能

    • className() 返回类的名字
    • superClass() 返回父类的 QMetaObject 对象
    • method()与methodCount() 提供meta函数信息(包括signals, slots 与 invokable函数)
    • enumerator()与 enumeratorCount() 提供enum类型信息
    • propertyCount()与 property() 提供属性信息
    • constructor()与 constructorCount() 提供 meta-constructors 信息

    既然meta object能为我们的类提供这么多信息,那么这些信息存放哪儿了(大家肯定都能猜到秘密在moc生成的文件内,但为清楚起见,我们看一下QMetaObject的定义)。

    这是用 struct 定义的一个类,我们略过其它,只看其数据成员

    复制代码
      1 struct Q_CORE_EXPORT QMetaObject
      2 {
      3     const char *className() const;
      4     const QMetaObject *superClass() const;
      5 
      6     QObject *cast(QObject *obj) const;
      7     const QObject *cast(const QObject *obj) const;
      8 
      9 #ifndef QT_NO_TRANSLATION
     10     // ### Qt 4: Merge overloads
     11     QString tr(const char *s, const char *c) const;
     12     QString trUtf8(const char *s, const char *c) const;
     13     QString tr(const char *s, const char *c, int n) const;
     14     QString trUtf8(const char *s, const char *c, int n) const;
     15 #endif // QT_NO_TRANSLATION
     16 
     17     int methodOffset() const;
     18     int enumeratorOffset() const;
     19     int propertyOffset() const;
     20     int classInfoOffset() const;
     21 
     22     int constructorCount() const;
     23     int methodCount() const;
     24     int enumeratorCount() const;
     25     int propertyCount() const;
     26     int classInfoCount() const;
     27 
     28     int indexOfConstructor(const char *constructor) const;
     29     int indexOfMethod(const char *method) const;
     30     int indexOfSignal(const char *signal) const;
     31     int indexOfSlot(const char *slot) const;
     32     int indexOfEnumerator(const char *name) const;
     33     int indexOfProperty(const char *name) const;
     34     int indexOfClassInfo(const char *name) const;
     35 
     36     QMetaMethod constructor(int index) const;
     37     QMetaMethod method(int index) const;
     38     QMetaEnum enumerator(int index) const;
     39     QMetaProperty property(int index) const;
     40     QMetaClassInfo classInfo(int index) const;
     41     QMetaProperty userProperty() const;
     42 
     43     static bool checkConnectArgs(const char *signal, const char *method);
     44     static QByteArray normalizedSignature(const char *method);
     45     static QByteArray normalizedType(const char *type);
     46 
     47     // internal index-based connect
     48     static bool connect(const QObject *sender, int signal_index,
     49                         const QObject *receiver, int method_index,
     50                         int type = 0, int *types = 0);
     51     // internal index-based disconnect
     52     static bool disconnect(const QObject *sender, int signal_index,
     53                            const QObject *receiver, int method_index);
     54     static bool disconnectOne(const QObject *sender, int signal_index,
     55                               const QObject *receiver, int method_index);
     56     // internal slot-name based connect
     57     static void connectSlotsByName(QObject *o);
     58 
     59     // internal index-based signal activation
     60     static void activate(QObject *sender, int signal_index, void **argv);  //obsolete
     61     static void activate(QObject *sender, int from_signal_index, int to_signal_index, void **argv); //obsolete
     62     static void activate(QObject *sender, const QMetaObject *, int local_signal_index, void **argv);
     63     static void activate(QObject *sender, const QMetaObject *, int from_local_signal_index, int to_local_signal_index, void **argv); //obsolete
     64 
     65     // internal guarded pointers
     66     static void addGuard(QObject **ptr);
     67     static void removeGuard(QObject **ptr);
     68     static void changeGuard(QObject **ptr, QObject *o);
     69 
     70     static bool invokeMethod(QObject *obj, const char *member,
     71                              Qt::ConnectionType,
     72                              QGenericReturnArgument ret,
     73                              QGenericArgument val0 = QGenericArgument(0),
     74                              QGenericArgument val1 = QGenericArgument(),
     75                              QGenericArgument val2 = QGenericArgument(),
     76                              QGenericArgument val3 = QGenericArgument(),
     77                              QGenericArgument val4 = QGenericArgument(),
     78                              QGenericArgument val5 = QGenericArgument(),
     79                              QGenericArgument val6 = QGenericArgument(),
     80                              QGenericArgument val7 = QGenericArgument(),
     81                              QGenericArgument val8 = QGenericArgument(),
     82                              QGenericArgument val9 = QGenericArgument());
     83 
     84     static inline bool invokeMethod(QObject *obj, const char *member,
     85                              QGenericReturnArgument ret,
     86                              QGenericArgument val0 = QGenericArgument(0),
     87                              QGenericArgument val1 = QGenericArgument(),
     88                              QGenericArgument val2 = QGenericArgument(),
     89                              QGenericArgument val3 = QGenericArgument(),
     90                              QGenericArgument val4 = QGenericArgument(),
     91                              QGenericArgument val5 = QGenericArgument(),
     92                              QGenericArgument val6 = QGenericArgument(),
     93                              QGenericArgument val7 = QGenericArgument(),
     94                              QGenericArgument val8 = QGenericArgument(),
     95                              QGenericArgument val9 = QGenericArgument())
     96     {
     97         return invokeMethod(obj, member, Qt::AutoConnection, ret, val0, val1, val2, val3,
     98                 val4, val5, val6, val7, val8, val9);
     99     }
    100 
    101     static inline bool invokeMethod(QObject *obj, const char *member,
    102                              Qt::ConnectionType type,
    103                              QGenericArgument val0 = QGenericArgument(0),
    104                              QGenericArgument val1 = QGenericArgument(),
    105                              QGenericArgument val2 = QGenericArgument(),
    106                              QGenericArgument val3 = QGenericArgument(),
    107                              QGenericArgument val4 = QGenericArgument(),
    108                              QGenericArgument val5 = QGenericArgument(),
    109                              QGenericArgument val6 = QGenericArgument(),
    110                              QGenericArgument val7 = QGenericArgument(),
    111                              QGenericArgument val8 = QGenericArgument(),
    112                              QGenericArgument val9 = QGenericArgument())
    113     {
    114         return invokeMethod(obj, member, type, QGenericReturnArgument(), val0, val1, val2,
    115                                  val3, val4, val5, val6, val7, val8, val9);
    116     }
    117 
    118     static inline bool invokeMethod(QObject *obj, const char *member,
    119                              QGenericArgument val0 = QGenericArgument(0),
    120                              QGenericArgument val1 = QGenericArgument(),
    121                              QGenericArgument val2 = QGenericArgument(),
    122                              QGenericArgument val3 = QGenericArgument(),
    123                              QGenericArgument val4 = QGenericArgument(),
    124                              QGenericArgument val5 = QGenericArgument(),
    125                              QGenericArgument val6 = QGenericArgument(),
    126                              QGenericArgument val7 = QGenericArgument(),
    127                              QGenericArgument val8 = QGenericArgument(),
    128                              QGenericArgument val9 = QGenericArgument())
    129     {
    130         return invokeMethod(obj, member, Qt::AutoConnection, QGenericReturnArgument(), val0,
    131                 val1, val2, val3, val4, val5, val6, val7, val8, val9);
    132     }
    133 
    134     QObject *newInstance(QGenericArgument val0 = QGenericArgument(0),
    135                          QGenericArgument val1 = QGenericArgument(),
    136                          QGenericArgument val2 = QGenericArgument(),
    137                          QGenericArgument val3 = QGenericArgument(),
    138                          QGenericArgument val4 = QGenericArgument(),
    139                          QGenericArgument val5 = QGenericArgument(),
    140                          QGenericArgument val6 = QGenericArgument(),
    141                          QGenericArgument val7 = QGenericArgument(),
    142                          QGenericArgument val8 = QGenericArgument(),
    143                          QGenericArgument val9 = QGenericArgument()) const;
    144 
    145     enum Call {
    146         InvokeMetaMethod,
    147         ReadProperty,
    148         WriteProperty,
    149         ResetProperty,
    150         QueryPropertyDesignable,
    151         QueryPropertyScriptable,
    152         QueryPropertyStored,
    153         QueryPropertyEditable,
    154         QueryPropertyUser,
    155         CreateInstance
    156     };
    157 
    158     int static_metacall(Call, int, void **) const;
    159     static int metacall(QObject *, Call, int, void **);
    160 
    161 #ifdef QT3_SUPPORT
    162     QT3_SUPPORT const char *superClassName() const;
    163 #endif
    164 
    165     struct { // private data
    166         const QMetaObject *superdata;
    167         const char *stringdata;
    168         const uint *data;
    169         const void *extradata;
    170     } d;
    171 };
    复制代码

    其中呢,

    • uperdata,指向父类的MetaObject,容易理解

    • extradata 似乎目前未启用,不用理解
    • 剩下两个是什么呢? 如你所想,就在 moc 生成的文件内

    moc生成的文件

    随便找个 moc 文件出来看看

    1 static const uint qt_meta_data_HPixmapScene[] = {...};
    2 static const char qt_meta_stringdata_HPixmapScene[] = {...};
    3 const QMetaObject HPixmapScene::staticMetaObject = {
    4 { &QGraphicsScene::staticMetaObject, qt_meta_stringdata_HPixmapScene,
    5 qt_meta_data_HPixmapScene, 0 }
    6 }; 

    这是一个QGraphicsScene的子类。对比前面QMetaObject的数据成员看看,是不是很简单:

    • 先分别定义1个 uint 和 char 的数组,
    • 用这两个数组首地址和父类的MetaObject的指针初始化 staticMetaObject

    • 这个 staticMetaObject 是我们自己的类的静态成员变量

    uint数组
    接下来我们可以看看 uint 数组,这个数组中存放的是一些索引值,来指导我们从char字符串中提取信息

    复制代码
     1 static const uint qt_meta_data_HPixmapScene[] = {
     2 
     3 // content:
     4 4, // revision
     5 0, // classname
     6 0, 0, // classinfo
     7 2, 14, // methods
     8 0, 0, // properties
     9 0, 0, // enums/sets
    10 0, 0, // constructors
    11 0, // flags
    12 0, // signalCount
    13 
    14 // slots: signature, parameters, type, tag, flags
    15 18, 14, 13, 13, 0x0a,
    16 41, 37, 13, 13, 0x0a,
    17 
    18 0 // eod
    19 }; 
    复制代码

    对比QMetaObject用到的数据结构 QMetaObjectPrivate,看看,是不是豁然开朗了:uint 数组中的第一段 就对应这个结构体

    复制代码
     1 struct QMetaObjectPrivate
     2 {
     3     int revision;
     4     int className;
     5     int classInfoCount, classInfoData;
     6     int methodCount, methodData;
     7     int propertyCount, propertyData;
     8     int enumeratorCount, enumeratorData;
     9     int constructorCount, constructorData; //since revision 2
    10     int flags; //since revision 3
    11     int signalCount; //since revision 4
    12     // revision 5 introduces changes in normalized signatures, no new members
    13     // revision 6 added qt_static_metacall as a member of each Q_OBJECT and inside QMetaObject itself
    14 
    15     static inline const QMetaObjectPrivate *get(const QMetaObject *metaobject)
    16     { return reinterpret_cast<const QMetaObjectPrivate*>(metaobject->d.data); }
    17 
    18     static int indexOfSignalRelative(const QMetaObject **baseObject,
    19                                      const char* name,
    20                                      bool normalizeStringData);
    21     static int indexOfSlotRelative(const QMetaObject **m,
    22                            const char *slot,
    23                            bool normalizeStringData);
    24     static int originalClone(const QMetaObject *obj, int local_method_index);
    25 
    26 #ifndef QT_NO_QOBJECT
    27     //defined in qobject.cpp
    28     enum DisconnectType { DisconnectAll, DisconnectOne };
    29     static void memberIndexes(const QObject *obj, const QMetaMethod &member,
    30                               int *signalIndex, int *methodIndex);
    31     static bool connect(const QObject *sender, int signal_index,
    32                         const QObject *receiver, int method_index_relative,
    33                         const QMetaObject *rmeta = 0,
    34                         int type = 0, int *types = 0);
    35     static bool disconnect(const QObject *sender, int signal_index,
    36                            const QObject *receiver, int method_index,
    37                            DisconnectType = DisconnectAll);
    38     static inline bool disconnectHelper(QObjectPrivate::Connection *c,
    39                                         const QObject *receiver, int method_index,
    40                                         QMutex *senderMutex, DisconnectType);
    41 #endif
    42 };
    复制代码

    char 数组

    配合上面的索引数组,继续看一下:

    1 static const char qt_meta_stringdata_HPixmapScene[] = {
    2 "HPixmapScenepixsetPixmap(QPixmap)"
    3 "imgsetImage(QImage)"
    4 }; 

    索引数组中 className 为0,我们看字符数组中从0开始是什么?恩,就是我们类的名字。

    我们类的类中还定义的两个slot函数

    public slots:
    void setPixmap(const QPixmap& pix);
    void setImage(const QImage& img);

    我们看看是怎么对应的,

    • 首先看索引数组 methodCount=2, methodData=14,告诉我们有两个method,数据从索引数组的14开始
    • 然后,我们从索引数组的14开始看,{18,   14,   13,   13, 0x0a

    • 这儿,18和14对应到我们的char数组了,看一下,分别是setPixmap(QPixmap) 和 pix。怎么样,和我们定义的函数对应上了吧。
    有什么不对
    • 其实上面有一处不太对,从开始就不太对。什么不对呢?

    Qt manual 对此只提了一句,在QObject的manual中,除此之外,似乎再没出现过:要用 meta object system,我们不一定要继承QObject,普通的C++的类也可以部分使用,与此对应,我们不用Q_OBJECT宏,而用Q_GADGET宏,当然,这个只提供部分meta object system 的功能,比如Qt 元对象系统之 "enum自省" 

    另外:ActiveQt模块中,QAxObject和QAxWidget 定义了信号槽,却没有使用Q_OBJECT,具体可以看 ActiveQt模块学习(三)

    另外 moc 生成的文件内有一个 Q_NO_DATA_RELOCATION 宏,无论用 Q_OBJECT 还是 Q_GADGET,我不清楚该宏起什么作用的。

    整理思路太累了,写完这个,还不清楚什么时候再写(二)

    参考

    Qt属性系统(Qt's Property System)

    使用

    • 使用 Q_PROPERTY 宏来声明属性(即 将属性注册到meta-object系统中)。
    • 通过QObject的metaObject即可获得用Q_PROPERTY注册的所有属性
      • QMetaObject::property 返回QMetaProperty对象指针
      • QMetaObject::propertyCount 属性个数(包含父类定义的属性)
      • QMetaObject::propertyOffset 属性的开始索引
      • QMetaObject::indexOfProperty 根据属性名得到属性索引
      • QMetaObject::userProperty 带USER的属性,无参数(只能有一个?)
    • 通过QMetaProperty可以获得属性的的各种信息
    • 使用QObjecty可以读取或设置属性
      • property
      • setProperty

    Q_PROPERTY

    Qt属性是靠 Q_PROPERTY 宏进行声明的

    复制代码
     1 Q_PROPERTY(type name  READ getFunction
     2 [WRITE setFunction]
     3 [RESET resetFunction]
     4 [NOTIFY notifySignal]
     5 [DESIGNABLE bool]
     6 [SCRIPTABLE bool]
     7 [STORED bool]
     8 [USER bool]
     9 [CONSTANT]
    10 [FINAL])
    复制代码

    例子:

    1 Q_PROPERTY(bool focus READ hasFocus)
    2 Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled)
    3 Q_PROPERTY(QCursor cursor READ cursor WRITE setCursor RESET unsetCursor)
    4 Q_PROPERTY(bool checked READ isChecked WRITE setChecked DESIGNABLE isCheckable NOTIFY toggled USER true)
    • READ 该项必须!后接无参的const函数
    • WRITE 后接无返回值的但参数函数
    • RESET 后接无参无返回值的函数
    • NOTIFT 后接单参的信号,属性变化时发射
    • DESIGNABLE 布尔量(默认True),是否在designer中出现
    • SCRIPTABLE 布尔量(默认True),是否用于脚本
    • STORED 布尔量(默认True),是否“不依赖”其他值。比如mimimumWidth 依赖 mimimumSize,故为False
    • USER 布尔量(默认False),是否是直接面向用户的。比如QPushButton的 checked 属性,则为 True
    • CONSTANT 出现则代表常量。不可有 WRITE 及 NOTIFY
    • FINAL 出现则代表不能被派生类重载?

    从源代码文件 qobjectdefs.h 中可以看到该宏定义为空

    1 #define Q_PROPERTY(TEXT)

    也就是是说该宏仅对 moc 有意义,moc处理后生成的代码在 xxx.moc 或 moc_xxx.cpp 中。

    moc

    看一个例子:

    1 Q_PROPERTY(bool colorDialog READ colorDialogEnabled WRITE setColorDialogEnabled) 

    查看对应的moc文件:

    • 索引数据在 static const uint qt_meta_data_QtColorPicker[]

    • 字符串数据在 static const char qt_meta_stringdata_QtColorPicker[]

    • 并在 qt_metacall 函数中生成有相关的代码

    自测如下-->

    源码:

    结果:

    动态属性
    通过QObject的setProperty 设置属性时,如果该属性不存在,则为设置为动态属性。动态属性存储在QObject中,而不是其QMetaObject对象中。

    类型

    • 对于 enum 类型,需要用 Q_ENUMS 或Q_FLAGS 将其注册到 meta-object系统中
    • 对自定义类型,如果要用作属性,需要用 Q_DECLARE_METATYPE()将其注册到meta-object系统中。

    对 PySide 及 PyQt4

    复制代码
     1 QtCore.pyqtProperty(type, fget=None, fset=None, freset=None, fdel=None, doc=None, designable=True, scriptable=True, stored=True, user=False, constant=False, final=False)
     2 
     3 type
     4 
     5 type of the property
     6 
     7 fget
     8 
     9 getter function
    10 
    11 fset
    12 
    13 None
    14 
    15 setter function
    16 
    17 freset
    18 
    19 None
    20 
    21 function used to reset the value of the property to its default value (only in C++)
    22 
    23 fdel
    24 
    25 None
    26 
    27 function for del'ing the property (only in Python)
    28 
    29 doc
    30 
    31 None
    32 
    33 docstring of the property
    34 
    35 designable
    36 
    37 True
    38 
    39 value of Qt DESIGNABLE flag
    40 
    41 scriptable
    42 
    43 True
    44 
    45 value of Qt SCRIPTABLE flag
    46 
    47 stored
    48 
    49 True
    50 
    51 value of Qt STORED flag
    52 
    53 user
    54 
    55 False
    56 
    57 value of Qt USER flag
    58 
    59 constant
    60 
    61 False
    62 
    63 value of Qt CONSTANT flag
    64 
    65 final
    66 
    67 False
    68 
    69 value of Qt FINAL flag
    复制代码

    使用举例:

    复制代码
     1 class MyObject(QObject):
     2   def __init__(self,startval=42):
     3     self.ppval = startval
     4   def readPP(self):
     5     return self.ppval
     6   def setPP(self,val):
     7     self.ppval = val
     8   pp = pyqtProperty(int, readPP, setPP)
     9 
    10 obj = MyObject()
    11 obj.pp = 47
    12 print obj.pp
    复制代码
    • PySide用Property来取代pyqtProPerty

    • PySide一开始出现一个失误,用的是QProperty而不是Property

    • PyQt4 为 fget 提供了默认的None,由于Qt要求READ函数必须有,故PySide不为其提供默认值

     看 ActiveQt 模块的源码,看到信号槽部分,实在看不懂了,只好回来继续学习 元对象系统 了。

    在前面的学习中中简单整理了Q_OBJECT宏 与 moc 生成的文件,并且从中学习了属性系统的宏 Q_PROPERTY。现在该看看源码中信号槽相关的内容了(Qt4.7的源码,其他版本与此可能不同)

    1 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  
    11     。。。。。。
    12 }
    复制代码

    这个复合语句的作用看不太懂,QInternal类的定义在 src/corelib/global/qnamespace.h 中,类成员的实现在 src/corelib/global/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 
     5     QInternal_CallBackTable *cbt = global_callback_table();
     6     if (cbt && cb < cbt->callbacks.size()) {
     7         QList<qInternalCallback> callbacks = cbt->callbacks[cb];
     8         bool ret = false;
     9         for (int i=0; i<callbacks.size(); ++i)
    10             ret |= (callbacks.at(i))(parameters);
    11         return ret;
    12     }
    13     return false;
    14 }
    复制代码

    然后检测4个参数是否都非空

    复制代码
    1     if (sender == 0 || receiver == 0 || signal == 0 || method == 0) {
    2         qWarning("QObject::connect: Cannot connect %s::%s to %s::%s",
    3                  sender ? sender->metaObject()->className() : "(null)",
    4                  (signal && *signal) ? signal+1 : "(null)",
    5                  receiver ? receiver->metaObject()->className() : "(null)",
    6                  (method && *method) ? method+1 : "(null)");
    7         return false;
    8     }
    复制代码

    然后

    1     QByteArray tmp_signal_name;
    2 
    3     if (!check_signal_macro(sender, signal, "connect", "bind"))
    4         return false;
    5     const QMetaObject *smeta = sender->metaObject();
    • 检测与信号连接的“信号或槽”是否符合格式,然后从元对象系统中获得相应的索引

    检测信号与槽的参数是否匹配,对与Queued连接,检测参数是否注册到元对象系统

    复制代码
     1 ++signal; //skip code
     2     int signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, false);
     3     if (signal_index < 0) {
     4         // check for normalized signatures
     5         tmp_signal_name = QMetaObject::normalizedSignature(signal - 1);
     6         signal = tmp_signal_name.constData() + 1;
     7 
     8         smeta = sender->metaObject();
     9         signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, false);
    10     }
    11     if (signal_index < 0) {
    12         // re-use tmp_signal_name and signal from above
    13 
    14         smeta = sender->metaObject();
    15         signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, true);
    16     }
    17     if (signal_index < 0) {
    18         err_method_notfound(sender, signal_arg, "connect");
    19         err_info_about_objects("connect", sender, receiver);
    20         return false;
    21     }
    22     signal_index = QMetaObjectPrivate::originalClone(smeta, signal_index);
    23     int signalOffset, methodOffset;
    24     computeOffsets(smeta, &signalOffset, &methodOffset);
    25     int signal_absolute_index = signal_index + methodOffset;
    26     signal_index += signalOffset;
    27 
    28     QByteArray tmp_method_name;
    29     int membcode = extract_code(method);
    30 
    31     if (!check_method_code(membcode, receiver, method, "connect"))
    32         return false;
    33     const char *method_arg = method;
    34     ++method; // skip code
    35 
    36     const QMetaObject *rmeta = receiver->metaObject();
    37     int method_index_relative = -1;
    38     switch (membcode) {
    39     case QSLOT_CODE:
    40         method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(&rmeta, method, false);
    41         break;
    42     case QSIGNAL_CODE:
    43         method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(&rmeta, method, false);
    44         break;
    45     }
    46 
    47     if (method_index_relative < 0) {
    48         // check for normalized methods
    49         tmp_method_name = QMetaObject::normalizedSignature(method);
    50         method = tmp_method_name.constData();
    51 
    52         // rmeta may have been modified above
    53         rmeta = receiver->metaObject();
    54         switch (membcode) {
    55         case QSLOT_CODE:
    56             method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(&rmeta, method, false);
    57             if (method_index_relative < 0)
    58                 method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(&rmeta, method, true);
    59             break;
    60         case QSIGNAL_CODE:
    61             method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(&rmeta, method, false);
    62             if (method_index_relative < 0)
    63                 method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(&rmeta, method, true);
    64             break;
    65         }
    66     }
    67 
    68     if (method_index_relative < 0) {
    69         err_method_notfound(receiver, method_arg, "connect");
    70         err_info_about_objects("connect", sender, receiver);
    71         return false;
    72     }
    73 
    74     if (!QMetaObject::checkConnectArgs(signal, method)) {
    75         qWarning("QObject::connect: Incompatible sender/receiver arguments"
    76                  "
            %s::%s --> %s::%s",
    77                  sender->metaObject()->className(), signal,
    78                  receiver->metaObject()->className(), method);
    79         return false;
    80     }
    81 
    82     int *types = 0;
    83     if ((type == Qt::QueuedConnection)
    84             && !(types = queuedConnectionTypes(smeta->method(signal_absolute_index).parameterTypes())))
    85         return false;
    86 
    87 #ifndef QT_NO_DEBUG
    88     if (warnCompat) {
    89         QMetaMethod smethod = smeta->method(signal_absolute_index);
    90         QMetaMethod rmethod = rmeta->method(method_index_relative + rmeta->methodOffset());
    91         check_and_warn_compat(smeta, smethod, rmeta, rmethod);
    92     }
    93 #endif
    复制代码

    若这一切都通过了,调用 QMetaObjectPrivate 的connect成员。调用成功,则调用 sender 对象的 connectNofify 函数(ActiveQt模块使用了该函数)

    1     if (!QMetaObjectPrivate::connect(sender, signal_index, receiver, method_index_relative, rmeta ,type, types))
    2         return false;
    3     const_cast<QObject*>(sender)->connectNotify(signal - 1);
    4     return true;
    5 }

    QMetaObjectPrivate::connect首先,检测是否是 uniqueconnection ,如果是,则先搜索已有的连接,找到则函数返回

    复制代码
     1 bool QMetaObjectPrivate::connect(const QObject *sender, int signal_index,
     2                                  const QObject *receiver, int method_index,
     3                                  const QMetaObject *rmeta, int type, int *types)
     4 {
     5     QObject *s = const_cast<QObject *>(sender);
     6     QObject *r = const_cast<QObject *>(receiver);
     7 
     8     int method_offset = rmeta ? rmeta->methodOffset() : 0;
     9     QObjectPrivate::StaticMetaCallFunction callFunction =
    10         (rmeta && QMetaObjectPrivate::get(rmeta)->revision >= 6 && rmeta->d.extradata)
    11         ? reinterpret_cast<const QMetaObjectExtraData *>(rmeta->d.extradata)->static_metacall : 0;
    12 
    13     QOrderedMutexLocker locker(signalSlotLock(sender),
    14                                signalSlotLock(receiver));
    15 
    16     if (type & Qt::UniqueConnection) {
    17         QObjectConnectionListVector *connectionLists = QObjectPrivate::get(s)->connectionLists;
    18         if (connectionLists && connectionLists->count() > signal_index) {
    19             const QObjectPrivate::Connection *c2 =
    20                 (*connectionLists)[signal_index].first;
    21 
    22             int method_index_absolute = method_index + method_offset;
    23 
    24             while (c2) {
    25                 if (c2->receiver == receiver && c2->method() == method_index_absolute)
    26                     return false;
    27                 c2 = c2->nextConnectionList;
    28             }
    29         }
    30         type &= Qt::UniqueConnection - 1;
    31     }
    复制代码

    创建连接

    复制代码
     1 QObjectPrivate::Connection *c = new QObjectPrivate::Connection;
     2     c->sender = s;
     3     c->receiver = r;
     4     c->method_relative = method_index;
     5     c->method_offset = method_offset;
     6     c->connectionType = type;
     7     c->argumentTypes = types;
     8     c->nextConnectionList = 0;
     9     c->callFunction = callFunction;
    10 
    11     QT_TRY {
    12         QObjectPrivate::get(s)->addConnection(signal_index, c);
    13     } QT_CATCH(...) {
    14         delete c;
    15         QT_RETHROW;
    16     }
    17 
    18     c->prev = &(QObjectPrivate::get(r)->senders);
    19     c->next = *c->prev;
    20     *c->prev = c;
    21     if (c->next)
    22         c->next->prev = &c->next;
    23 
    24     QObjectPrivate *const sender_d = QObjectPrivate::get(s);
    25     if (signal_index < 0) {
    26         sender_d->connectedSignals[0] = sender_d->connectedSignals[1] = ~0;
    27     } else if (signal_index < (int)sizeof(sender_d->connectedSignals) * 8) {
    28         sender_d->connectedSignals[signal_index >> 5] |= (1 << (signal_index & 0x1f));
    29     }
    30 
    31     return true;
    32 }
    复制代码

    其他还有部分代码,看不太懂,暂略(应该不影响理解)

    QMetaObject::activate

    信号的定义体在 moc 生成的文件内,在定义体中会调用 QMetaObject::activate 函数来实现信号的功能。

    首先判断该信号有无被链接,是否是block设置(具体含义?)

    复制代码
     1 void QMetaObject::activate(QObject *sender, const QMetaObject *m, int local_signal_index,
     2                            void **argv)
     3 {
     4     int signalOffset;
     5     int methodOffset;
     6     computeOffsets(m, &signalOffset, &methodOffset);
     7 
     8     int signal_index = signalOffset + local_signal_index;
     9 
    10     if (!sender->d_func()->isSignalConnected(signal_index))
    11         return; // nothing connected to these signals, and no spy
    12 
    13     if (sender->d_func()->blockSig)
    14         return;
    复制代码

    和signal spy有关的一些代码

    复制代码
     1     int signal_absolute_index = methodOffset + local_signal_index;
     2 
     3     void *empty_argv[] = { 0 };
     4     if (qt_signal_spy_callback_set.signal_begin_callback != 0) {
     5         qt_signal_spy_callback_set.signal_begin_callback(sender, signal_absolute_index,
     6                                                          argv ? argv : empty_argv);
     7     }
     8 
     9     Qt::HANDLE currentThreadId = QThread::currentThreadId();
    10 
    11     QMutexLocker locker(signalSlotLock(sender));
    12     QObjectConnectionListVector *connectionLists = sender->d_func()->connectionLists;
    13     if (!connectionLists) {
    14         locker.unlock();
    15         if (qt_signal_spy_callback_set.signal_end_callback != 0)
    16             qt_signal_spy_callback_set.signal_end_callback(sender, signal_absolute_index);
    17         return;
    18     }
    19     ++connectionLists->inUse;
    复制代码

    用双重循环依次处理每一个信号的每一个连接

    复制代码
      1     const QObjectPrivate::ConnectionList *list;
      2     if (signal_index < connectionLists->count())
      3         list = &connectionLists->at(signal_index);
      4     else
      5         list = &connectionLists->allsignals;
      6 
      7     do {
      8         QObjectPrivate::Connection *c = list->first;
      9         if (!c) continue;
     10         // We need to check against last here to ensure that signals added
     11         // during the signal emission are not emitted in this emission.
     12         QObjectPrivate::Connection *last = list->last;
     13 
     14         do {
     15             if (!c->receiver)
     16                 continue;
     17 
     18             QObject * const receiver = c->receiver;
     19             const bool receiverInSameThread = currentThreadId == receiver->d_func()->threadData->threadId;
     20 
     21             // determine if this connection should be sent immediately or
     22             // put into the event queue
     23             if ((c->connectionType == Qt::AutoConnection && !receiverInSameThread)
     24                 || (c->connectionType == Qt::QueuedConnection)) { //对queued的连接单独处理
     25                 queued_activate(sender, signal_absolute_index, c, argv ? argv : empty_argv);
     26                 continue;
     27 #ifndef QT_NO_THREAD
     28             } else if (c->connectionType == Qt::BlockingQueuedConnection) {
     29                 locker.unlock();
     30                 if (receiverInSameThread) {
     31                     qWarning("Qt: Dead lock detected while activating a BlockingQueuedConnection: "
     32                     "Sender is %s(%p), receiver is %s(%p)",
     33                     sender->metaObject()->className(), sender,
     34                     receiver->metaObject()->className(), receiver);
     35                 }
     36                 QSemaphore semaphore;
     37                 QCoreApplication::postEvent(receiver, new QMetaCallEvent(c->method_offset, c->method_relative,
     38                                                                          c->callFunction,
     39                                                                          sender, signal_absolute_index,
     40                                                                          0, 0,
     41                                                                          argv ? argv : empty_argv,
     42                                                                          &semaphore));
     43                 semaphore.acquire();
     44                 locker.relock();
     45                 continue;
     46 #endif
     47             }
     48             //对其他连接,继续(发送与接收者在同一线程,则设置当前发送者,QObject::sender() 需要该功能)
     49             QObjectPrivate::Sender currentSender;
     50             QObjectPrivate::Sender *previousSender = 0;
     51             if (receiverInSameThread) {
     52                 currentSender.sender = sender;
     53                 currentSender.signal = signal_absolute_index;
     54                 currentSender.ref = 1;
     55                 previousSender = QObjectPrivate::setCurrentSender(receiver, &currentSender);
     56             }
     57             const QObjectPrivate::StaticMetaCallFunction callFunction = c->callFunction;
     58             const int method_relative = c->method_relative;
     59             if (callFunction && c->method_offset <= receiver->metaObject()->methodOffset()) {
     60                 //we compare the vtable to make sure we are not in the destructor of the object.
     61                 locker.unlock();
     62                 if (qt_signal_spy_callback_set.slot_begin_callback != 0)
     63                     qt_signal_spy_callback_set.slot_begin_callback(receiver, c->method(), argv ? argv : empty_argv);
     64 
     65 #if defined(QT_NO_EXCEPTIONS)
     66                 callFunction(receiver, QMetaObject::InvokeMetaMethod, method_relative, argv ? argv : empty_argv);
     67 #else
     68                 QT_TRY {
     69                     callFunction(receiver, QMetaObject::InvokeMetaMethod, method_relative, argv ? argv : empty_argv);
     70                 } QT_CATCH(...) {
     71                     locker.relock();
     72                     if (receiverInSameThread)
     73                         QObjectPrivate::resetCurrentSender(receiver, &currentSender, previousSender);
     74 
     75                     --connectionLists->inUse;
     76                     Q_ASSERT(connectionLists->inUse >= 0);
     77                     if (connectionLists->orphaned && !connectionLists->inUse)
     78                         delete connectionLists;
     79                     QT_RETHROW;
     80                 }
     81 #endif
     82                 if (qt_signal_spy_callback_set.slot_end_callback != 0)
     83                     qt_signal_spy_callback_set.slot_end_callback(receiver, c->method());
     84                 locker.relock();
     85             } else {
     86                 const int method = method_relative + c->method_offset;
     87                 locker.unlock();
     88 
     89                 if (qt_signal_spy_callback_set.slot_begin_callback != 0) {
     90                     qt_signal_spy_callback_set.slot_begin_callback(receiver,
     91                                                                 method,
     92                                                                 argv ? argv : empty_argv);
     93                 }
     94                 //调用metacall
     95 #if defined(QT_NO_EXCEPTIONS)
     96                 metacall(receiver, QMetaObject::InvokeMetaMethod, method, argv ? argv : empty_argv);
     97 #else
     98                 QT_TRY {
     99                     metacall(receiver, QMetaObject::InvokeMetaMethod, method, argv ? argv : empty_argv);
    100                 } QT_CATCH(...) {
    101                     locker.relock();
    102                     if (receiverInSameThread)
    103                         QObjectPrivate::resetCurrentSender(receiver, &currentSender, previousSender);
    104 
    105                     --connectionLists->inUse;
    106                     Q_ASSERT(connectionLists->inUse >= 0);
    107                     if (connectionLists->orphaned && !connectionLists->inUse)
    108                         delete connectionLists;
    109                     QT_RETHROW;
    110                 }
    111 #endif
    112 
    113                 if (qt_signal_spy_callback_set.slot_end_callback != 0)
    114                     qt_signal_spy_callback_set.slot_end_callback(receiver, method);
    115 
    116                 locker.relock();
    117             }
    118 
    119             if (receiverInSameThread)
    120                 QObjectPrivate::resetCurrentSender(receiver, &currentSender, previousSender);
    121 
    122             if (connectionLists->orphaned)
    123                 break;
    124         } while (c != last && (c = c->nextConnectionList) != 0);
    125 
    126         if (connectionLists->orphaned)
    127             break;
    128     } while (list != &connectionLists->allsignals &&
    129         //start over for all signals;
    130         ((list = &connectionLists->allsignals), true));
    131 
    132     --connectionLists->inUse;
    133     Q_ASSERT(connectionLists->inUse >= 0);
    134     if (connectionLists->orphaned) {
    135         if (!connectionLists->inUse)
    136             delete connectionLists;
    137     } else if (connectionLists->dirty) {
    138         sender->d_func()->cleanConnectionLists();
    139     }
    140 
    141     locker.unlock();
    142 
    143     if (qt_signal_spy_callback_set.signal_end_callback != 0)
    144         qt_signal_spy_callback_set.signal_end_callback(sender, signal_absolute_index);
    145 
    146 }
    复制代码


    接下来看metacall代码

    复制代码
    1 int QMetaObject::metacall(QObject *object, Call cl, int idx, void **argv)
    2 {
    3     if (QMetaObject *mo = object->d_ptr->metaObject)
    4         return static_cast<QAbstractDynamicMetaObject*>(mo)->metaCall(cl, idx, argv);
    5     else
    6         return object->qt_metacall(cl, idx, argv);
    7 }
    复制代码

    前面的 QAbstractDynamicMetaObject 部分不清楚作用,后面的 qt_metacall 比较好理解了(代码在moc生成的文件中)

    queued_activate

    首先是参数检测,然后是构建 QMetaCallEvent 事件,并post该事件

    复制代码
     1 static void queued_activate(QObject *sender, int signal, QObjectPrivate::Connection *c, void **argv)
     2 {
     3     if (!c->argumentTypes && c->argumentTypes != &DIRECT_CONNECTION_ONLY) {
     4         QMetaMethod m = sender->metaObject()->method(signal);
     5         int *tmp = queuedConnectionTypes(m.parameterTypes());
     6         if (!tmp) // cannot queue arguments
     7             tmp = &DIRECT_CONNECTION_ONLY;
     8         if (!c->argumentTypes.testAndSetOrdered(0, tmp)) {
     9             if (tmp != &DIRECT_CONNECTION_ONLY)
    10                 delete [] tmp;
    11         }
    12     }
    13     if (c->argumentTypes == &DIRECT_CONNECTION_ONLY) // cannot activate
    14         return;
    15     int nargs = 1; // include return type
    16     while (c->argumentTypes[nargs-1])
    17         ++nargs;
    18     int *types = (int *) qMalloc(nargs*sizeof(int));
    19     Q_CHECK_PTR(types);
    20     void **args = (void **) qMalloc(nargs*sizeof(void *));
    21     Q_CHECK_PTR(args);
    22     types[0] = 0; // return type
    23     args[0] = 0; // return value
    24     for (int n = 1; n < nargs; ++n)
    25         args[n] = QMetaType::construct((types[n] = c->argumentTypes[n-1]), argv[n]);
    26     QCoreApplication::postEvent(c->receiver, new QMetaCallEvent(c->method_offset,
    27                                                                 c->method_relative,
    28                                                                 c->callFunction,
    29                                                                 sender, signal, nargs,
    30                                                                 types, args));
    31 }
    复制代码

    blocking_activate

    首先,检测是否是同一线程,然后发送事件 QMetaCallEvent

    既然和事件有关系,就看看事件是怎么回事

    复制代码
     1     case QEvent::MetaCall:
     2         {
     3 #ifdef QT_JAMBI_BUILD
     4             d_func()->inEventHandler = false;
     5 #endif
     6             QMetaCallEvent *mce = static_cast<QMetaCallEvent*>(e);
     7             QObjectPrivate::Sender currentSender;
     8             currentSender.sender = const_cast<QObject*>(mce->sender());
     9             currentSender.signal = mce->signalId();
    10             currentSender.ref = 1;
    11             QObjectPrivate::Sender * const previousSender =
    12                 QObjectPrivate::setCurrentSender(this, &currentSender);
    13 #if defined(QT_NO_EXCEPTIONS)
    14             mce->placeMetaCall(this);
    15 #else
    16             QT_TRY {
    17                 mce->placeMetaCall(this);
    18             } QT_CATCH(...) {
    19                 QObjectPrivate::resetCurrentSender(this, &currentSender, previousSender);
    20                 QT_RETHROW;
    21             }
    22 #endif
    23             QObjectPrivate::resetCurrentSender(this, &currentSender, previousSender);
    24             break;
    25         }
    复制代码

    可以看到,这儿调用了 placeMetaCall

    复制代码
    1 void QMetaCallEvent::placeMetaCall(QObject *object)
    2 {
    3     if (callFunction_) {
    4         callFunction_(object, QMetaObject::InvokeMetaMethod, method_relative_, args_);
    5     } else {
    6         QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, method_offset_ + method_relative_, args_);
    7     }
    8 }
    复制代码

    参考

    基本理解

    • Q_DECLARE_METATYPE
      • 如果要使自定义类型或其他非QMetaType内置类型在QVaiant中使用,必须使用该宏。
      • 该类型必须有公有的 构造、析构、复制构造 函数
    • qRegisterMetaType 必须使用该函数的两种情况
      • 如果非QMetaType内置类型要在 Qt 的属性系统中使用
      • 如果非QMetaType内置类型要在 queued 信号与槽 中使用

    二者关系

    二者的代码:

    • Q_DECLARE_METATYPE 展开后是一个特化后的类 QMetaTypeId<TYPE>

    • qRegisterMetaType 将某类型注册中 MetaType 系统中

    二者的联系:

    • QMetaTypeId<TYPE>的类中的成员包含对qRegisterMetaType的调用

    • 我们知道类中的成员函数并不一定会被调用(即,该宏并不确保类型被注册到MetaType)。

    • 通过qRegisterMetaType可以确保类型被注册

    两个qRegisterMetaType 的联系

    • 无参的qRegisterMetaType函数会通过该成员调用带参数的qRegisterMetaType()

    这两个东西真难理清,不妨看看源码吧。

    1 Q_DECLARE_METATYPE

    代码来源:src/corelib/kernel/qmetatype.h

    复制代码
     1 #define Q_DECLARE_METATYPE(TYPE)                                        
     2     QT_BEGIN_NAMESPACE                                                  
     3     template <>                                                         
     4     struct QMetaTypeId< TYPE >                                          
     5     {                                                                   
     6         enum { Defined = 1 };                                           
     7         static int qt_metatype_id()                                     
     8             {                                                           
     9                 static QBasicAtomicInt metatype_id = Q_BASIC_ATOMIC_INITIALIZER(0); 
    10                 if (!metatype_id)                                       
    11                     metatype_id = qRegisterMetaType< TYPE >(#TYPE,      
    12                                reinterpret_cast< TYPE *>(quintptr(-1))); 
    13                 return metatype_id;                                     
    14             }                                                           
    15     };                                                                  
    16     QT_END_NAMESPACE
    复制代码
    • 宏展开是一个在Qt的命名空间中的一个类模板的特化 QMetaTypeId<TYPE>

    • 该类含一个enum和一个返回!QMetaType的id的成员函数

    qRegisterMetaType(const char *typeName)

    代码来源:src/corelib/kernel/qmetatype.h

    复制代码
     1 template <typename T>
     2 int qRegisterMetaType(const char *typeName
     3 #ifndef qdoc
     4     , T * dummy = 0
     5 #endif
     6 )
     7 {
     8     const int typedefOf = dummy ? -1 : QtPrivate::QMetaTypeIdHelper<T>::qt_metatype_id();
     9     if (typedefOf != -1)
    10         return QMetaType::registerTypedef(typeName, typedefOf);
    11 
    12     typedef void*(*ConstructPtr)(const T*);
    13     ConstructPtr cptr = qMetaTypeConstructHelper<T>;
    14     typedef void(*DeletePtr)(T*);
    15     DeletePtr dptr = qMetaTypeDeleteHelper<T>;
    16 
    17     return QMetaType::registerType(typeName, reinterpret_cast<QMetaType::Destructor>(dptr),
    18                                    reinterpret_cast<QMetaType::Constructor>(cptr));
    19 }
    复制代码
    • 该函数的核心就是调用了registerType 函数
    • 两个Helper模板函数分别对构造和析构函数进行封装

    registerType

    代码来源:src/corelib/kernel/qmetatype.cpp

    复制代码
     1 int QMetaType::registerType(const char *typeName, Destructor destructor,
     2                             Constructor constructor)
     3 {
     4     QVector<QCustomTypeInfo> *ct = customTypes();
     5     if (!ct || !typeName || !destructor || !constructor)
     6         return -1;
     7 
     8 #ifdef QT_NO_QOBJECT
     9     NS(QByteArray) normalizedTypeName = typeName;
    10 #else
    11     NS(QByteArray) normalizedTypeName = QMetaObject::normalizedType(typeName);
    12 #endif
    13 
    14     int idx = qMetaTypeStaticType(normalizedTypeName.constData(),
    15                                   normalizedTypeName.size());
    16 
    17     if (!idx) {
    18         QWriteLocker locker(customTypesLock());
    19         idx = qMetaTypeCustomType_unlocked(normalizedTypeName.constData(),
    20                                            normalizedTypeName.size());
    21         if (!idx) {
    22             QCustomTypeInfo inf;
    23             inf.typeName = normalizedTypeName;
    24             inf.constr = constructor;
    25             inf.destr = destructor;
    26             inf.alias = -1;
    27             idx = ct->size() + User;
    28             ct->append(inf);
    29         }
    30     }
    31     return idx;
    32 }
    复制代码

    函数功能:

    • 根据类型名查找其MetaType类型,如果已存在,则直接返回;否则创建后返回。

    • 创建一个 !QCustomTypeInfo 对象
    • 该对象包含要类型的构造、析构信息,已经规范化后的类型名
    • 该对象存入一个全局的!QVector中

    qRegisterMetaType()

    看manual,可以知道,qRegisterMetaType 还有一个无参的重载函数。

    1 template <typename T>
    2 inline int qRegisterMetaType()
    3 {
    4    return qMetaTypeId(static_cast<T *>(0));
    5 } 
    • 函数看起来和带参数的那个似乎区别很大(难道不是么?)。
    • 手册中告诉我们,执行这个的时候,模板参数T必须用 Q_DECLARE_METATYPE() 声明过
    • 能猜到原因吗?注意看前面 Q_DECLARE_METATYPE() 代码,
    • 对了。类中的成员函数qt_metatype_id中包含对qRegisterMetaType(typeName)的调用
    • 这儿就是辗转调用了这个带参数的qRegisterMetaType函数 

    unregisterType(const char *typeName)

    函数的作用是取消自己先前注册的某个metatype类型。

    前面提到注册信息在一个全局的 QVector<QCustomTypeInfo>中,当取消注册的时候是怎么样的呢?直接删除Vector中相应的项么?源码告诉我们,不是的。

    实际是查找到相应的项,清空该项的内容

    复制代码
     1 void QMetaType::unregisterType(const char *typeName)
     2 {
     3     QVector<QCustomTypeInfo> *ct = customTypes();
     4     if (!ct || !typeName)
     5         return;
     6 
     7 #ifdef QT_NO_QOBJECT
     8     NS(QByteArray) normalizedTypeName = typeName;
     9 #else
    10     NS(QByteArray) normalizedTypeName = QMetaObject::normalizedType(typeName);
    11 #endif
    12     QWriteLocker locker(customTypesLock());
    13     for (int v = 0; v < ct->count(); ++v) {
    14         if (ct->at(v).typeName == typeName) {
    15             QCustomTypeInfo &inf = (*ct)[v];
    16             inf.typeName.clear();
    17             inf.constr = 0;
    18             inf.destr = 0;
    19             inf.alias = -1;
    20         }
    21     }
    22 }
    复制代码

     

    Q_DECLARE_PRIVATE与Q_DECLARE_PUBLIC

    这两个宏在Qt的源码中随处可见,重要性不言而喻。在 部落格的 Inside Qt Series 系列文章中,他用了3篇文章来讲这个问题。

    因为 QObject 本身比较复杂,这两个宏和一个复杂的东西搅和到一块,还真是不好理解。不过幸好,这个两个宏和QObject 没有必然的联系。故接下来,忘记 QObject,看一个普通的C++的类

    例子

    类 QtServiceController 定义:

    复制代码
    1 class QtServiceController
    2 {
    3  Q_DECLARE_PRIVATE(QtServiceController)
    4 public:
    5  QtServiceController(const QString &name);
    6  //省略其他
    7 private:
    8  QtServiceControllerPrivate *d_ptr;
    9 }; 
    复制代码

    类 QtServiceControllerPrivate 定义:

    复制代码
    1 class QtServiceControllerPrivate
    2 {
    3     Q_DECLARE_PUBLIC(QtServiceController)
    4 public:
    5     QString serviceName;
    6     QtServiceController *q_ptr;
    7 }; 
    复制代码

    将所有的private数据成员,独立出来放于一个独立的私有的数据对象中。这一点是比较好理解的,那么这两个宏在这起什么作用呢?

    注意:上面定义的两个指针 d_ptr, q_ptr。

    宏定义

    宏定义在 QtGlobal(即qglobal.h)头文件中:

    复制代码
     1 #define Q_DECLARE_PRIVATE(Class) 
     2     inline Class##Private* d_func() { return reinterpret_cast<Class##Private *>(qGetPtrHelper(d_ptr)); } 
     3     inline const Class##Private* d_func() const { return reinterpret_cast<const Class##Private *>(qGetPtrHelper(d_ptr)); } 
     4     friend class Class##Private;
     5 
     6 #define Q_DECLARE_PRIVATE_D(Dptr, Class) 
     7     inline Class##Private* d_func() { return reinterpret_cast<Class##Private *>(Dptr); } 
     8     inline const Class##Private* d_func() const { return reinterpret_cast<const Class##Private *>(Dptr); } 
     9     friend class Class##Private;
    10 
    11 #define Q_DECLARE_PUBLIC(Class)                                    
    12     inline Class* q_func() { return static_cast<Class *>(q_ptr); } 
    13     inline const Class* q_func() const { return static_cast<const Class *>(q_ptr); } 
    14     friend class Class;
    复制代码

    这两个宏在这看起来真蛮绕的,因为这个例子太简单了,两个宏的威力发挥不出来。反正核心就是:

    • 在 QtServiceController 中通过 d_func() 可以获得 QtServiceControllerPrivate 的指针 d_ptr

    • 在 QtServiceControllerPrivate 中通过 q_func() 可以获得 QtServiceController 的指针 q_ptr

    Q_D 与 Q_Q

    这是另两个Qt源码中随处可见的宏,那么它们有什么用呢?

    1 #define Q_D(Class) Class##Private * const d = d_func()
    2 #define Q_Q(Class) Class * const q = q_func()

    两个宏展开后分别是对 d_func 和 q_func 两个函数的调用,返回值分别赋值给 d 和 q 两个指针变量。

    于是:

    • 在 QtServiceController 中的成员函数中,我们只需要添加 Q_D(QtServiceController) 宏,在该函数内就可以直接用 d 来指代 d_ptr

    • 在 QtServiceControllerPrivate 中的成员函数中,我们只需要添加 Q_Q(QtServiceController)宏,在该函数内就可以直接用 q 来指代 q_ptr

    d_ptr与q_ptr

    绕这么大圈,为什么不直接用 d_ptr 与 q_ptr 呢。在,在我们的例子中,确实可以直接用,而且会更直接更简单。官方这么用了,或许是为了和其他类保持一致吧。

    但在其他情况下,这么做显然是有意义的,因为 d_ptr 与 d,q_ptr 与 q 的类型并不一致(比如QObject系列)。这也是为何宏展开后有cast的原因。

    参考

    C++ GUI Qt4 编程 一书多线程部分提到invokeMethod的用法

    QMetaObject::invokeMethod(label, SLOT(setText(const QString&)), Q_ARG(QString, "Hello"));

    而 Qt Manual 中介绍却是

    • You only need to pass the name of the signal or slot to this function, not the entire signature. For example, to asynchronously invoke the animateClick() slot on a QPushButton, use the following code:

    QMetaObject::invokeMethod(pushButton, "animateClick");

    这可怎么办?一个是官方的图书,一个是官方的Manual。是否意味着两种方式都可以呢,还是说Qt的早期版本用的是前者?

    查 Qt4.7/Qt4.6/Qt4.5/Qt4.4/Qt4.3/Qt4.2/Qt4.1/Qt4.0 ,结果发现都没有提到前面的用法。是不是书的出错呢?网上搜一下:确实有人抱怨它不工作

    测试

    本着事实就是的精神,还是先写个程序测试一下:

    复制代码
     1 #include <QtCore/QObject>
     2 #include <QtCore/QDebug>
     3 #include <QtCore/QCoreApplication>
     4 class Test : public QObject
     5 {
     6 Q_OBJECT
     7 public:
     8 Test(QObject * parent):QObject(parent)
     9 {
    10 connect(this, SIGNAL(sig1(QString)), SLOT(slot1(QString)));
    11 QMetaObject::invokeMethod(this, "sig1", Q_ARG(QString, "constructor"));
    12 }
    13 Q_INVOKABLE void method1(const QString& t)
    14 {
    15 qDebug()<<"from method:"<<t;
    16 }
    17 
    18 signals:
    19 void sig1(const QString& t);
    20 
    21 public slots:
    22 void slot1(const QString& t)
    23 {
    24 qDebug()<<"from slot:"<<t;
    25 }
    26 };
    27  #include "main.moc"
    28 int main(int argc, char *argv[])
    29 {
    30 QCoreApplication a(argc, argv);
    31 Test obj(0);
    32 QMetaObject::invokeMethod(&obj, "slot1", Q_ARG(QString, "Hello"));
    33 QMetaObject::invokeMethod(&obj, "method1", Q_ARG(QString, "Hello"));
    34     QMetaObject::invokeMethod(&obj, SLOT(slot1(QString)), Q_ARG(QString, "Hello with SLOT"));
    35 QMetaObject::invokeMethod(&obj, METHOD(method1(QString)), Q_ARG(QString, "Hello with METHOD"));
    36 return a.exec();
    37 } 
    复制代码

    确实如他人所说,SLOT这种用法不工作

    1 from slot: "constructor"
    2 from slot: "Hello"
    3 from method: "Hello"
    4 QMetaObject::invokeMethod: No such method Test::1slot1(QString)(QString)
    5 QMetaObject::invokeMethod: No such method Test::0method1(QString)(QString)

    顺便看看源码吧

    复制代码
     1 bool QMetaObject::invokeMethod(QObject *obj,
     2                                const char *member,
     3                                Qt::ConnectionType type,
     4                                QGenericReturnArgument ret,
     5                                QGenericArgument val0,
     6                                QGenericArgument val1,
     7                                QGenericArgument val2,
     8                                QGenericArgument val3,
     9                                QGenericArgument val4,
    10                                QGenericArgument val5,
    11                                QGenericArgument val6,
    12                                QGenericArgument val7,
    13                                QGenericArgument val8,
    14                                QGenericArgument val9)
    15 {
    16     if (!obj)
    17         return false;
    18 
    19     QVarLengthArray<char, 512> sig;
    20     int len = qstrlen(member);
    21     if (len <= 0)
    22         return false;
    //生成函数原型字符串(从这儿可以看到书中方法不工作的原因) 23 sig.append(member, len); 24 sig.append('('); 25 26 const char *typeNames[] = {ret.name(), val0.name(), val1.name(), val2.name(), val3.name(), 27 val4.name(), val5.name(), val6.name(), val7.name(), val8.name(), 28 val9.name()}; 29 30 int paramCount; 31 for (paramCount = 1; paramCount < MaximumParamCount; ++paramCount) { 32 len = qstrlen(typeNames[paramCount]); 33 if (len <= 0) 34 break; 35 sig.append(typeNames[paramCount], len); 36 sig.append(','); 37 } 38 if (paramCount == 1) 39 sig.append(')'); // no parameters 40 else 41 sig[sig.size() - 1] = ')'; 42 sig.append(''); 43 //在元对象系统中看该函数信息是否存在 44 int idx = obj->metaObject()->indexOfMethod(sig.constData()); 45 if (idx < 0) { 46 QByteArray norm = QMetaObject::normalizedSignature(sig.constData()); 47 idx = obj->metaObject()->indexOfMethod(norm.constData()); 48 } 49 50 if (idx < 0 || idx >= obj->metaObject()->methodCount()) { 51 qWarning("QMetaObject::invokeMethod: No such method %s::%s", 52 obj->metaObject()->className(), sig.constData()); 53 return false; 54 }
    //获得相应的 QMetaMethod,调用其 invoke 方法 55 QMetaMethod method = obj->metaObject()->method(idx); 56 return method.invoke(obj, type, ret, 57 val0, val1, val2, val3, val4, val5, val6, val7, val8, val9); 58 }
    复制代码

    接着看看它的源码,首先:

    * 如果指定了返回值,检查返回值的类型是否和QMetaMethod 的中的一致

    复制代码
      1 bool QMetaMethod::invoke(QObject *object,
      2                          Qt::ConnectionType connectionType,
      3                          QGenericReturnArgument returnValue,
      4                          QGenericArgument val0,
      5                          QGenericArgument val1,
      6                          QGenericArgument val2,
      7                          QGenericArgument val3,
      8                          QGenericArgument val4,
      9                          QGenericArgument val5,
     10                          QGenericArgument val6,
     11                          QGenericArgument val7,
     12                          QGenericArgument val8,
     13                          QGenericArgument val9) const
     14 {
     15     if (!object || !mobj)
     16         return false;
     17 
     18     Q_ASSERT(mobj->cast(object));
     19 
     20     // check return type
     21     if (returnValue.data()) {
     22         const char *retType = typeName();
     23         if (qstrcmp(returnValue.name(), retType) != 0) {
     24             // normalize the return value as well
     25             // the trick here is to make a function signature out of the return type
     26             // so that we can call normalizedSignature() and avoid duplicating code
     27             QByteArray unnormalized;
     28             int len = qstrlen(returnValue.name());
     29 
     30             unnormalized.reserve(len + 3);
     31             unnormalized = "_(";        // the function is called "_"
     32             unnormalized.append(returnValue.name());
     33             unnormalized.append(')');
     34 
     35             QByteArray normalized = QMetaObject::normalizedSignature(unnormalized.constData());
     36             normalized.truncate(normalized.length() - 1); // drop the ending ')'
     37 
     38             if (qstrcmp(normalized.constData() + 2, retType) != 0)
     39                 return false;
     40         }
     41     }
     42 
     43     // check argument count (we don't allow invoking a method if given too few arguments)
     44     const char *typeNames[] = {
     45         returnValue.name(),
     46         val0.name(),
     47         val1.name(),
     48         val2.name(),
     49         val3.name(),
     50         val4.name(),
     51         val5.name(),
     52         val6.name(),
     53         val7.name(),
     54         val8.name(),
     55         val9.name()
     56     };
     57     int paramCount;
     58     for (paramCount = 1; paramCount < MaximumParamCount; ++paramCount) {
     59         if (qstrlen(typeNames[paramCount]) <= 0)
     60             break;
     61     }
     62     int metaMethodArgumentCount = 0;
     63     {
     64         // based on QMetaObject::parameterNames()
     65         const char *names = mobj->d.stringdata + mobj->d.data[handle + 1];
     66         if (*names == 0) {
     67             // do we have one or zero arguments?
     68             const char *signature = mobj->d.stringdata + mobj->d.data[handle];
     69             while (*signature && *signature != '(')
     70                 ++signature;
     71             if (*++signature != ')')
     72                 ++metaMethodArgumentCount;
     73         } else {
     74             --names;
     75             do {
     76                 ++names;
     77                 while (*names && *names != ',')
     78                     ++names;
     79                 ++metaMethodArgumentCount;
     80             } while (*names);
     81         }
     82     }
     83     if (paramCount <= metaMethodArgumentCount)
     84         return false;
     85 
    //为了利用现有的代码来规范化这儿返回值的类型,这儿构造了一个函数_(typeOfReturn)

              //检查参数个数,传递的参数是否不少于需要的参数

              //检查Connection的类型,处理AutoConnection

     86     // check connection type
     87     QThread *currentThread = QThread::currentThread();
     88     QThread *objectThread = object->thread();
     89     if (connectionType == Qt::AutoConnection) {
     90         connectionType = currentThread == objectThread
     91                          ? Qt::DirectConnection
     92                          : Qt::QueuedConnection;
     93     }
     94 
     95 #ifdef QT_NO_THREAD
     96     if (connectionType == Qt::BlockingQueuedConnection) {
     97         connectionType = Qt::DirectConnection;
     98     }
     99 #endif
    100 
    101     // invoke!
    102     void *param[] = {
    103         returnValue.data(),
    104         val0.data(),
    105         val1.data(),
    106         val2.data(),
    107         val3.data(),
    108         val4.data(),
    109         val5.data(),
    110         val6.data(),
    111         val7.data(),
    112         val8.data(),
    113         val9.data()
    114     };
    115     // recompute the methodIndex by reversing the arithmetic in QMetaObject::property()
    116     int idx_relative = ((handle - priv(mobj->d.data)->methodData) / 5);
    117     int idx_offset =  mobj->methodOffset();
    118     QObjectPrivate::StaticMetaCallFunction callFunction =
    119         (QMetaObjectPrivate::get(mobj)->revision >= 6 && mobj->d.extradata)
    120         ? reinterpret_cast<const QMetaObjectExtraData *>(mobj->d.extradata)->static_metacall : 0;
    121     //对于 直连的,直接调 metacall,它进而去调用对象的 qt_metacall
    122     if (connectionType == Qt::DirectConnection) {
    123         if (callFunction) {
    124             callFunction(object, QMetaObject::InvokeMetaMethod, idx_relative, param);
    125             return true;
    126         } else {
    127             return QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, idx_relative + idx_offset, param) < 0;
    128         }
    129     } else if (connectionType == Qt::QueuedConnection) { //对于 Queued 的连接,post 相应的事件,进而转到对象的event()函数中
    130         if (returnValue.data()) {
    131             qWarning("QMetaMethod::invoke: Unable to invoke methods with return values in "
    132                      "queued connections");
    133             return false;
    134         }
    135 
    136         int nargs = 1; // include return type
    137         void **args = (void **) qMalloc(paramCount * sizeof(void *));
    138         Q_CHECK_PTR(args);
    139         int *types = (int *) qMalloc(paramCount * sizeof(int));
    140         Q_CHECK_PTR(types);
    141         types[0] = 0; // return type
    142         args[0] = 0;
    143 
    144         for (int i = 1; i < paramCount; ++i) {
    145             types[i] = QMetaType::type(typeNames[i]);
    146             if (types[i]) {
    147                 args[i] = QMetaType::construct(types[i], param[i]);
    148                 ++nargs;
    149             } else if (param[i]) {
    150                 qWarning("QMetaMethod::invoke: Unable to handle unregistered datatype '%s'",
    151                          typeNames[i]);
    152                 for (int x = 1; x < i; ++x) {
    153                     if (types[x] && args[x])
    154                         QMetaType::destroy(types[x], args[x]);
    155                 }
    156                 qFree(types);
    157                 qFree(args);
    158                 return false;
    159             }
    160         }
    161 
    162         QCoreApplication::postEvent(object, new QMetaCallEvent(idx_offset, idx_relative, callFunction,
    163                                                         0, -1, nargs, types, args));
    164     } else { // blocking queued connection
    165 #ifndef QT_NO_THREAD
    166         if (currentThread == objectThread) {
    167             qWarning("QMetaMethod::invoke: Dead lock detected in "
    168                         "BlockingQueuedConnection: Receiver is %s(%p)",
    169                         mobj->className(), object);
    170         }
    171         //对于 bolckedqueued 的连接,使用了信号量
    172         QSemaphore semaphore;
    173         QCoreApplication::postEvent(object, new QMetaCallEvent(idx_offset, idx_relative, callFunction,
    174                                                         0, -1, 0, 0, param, &semaphore));
    175         semaphore.acquire();
    176 #endif // QT_NO_THREAD
    177     }
    178     return true;
    179 }
    复制代码
    转自:http://www.cnblogs.com/lfsblack/p/5284602.html
  • 相关阅读:
    win10如何将现有的桌面壁纸找出来
    js 显示网站当前访客是几位访客
    SELECT DISTINCT 取列中所有不重复的值
    mysql5.6和8.0中都没有len()函数,获取字符串长度的函数是length()
    TOP 子句用于规定要返回的记录的数目。
    sqlmap提示you haven't updated sqlmap for more than 126 days!!!
    利用代码生成a-z的所有字母的指定长度的组合字典
    生成图形验证码 将图形验证码流写到前台
    JDK历史版本
    mysql 数据库隔离级别
  • 原文地址:https://www.cnblogs.com/liushui-sky/p/6474085.html
Copyright © 2020-2023  润新知