Qt Property System

来源:互联网 发布:丝丽隆鼻 知乎 编辑:程序博客网 时间:2024/06/03 16:54

Qt Property System

  • Qt Property System
    • 示例代码

如同我在Qt 信号和槽所介绍的,在MOC code generator的帮助下,qt会产生精心组织的代码将名称和位置联系在一起,知道了对象、名称,就能找出相应的位置,进而调用相应的函数。

test_1:

#include "propertysystem.h"#include <QVariant>void test_1(){    Property pt;      MyClass mc;    mc.m_str = "mc property";    pt.setProperty("property_mc", QVariant::fromValue(mc));    MyClass mc1 = pt.property("property_mc").value<MyClass>();}

代码中使用的propertysystem.h的代码在后面的示例代码 小节。

当我写下:

pt.setProperty("property_mc", QVariant::fromValue(mc));MyClass mc1 = pt.property("property_mc").value<MyClass>();

调用时,就开始好奇,他们做了什么呢,他们是怎么关联到相关属性的呢?

我们可以如上面那样操作,是因为我们知道,pt这个对象的类中用Q_PROPERTY关联了属性名”property_mc“与某个类型为MyClass的成员变量(或以MyClass类型变量为参数的成员函数),MOC在幕后已经将名称和位置的关系组织好了。

moc_propertysystem.cpp摘要:

