C 语言

关注公众号 jb51net

关闭
首页 > 软件编程 > C 语言 > Qt属性系统

C++ Qt属性系统详细介绍

投稿:lqh

这篇文章主要介绍了C++ Qt属性系统详细介绍的相关资料,需要的朋友可以参考下

C++ Qt属性系统详细介绍

Qt提供了一个绝妙的属性系统。跟那些由编译器提供的属性差不多。然而,作为一个独立于编译器和平台的库,Qt不依赖于非标准的编译特性,比如__property 或[property]。Qt可以在任何平台上的标准编译器下编译。Qt属性系统基于元数据对象系统--就是那个提供了对象内置信号和槽通讯机制的家伙。

声明属性需要什么

要声明一个属性,需在继承自QObject的类中使用Q_PROPERTY()宏。

Q_PROPERTY(type name
  READ getFunction
  [WRITE setFunction]
  [RESET resetFunction]
  [NOTIFY notifySignal]
  [DESIGNABLE bool]
  [SCRIPTABLE bool]
  [STORED bool]
  [USER bool]
  [CONSTANT]
  [FINAL])


下面是一些典型的声明属性的示例:

Q_PROPERTY(bool focus READ hasFocus) 
Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled) 
Q_PROPERTY(QCursor cursor READ cursor WRITE setCursor RESET unsetCursor) 

 READ,WRITE和RESET函数都可以被继承。它们也可以是虚函数。当它们在被多重继承中被继承时,它们必须出现在第一个被继承的类中。

属性的类型可以是被QVariant支持的所有类型,也可以是用户定义的类型。在下面的例子中,类QDate被当作用户自定义类型。
Q_PROPERTY(QDate data READ getDate WRITE setDate)

因为QDate是用户定义的,你必须包含<QDate>头文件。

对于QMap,QList和QValueList属性,属性的值是一个QVariant,它包含整个list或map。注意Q_PROPERTY字符串不能包含逗号,因为逗号会划分宏的参数。因此,你必须使用QMap作为属性的类型而不是QMap<QString,QVariant>。为了保持一致性,也需要用QList和QValueList而不是QList<QVariant>和QValueList<QVariant>。

 通过元数据对象系统读写属性

一个属性可以使用常规函数QObject::property()和QObject::setProperty()进行读写,不用知道属性所在类的任何细节,除了属性的名字。在下面的小代码片段中,调用QAbstractButton::setDown()和QObject::setProperty()都把属性设置为“down”。

QPushButton *button = new QPushButton; 
QObject *object = button; 
button->setDown(true); 
object->setProperty("down", true); 

通过WRITE操作器来操作一个属性是上面两者中更好的,因为它快并且在编译时给于更好的诊断帮助,但是以这种方式设置属性要求你必须在编译时了解其类。通过名字来操作属性使你可以操作在编译器你不了解的类。你可以在运行时发现一个类的属性们,通过查询它的QObject,QMetaObject和QMetaProerties。

QObject *object = ... 
const QMetaObject *metaobject = object->metaObject(); 
int count = metaobject->propertyCount(); 
for (int i=0; i<count; ++i) { 
  QMetaProperty metaproperty = metaobject->property(i); 
  const char *name = metaproperty.name(); 
  QVariant value = object->property(name); 
  ... 
} 

在上面的代码片段中,QMetaObject::property()被用于获取未知类中的属性的metadata。从metadata中获取属性名然后传给QObject::property()来获取

一个简单例子

假设我们有一个类MyClass,它从QObject派生并且在它的private区使用 了Q_OBJECT宏。我们想在MyClass类中声明一个属性来持续追踪一个Priorty值。属性的值叫做priority,并且它的类型是一个在类MyClass中定义的叫做Priority的枚举。

我们在类的private区使用Q_PROPERTY()来声明属性。READ函数叫做priority,并且我们包含一个WRITE函数叫做setPriority。枚举类型必须使用Q_ENUMS()注册到元数据对象系统中。注册一个枚举类型使得枚举的名字可以在调用QObject::setProperty()时使用。我们还必须为READ和WRITE函数提供我们自己的声明。MyClass的声明看起来应该是这样的:

class MyClass : public QObject 
{ 
  Q_OBJECT 
  Q_PROPERTY(Priority priority READ priority WRITE setPriority) 
  Q_ENUMS(Priority) 
public: 
  MyClass(QObject *parent = 0); 
  ~MyClass(); 
  enum Priority { High, Low, VeryHigh, VeryLow }; 
  void setPriority(Priority priority); 
  Priority priority() const; 
}; 

READ函数是const的并且返回属性的类型。WRITE函数返回void并且具有一个属性类型的参数。元数据对象编译器强制做这些事情。

在有了一个指向MyClass实例的指针时,我们有两种方法来设置priority属性:

MyClass *myinstance = new MyClass; 
 QObject *object = myinstance; 
 myinstance->setPriority(MyClass::VeryHigh); 
 object->setProperty("priority", "VeryHigh"); 

在此例子中,枚举类型在MyClass中声明并被使用Q_ENUMS()注册到元数据对象系统中。这使得枚举值可以在调用setProperty()时做为字符串使用。如果枚举类型是在其它类中声明的,那么我们就需要用枚举的全名(如OtherClass::Priority),并且这个其它类也必须从QObject中派生并且也要注册枚举类型。

另一个简单的Q_FLAGS()也是可用的。就像Q_ENUMS(),它注册一个枚举类型,但是它把枚举类型作为一个flag的集合,也就是,值可以用OR操作来合并。一个I/O类可能具有枚举值Read和Write并且QObject::setProperty()可以接受 Read|Write。此时应使用Q_FLAGS()来注册枚举值。

动态属性

Qobject::setProperty()也可以用来在运行时向一个类的实例添加新的属性。当使用一个名字和值调用它时,如果一个对应的属性已经存在,并且如果值的类型与属性的类型兼容,那么值就被存储到属性中,然后返回true。如果值类型不兼容,属性的值就不会发生改变,就会返回false。但是如果对应名字的属性不存在,那么一个新的属性就诞生了,以传入的名字为名,以传入的值为值,但是依然会返回false。这表示返回值不能用于确定一个属性是否被设置值,除非你已经知道这个属性已经存在于QObject中了。

注意动态属性被添加到单个实现的基础中,也就是,被添加到QObject,而不是QMetaObject。一个属性可以从一个实例中删除,通过传入属性的名字和非法的QVariant值给QObject::setProperty()。默认的QVariant构造器构造一个非法的QVariant。
动态属性可用QObject::property()来查询,就行使用Q_PROPERTY()声明的属性一样。

属性和自定义类型

被属性使用的自定义类型需要使用Q_DECLARE_METATYPE()宏注册,以使它们的值能被保存在QVariant对象中。这使得它们可以用于被Q_PROPERTY()声明的静态类型中,也可以被用于动态类型中。

 感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

您可能感兴趣的文章:
阅读全文