动态属性
前面我们知道使用Q_PROPERTY宏可以为类创建变量,但是那只是静态变量。在QObject中加载和存储一些属性也是可能的,即添加动态属性。
到现在位置,我们已经对用Q_PROPERTY宏定义的那些属性进行了专门处理。这些属性对该类的QMetaObject是可知,且有对应的QMetaProperty与之对应。同一个类的所有对象会共享同一个metaObject,因而会有相同的元属性组。另一方面,在运行时获得动态属性的情况下,同一个类的两个对象会具有相同的元属性列表,但对于动态属性可以有不同动态属性列表。
class DynoProps : public QObject
{
Q_OBJECT
Q_PROPERTY(QString someString READ someString WRITE setSomeString )
public:
QString someString() {return m_someString;}
void setSomeString(QString ss) {m_someString = ss;}
QString propsInventory();
private:
QString m_someString;
};
QString DynoProps::propsInventory()
{
static const QMetaObject* meta = metaObject();//该函数由moc生成
QStringList res;
res << "Fixed Properties:";
for (int i = 0; i < meta->propertyCount(); ++i) {
res << QString("%1\t%2").arg(QString(meta->property(i).name()))/*Returns this property's name.*/
.arg(meta->property(i).read(this).toString());//(read):Reads the property's value from the given object. Returns the value if it was able to read it; otherwise returns an invalid variant.
}
res << "Dynamic Properties:";
foreach (QByteArray dpname, dynamicPropertyNames()) {
res << QString("%1\t%2").arg(QString(dpname)) .arg(property(dpname).toString());
}
return res.join("\n");
}
以上函数给出了一种显示固定属性和动态属性的方法。固定属性的清单来自QMetaObject。使用QMetaProperty::read()
或者QObject::property()
可以查询属性的值。propertyCount()
函数会设置对QMetaProperty清单进行遍历的条件。
QMetaObject无法知道动态属性。那么,如果要知道动态属性必须得使用QObject的一些方法。可以对利用QObject::dynamicPropertyNames()
返回的QList清单中的名称进行遍历,并可用QObject::property()
获得其值。
使用和输出:
int main(int argc, char *argv[])
{
QTextStream cout (stdout);
DynoProps d1,d2;
d1.setObjectName("d1");
d2.setObjectName("d2");
d1.setProperty("intprop",42);
d1.setProperty("realProp",3.1415926);
cout << d1.propsInventory() << endl<<endl;
cout << d2.propsInventory() << endl;
return 0;
}
运行结果
Fixed Properties:
objectName d1
someString
Dynamic Properties:
intprop 42
realProp 3.1415926
Fixed Properties:
objectName d2
someString
Dynamic Properties:
元类型的声明和注册
QMetaType是一个用于值类型的辅助类(helper class)。对于内置60多种数据类型,QMetaType为每个类型ID都关联了一个类型名,从而使构造和析构可用在运行时动态发生。有一个名称为QMetaType::Type
的公共枚举,它有所有QVariant兼容类型的值。即QMetaType::Type
中的枚举值与QVariant::Type
中的枚举值一样。
通过使用Q_ENUMS宏,我们可以在QVariant系统中加入一些自定义的枚举类型。使用Q_DECLARE_METATYPE(MyType)
宏把自己定义的值类型加到QMetaType列表中。它需要提供一个公共的默认构造函数,公共的拷贝构造函数和公共的析构函数。ps:编译器默认的即可。
-----------------------h-----------------
class Computer {
public:
Computer(int aa ) :
a(aa) {}
int a;
};
Q_DECLARE_METATYPE(Computer) Tag1
-----------------------cpp-----------------
int fracType = QMetaType::type("Computer");//Returns a handle to the type called typeName, or 0 if there is no such type.
[...]
Computer com(2);
QVariant var;
var.setValue(com);
cout << var.value<Computer>().a <<endl;
----------------------output------------------
258
2
如果将Tag1行注释掉,则输出为报错。error: 'qt_metatype_id' is not a member of 'QMetaTypeId<Computer>'
qRegisterMetaType()
要注册的元类型必须已经用Q_DECLARE_METATYPE声明过。模板函数qRegisterMetaType<T>()
会注册类型T并返回由QMetaType使用的内部ID。这个函数有一个重载版本,qRegisterMetaType<T>(const char* name)
,同样也是用来注册类型并返回ID 。在使用类型T作为信号和槽的参数时,qRegisterMetaType<T>()
必须在第一个连接之前调用。即在连接信号和槽之前调用。