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
- Qt Property System
- Qt 之 The Property System
- system property
- Qt属性系统(Qt's Property System)及在Pyqt中的应用
- Android System Property 一 System Property
- Android Property System
- Android Property System
- android property system
- Android Property System
- J2ME system property
- java System Property
- Android Property System (Forwarding)
- Android Property System
- (转载) Android Property System
- Android Property System
- android property system
- Android Property System
- Android Property System
- HTML-css 简单网页小制作(图片、文字)---Day2
- ByteBuffer常用方法详解
- 资料搬运 理解LINUX IO
- 线性回归
- POJ 2656 Unhappy Jinjin
- Qt Property System
- asyncio 官方文档
- Linux下的硬链接和软链接
- OpenGL 的空间变换(下):空间变换
- (最小生成树)ZOJ 1406 Jungle Roads
- 如何修改jar包中的class文件
- poj-3083-Children of the Candy Corn-DFS+BFS
- 【hibernate】多对一,一对多关系
- SVN服务器搭建与使用三