Qt Property System
Posted unclerunning
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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_LITERAL
static 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
以上是关于Qt Property System的主要内容,如果未能解决你的问题,请参考以下文章