static const qt_meta_stringdata_Property_t qt_meta_stringdata_Property = {    {        QT_MOC_LITERAL(0, 0, 8),  // "Property"        QT_MOC_LITERAL(1, 9, 11), // "property_mc"        QT_MOC_LITERAL(2, 21, 7), // "MyClass"        QT_MOC_LITERAL(3, 29, 12) // "property_int"    },      "Property\0property_mc\0MyClass\0property_int"};#undef QT_MOC_LITERALstatic const uint qt_meta_data_Property[] = {...       2,   14, // properties... //14:  // 从这里可以看到: // 名为"property_mc" 的属性的相对位置为0,因为它在最前面。  // 名为"property_int"的属性的相对位置为1,在属性"property_mc"的后面。 // properties: name,             type,                   flags                1,             0x80000000 | 2,          0x0009500b, //         "property_mc"       "MyClass"                3,             QMetaType::Int,          0x00095003, //         "property_int"         0        // eod};void Property::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a){#ifndef QT_NO_PROPERTIES    if (_c == QMetaObject::ReadProperty) {        Property *_t = static_cast<Property *>(_o);        Q_UNUSED(_t)        void *_v = _a[0];        switch (_id) {        //对应相对位置0            case 0: *reinterpret_cast< MyClass*>(_v) = _t->m_mc; break;        //对应相对位置1            case 1: *reinterpret_cast< int*>(_v) = _t->getInt(); break;        default: break;        }    } else if (_c == QMetaObject::WriteProperty) {        Property *_t = static_cast<Property *>(_o);        Q_UNUSED(_t)        void *_v = _a[0];        switch (_id) {        //对应相对位置0               case 0:            if (_t->m_mc != *reinterpret_cast< MyClass*>(_v)) {                _t->m_mc = *reinterpret_cast< MyClass*>(_v);            }            break;        //对应相对位置1              case 1: _t->setInt(*reinterpret_cast< int*>(_v)); break;        default: break;        }    } else if (_c == QMetaObject::ResetProperty) {    }#endif // QT_NO_PROPERTIES    Q_UNUSED(_o);    Q_UNUSED(_id);    Q_UNUSED(_c);    Q_UNUSED(_a);}//第二个参数_id是某个属性在继承树中的绝对位置[相对位置+属性偏移]int Property::qt_metacall(QMetaObject::Call _c, int _id, void **_a){  //先在父类中查找匹配    _id = PropertyBase::qt_metacall(_c, _id, _a);    //如果返回的_id小于0,表示属性在继承树的某个上层类中定义,或者不存在。    if (_id < 0)        return _id;#ifndef QT_NO_PROPERTIES   if (_c == QMetaObject::ReadProperty  ||       _c == QMetaObject::WriteProperty ||       _c == QMetaObject::ResetProperty ||       _c == QMetaObject::RegisterPropertyMetaType)    {        //返回值_id为属性相对该类的相对位置。             qt_static_metacall(this, _c, _id, _a);        //对_id减2很重要,2是本类定义的属性数量。        _id -= 2;    }  ...#endif // QT_NO_PROPERTIES    return _id;}

其实明白了信号和槽的工作机制后,理解属性系统的工作机制就容易多了,这里以上面的test_1为例,简单地说说setProperty和property的工作机制:

函数根据输入的属性名参数 “property_mc”,从pt开始,在QMetaObject组成的继承树中,自下向上地查找定义了 “property_mc”属性的类的QMetaObject,因为Property类定义了 “property_mc”属性,所以就找到了Property类的QMetaObject:Property::staticMetaObject。然后得到 “property_mc”属性相对于Property::staticMetaObject的相对位置,在qt_meta_data_Property[]中我们看到, “property_mc”相对于Property::staticMetaObject的相对位置为0。有了这两个信息:

  • 继承树中第一个定义了 “property_mc”属性的类的staticMetaObject:Property::staticMetaObject;
  • “property_mc”属性相对于找到的staticMetaObject的相对位置:0。

就可以调用Property::qt_static_metacall ,像这样:

Property::qt_static_metacall(&pt,QMetaObject::WriteProperty,0,value);Property::qt_static_metacall(&pt,QMetaObject::ReadProperty,0,value);

qt_static_metacall函数再进一步根据传递进来的相对位置0,执行case 0。

得到两个信息之后,还有一种调用路径也能达到同样的目的,这次不调用Property::qt_static_metacall,而是调用Property::qt_metacall:

bool QMetaProperty::write(QObject *object, const QVariant &value) const{    if (!object || !isWritable())        return false;    QVariant v = value;    uint t = QVariant::Invalid;...    int status = -1;    // the flags variable is used by the declarative module to implement    // interception of property writes.    int flags = 0;    void *argv[] = { 0, &v, &status, &flags };    if (t == QMetaType::QVariant)        argv[0] = &v;    else        argv[0] = v.data();    if (priv(mobj->d.data)->flags & PropertyAccessInStaticMetaCall &&         mobj->d.static_metacall)      //调用qt_static_metacall        mobj->d.static_metacall(object, QMetaObject::WriteProperty,                                 idx, argv);    else      //转而调用object->qt_metacall         QMetaObject::metacall(object, QMetaObject::WriteProperty,                               idx + mobj->propertyOffset(), argv);    return status;}

可以看到,传给qt_metacall的位置是加上了偏移的绝对位置。QObject有一个objectName : QString属性,PropertyBase有一个property_name : QString属性,对于本例,代码中object就是pt,mobj就是Property::staticMetaObject,所以mobj->propertyOffset()返回的偏移值就是1(QObject)+1(PropertyBase) = 2,idx就是 “property_mc”属性的相对位置,idx + mobj->propertyOffset() = 2就是 “property_mc”属性在继承树中的绝对位置。

显然,直接通过绝对位置作为参数调用Property::qt_static_metacall处理是不行的,qt_metacall做了一个很用心的处理,以绝对位置作为参数,先调用直接基类的qt_metacall,直接基类的qt_metacall继续这个传递过程,直到传递到了QObject::qt_metacall。对于这个例子,以2为参数传递到QObject::qt_metacall之后,QObject::qt_metacall以2为参数调用QObject::qt_static_metacall,2匹配到case default,什么不做,返回到QObject::qt_metacall,QObject::qt_metacall返回2-1(QObject的属性个数)=1,回到PropertyBase::qt_metacall,PropertyBase::qt_metacall以返回值1为参数调用PropertyBase::qt_static_metacall,1匹配到case default,什么不做,返回到PropertyBase::qt_metacall,PropertyBase::qt_metacall返回1-1(PropertyBase的属性个数)=0,回到Property::qt_metacall,Property::qt_metacall以返回值0为参数调用Property::qt_static_metacall,配置到case 0,并执行case 0。

殊途通过!

仔细分析第二种情况下的执行过程,发现QObject::qt_metacall和PropertyBase::qt_metacall就像打了个酱油一样,除了从绝对偏移中减去自己的属性个数之外,他们没做什么。从QObject::qt_metacall一步步返回到Property::qt_metacall的过程中,会不断的将继承树上层的属性数从绝对位置id中减掉,到最后,我们得到的就是一个相对于Property::staticMetaObject的相对位置:0。

示例代码

propertybase.h

#ifndef PROPERTYBASE_H#define PROPERTYBASE_H#include <QObject>class PropertyBase : public QObject{    // 先启用Meta object system    Q_OBJECT    // 启用Property system    Q_PROPERTY(QString property_name READ getName WRITE setName)public:    PropertyBase(QObject *parent = 0) :QObject(parent){}private:    QString getName() const { return m_name; }    void setName(QString const & _name){ m_name = _name; }private:    QString m_name;};#endif

myclass.h

#ifndef MYCLASS_H#define MYCLASS_H#include <QString>#include <QObject>class MyClass{public:    MyClass() = default;    MyClass(const MyClass &_t){        m_str = _t.m_str;    }    bool operator !=(MyClass const & _mc ){        return m_str != _mc.m_str;    }    QString m_str;};//延迟注册Q_DECLARE_METATYPE(MyClass)

propertysystem.h

#ifndef PROPERTY_H#define PROPERTY_H#include <QObject>#include <propertybase.h>#include "myclass.h"class Property : public PropertyBase{    // 先启用Meta object system    Q_OBJECT    // 启用Property system    //Q_PROPERTY(MyClass property_mc READ getMc WRITE setMc)    Q_PROPERTY(MyClass property_mc MEMBER m_mc)    Q_PROPERTY(int property_int READ getInt WRITE setInt)public:    Property(QObject *parent = 0) :PropertyBase(parent){}private:    MyClass getMc() const { return m_mc; }    void setMc(MyClass &_ob){ m_mc = _ob;}    int getInt() const { return m_i; }    void setInt(int _i){ m_i = _i; }private:    MyClass m_mc;    int m_i;};#endif
0 0
原创粉丝点击