Qt属性系统(Qt Property System)

Qt提供了巧妙的属性系统,它与某些编译器支持的属性系统相似。然而,作为平台和编译器无关的库,Qt不能够依赖于那些非标准的编译器特性,比如__property 或者 [property]。Qt的解决方案能够被任何Qt支持的平台下的标准C++编译器支持。它依赖于元对象系统(Meta_Object Sytstem),元对象系统通过信号和槽提供了对象间通讯的机制。

怎样声明属性

QObject的子类的私有域中使用Q_PROPERTY宏来声明一个属性

Q_PROPERTY(type name

(READ getFunction [WRITE setFunction] | MEMBER memberName [(READ getFunction | WRITE setFunction)])

[RESET resetFunction]

[NOTIFY notifySignal]

[REVISION int]

[DESIGNABLE bool]

[SCRITABLE bool]

[STORED bool]

[USER bool]

[CONSTANT]

[FINAL]

)

以下是来自QWidget类的一些属性声明

Q_PROPERTY(bool focus READ hasFocus)

Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled)

Q_PROPERTY(QCursor cursor READ cursor WRITE setCursor RESET unsetCursor)

以下例子展示了如何使用MEMBER关键字将类数据成员导出为Qt属性。注,NOTIFY signal必须被指定,这样才能被QML使用

Q_PROPERTY(QColor color MEMBER m_color NOTIFY colorChanged)

Q_PROPERTY(qreal spaing MEMBER m_spacing NOTIFY spaingChanged)

Q_PROPERTY(QString text MEMBER m_text NOTIFY textChanged)

...

signals:

void colorChanged();

void spacingChanged();

void textChanged(const QString &netText);

private:

QColor  m_color;

qreal     m_spacing;

QString m_text;

一个属性的表现就像一个普通的数据成员一样,但是它有额外供元对象系统访问的特性。

如果MEMBER关键字没有被指定,则一个READ访问函数是必须的。它被用来访问数据成员的值。理想地,它是一个常成员函数,它的返回类型须是属性类型或者是属性类型的常引用。比如,QWidget::focus是一个只读的属性,通过读函数,QWidget::hadFocus访问。

一个WRITE函数是可选的。它被用来设置数据成员的值。它的返回类型必须是void,而且仅能有一个参数,其类型必须是属性类型或者是属性类型的指针类型或者是属性类型引用。例如,QWidget::enabled有一个WRITE函数,QWidget::setEnabled(bool)。只读属性不需要WRITE函数。比如QWidget::focus就没有WRITE函数。

如果属性没有READ访问函数,则需要用MEMBER指定成员变量。这使得给定的成员变量在没有创建READ和WRITE的函数下可读可写。如果你需要控制变量的访问权限,也可以使用READ和WRITE函数而不仅仅是MEMBER(但是别同时使用)。

一个RESET函数也是可选的。它被用来将属性设置为上下文指定的默认值。例如,QWidget::cursor有READ和WRITE函数,QWidget::cursor() QWidget::setCursor(),同时它也有一个RESET函数QWidget::unsetCursor(),因为没有可用的setCursor调用可以确定的将cursor属性重置为上下文默认的值。RESET函数必须返回void类型,而且不带参数。

NOTIFY也是可选的。如果定义了NOTIFY则需要指定一个已经存在的信号,该信号在属性值发生改变是发射。与属性相关的信号必须有一个或者零个参数,而且必须与属性的类型相同。参数为数据成员的新值。NOTIFY信号应该仅仅当属性值真正的发生改变时发出,以避免被QML重新评估。

REVISION也是可选的,如果包含了该关键字,它定义了该属性和信号被特定版本的API使用通常是QML。如果没有包含该关键字其默认为0。

DESIGNABLE指定了该属性在GUI编辑器中是否可见(比如QtDesigner)。大多数的属性是可设计的(DESIGNABLE默认为真)。除了true和false,你还可以指定boolean成员函数。

SCRITABLE属性指定了该属性是否可以被script engine访问,其默认为真。除了true和false你还可以指定boolean函数。

STORED属性指定了该属性是否是独立的或者是否依赖于别的属性。它也指定了当保存对象属性时是否会保存该属性。大多数的属性的STORED为真。但是,QWidget::minmunWidth()的STROED为false,因为它的值是从QQWidget::minimumSize()中取得的,它的类型是QSize。

USER指定了属性是否被设计为用户可见和可编辑的。正常情况下,每一个对象只用一个USER属性(默认为false)。例如,QAbstractButton::clicked对Buttons是可编辑的(checkable)。注,QItemDelegate使用设置和访问函数色设置widget的USER属性。

CONSTANT的出现表明属性是一个常量值。对于给点的对象实例,每一次READ函数的调用都应该返回相同的值。对于不同的实例该属性可能会不相同。同时不能有WRITE函数和NOTIFY信号。

FINAL表明该属性不会再子类中被覆盖。在某些情况下它被用来优化性能,但是并没有被moc实现。必须注意,绝不在子类中覆盖FIANL属性。

READ WRITE RESET函数可以被继承。它们也可以是虚函数。当在使用多继承的类中使用的时候,其必须来自第一个类。

属性类型可以是任何QVariant支持的属性,或者是用户自定义的属性。在这个例子中,类QDate被看做用户自定义的类型。

Q_PROPERTY(QDate data READ getDate WRITE setDate)

因为QDate是用户自定义的,当声明属性时,你必须包含<QDate>头文件。

由于历史原因,QMap和QList是QVariantMap和QVariantList的同义词。

使用元对象系统读写属性

一个属性可以通过QObject::poperty()函数、QObject::setProperty()函数访问和设置。除了属性的名字之外不用知道类的别的信息。在下面的代码段中,调用函数QAbstractButton::setDown()和函数QObject::setProperty()都是设置属性“down”

QPushButton* button = new QPushButton;

QObject* object = button;

button->setDown(true);

object->setProperty("down" , true);

通过WRITE函数设置属性值,比上述两者都好,因为它效率更高而且在编译时期有更好的诊断。但是这需要你在编译实际了解整个类(能够访问其定义)。通过属性名访问属性,能够让你再不了解类的定义的情况访问或者设置属性。你可以在运行时期通过QObject,QMetaObject和QMetaProperties查询类属性。

QObject *object = ...

const QMetaObject *metaObject = object->metaObject();

int count = metaObject->propertyCount();

for (int i = 0 ; i< count; ++i) {

QMetaProperty metaProperty = metaObject->property(i);

cont char *name = metaProperty.name();

QVariant value = object->property(name);

}

在上述的代码片段中,QMetaObject::property()被用来获取定义在某个未知的类中的metaData。属性的名称通过metaData获取,并且将其传给QObject::property()来获取属性值。

假设我们有一个简单的类MyClass,它继承自QObject而且在private域中使用了Q_OBJECT。我们想声明一个属性用于跟踪权限值。该属性的名称是priority,它的类型是定义在MyClass中的Priority枚举。

我们使用Q_PROPERTY在private区里声明属性。READ函数是priority(),WRITE函数是setPriority()。枚举类型需要使用Q_ENUM()宏将其注册到Meta-Object System中。注册一个枚举类型使得枚举可以在setPropert函数中使用。我们也必须提供READ和WRITE函数的声明。该类的定义如下:

class MyClass : public QObjct

{

Q_OBJECT

Q_PROPERTY(Priority priority READ priority WRITE setPriority NOTIFY priorityChanged)

public:

explicit MyClass(QObject *parent = 0);

~MyClass();

enum Priority { High , Low , VeryHigh , VeryLow };

Q_ENUM(Priority)

void setPriority(Priority priority)

{

m_priority = priority;

}

Priority priority() const { return m_priority; }

signals:

void priorityChanged(Priority);

private:

Priority m_priority;

};

READ函数是常成员函数而且返回Priority类型。WRITE函数返回void而且只有一个类型为Priority的参数。

给定一个指向MyClass实例的类型为MyClass或者QObject的指针,我们有两种方式去设置它的priority属性。

MyClass *myinstance = new MyClass;

QObject *object = myinstance;

myinstance->setPriority(MyClass::VeryHigh);

object->setProperty("priority" , "VeryHigh");

在这个例子中,定义在MyClass中的枚举类型是属性的类型,而且被Q_ENUM()宏注册在Meta-Object System中。这使得枚举类型可以在setProperty中通过字符串访问(string),使用在别的类中定义的枚举类型,他必须被完全的声明(i.e. OtherClass::Priority)。而且那个类应该继承自QObject而且使用Q_ENUM()宏注册。

一个相似的宏Q_FLAG()。就像Q_ENUM()一样,它注册枚举类型,但是将其标记为一系列的flag,即,可以使用或操作。一个IO类有着Read和Write的枚举值,而且之后可以在QObject::setProperty传入Read | Write访问。Q_FLAG()应该被用来注册枚举类型。

动态属性

QObject::setProperty()也可以被用来在运行时期为类实例添加属性。当传入名称和值调用该函数时。如果属性名称已经在类中存在并且传入的类型与属性的类型兼容,则属性值被保存并且返回真,否则值不会被修改,但是函数返回假。但是如果给定的属性名不存在则新的属性被添加到类中,当函数仍然返回false。这意味着函数的返回值不能用来确定属性值看是否被成功的设定。除非你已经知道属性之前是否存在。

注:动态属性被添加到每一个实例中。即它们被添加到QObject中而不是QMetaObject中。可以通过传递一个空的QVariant给setProperty函数来移除属性。QVariant的默认构造函数构造一个无效的QVariant对象。

动态属性可以通过QObject::property()查询,就像Q_PROPERTY定义的属性一样。

属性和自定义类型

被属性使用的自定义类型需要使用Q_DECLARE_METATYPE宏注册。这样QVariant对象才能够保存该类的值。这个在动态和静态属性都是适用的。

为类添加额外的信息

与属性系统相对应的是Q_CLASSINFO(name , value)宏。这个宏将添加name-value的到类的元对象中。例如:

Q_CLASSINFO("Version" , "3.0.0")

和被的元对象数据一样,类信息可以在运行时通过QMetaObject::classInfo函数访问。

by linannk

2016/6/2 1:33

附:所谓添加属性到QOject中二不是QMetaObject中的意思是:

假设:有两个MyClass对象的实例a 与 b,当为a动态添加一个属性时,b是不会受到影响的。

QMetaObject是所有的MyClass实例所共享的。

关于Q_DECLARE_METATYPE,另一个重要的用途就是用于注册信号和槽中使用的用户自定类型。

如果信号和槽使用Qt::QueuedConnection连接,则还需要使用qRegisterMetaType<T>()函数注册。

此外,Qt的状态机框架和动画框架依赖属性系统。

这份文档大部分Qt的文档翻译,加上部分自己使用Qt中的心得。

时间: 2024-11-25 15:39:59

Qt属性系统(Qt Property System)的相关文章

Qt属性系统

The Property System Qt提供一个类似于其他编译器供应商提供的精致的属性系统.然而,作为一个编译器和平台独立的库,Qt并不依赖于非标准编译器特性,如__property 或 [property].Qt解决方案能在支持Qt的平台上与任何标准C++编译器一起工作.它依赖于 Meta-Object System . Requirements for DeclaringProperties 要声明一个属性,在继承Qobject的类中用 Q_PROPERTY()宏. Q_PROPERTY

[Qt入门篇]5 Qt的属性系统——声明属性

[Qt入门篇]5 Qt的属性系统--声明属性 Qt提供了灵活的属性系统,它基于Qt的元对象系统,不依赖于编译器,这保证了Qt独立于编译其和平台的特点.这篇文章主要看看如何声明属性. 属性系统比较复杂,先看一个简单的例子.在QWidget中,有很多属性的声明,找一个简单学习: Q_PROPERTY(boolmodalREADisModal) 这里出现了5个元素:Q_PROPERTY.bool.modal.READ.isModal.这五个元素都是啥作用呢? Q_PROPERTY:用于声明属性的宏:

[Qt入门篇]6 Qt的属性系统——READ/WRITE/MEMBER

在上一篇文章中,介绍了Qt属性的声明过程,本文主要介绍Q_PROPERTY()中可以使用的关键字. 在Qt5.6中,Q_PROPERTY()总共有12个关键字,本文介绍三个:READ.WRITE和MEMBER.为什么首先这三个?因为一个属性必须有一个读操作,如果没有READ定义,就必须定义MEMBER,否则这个属性无法访问,一个无法访问的属性又有什么意义呢?而WRITE则是与READ相关的操作,它是可选的. READ fun:定义了读取属性的接口fun,fun必须返回属性的类型或者属性同类型引用

关于QT的系统总结(非常全面,非常好)

源地址:http://www.cnblogs.com/wangqiguo/p/4625611.html 阅读目录 编译环境与开发流程 QT项目的构成及原理 QT中的布局 QT中的通用控件 QVariant 类型 QComboBox控件 QTableWidget控件 QTabWidget控件 QWebview控件 使用QSS 编码问题 QT的内存管理 QT的信号槽 QT中绘图 QT的线程 QT中使用第三方的dll QT中为控件添加右键菜单的方法 结束语 回到顶部 编译环境与开发流程 开发QT有两种

深度探索QT窗口系统(五篇)

窗口作为界面编程中最重要的部分,没有窗口就没有界面,是窗口让我们摆脱了DOS时代,按钮是一个窗口,文本框是一个窗口,标签页是一个窗口,...一个窗口可以由多个窗口组成,每天我们都在与窗口打交道,当你打开windows时,桌面就是一个窗口,你打开浏览器时,你也正在访问窗口...好了,回到今天的话题,QT窗口系统,QT是一个跨平台的框架,类似微软的MFC,Borland的OWL,前者只适用于windows,而使用QT你可以开发windows平台应用程序,你也可以开发linux应用程序,当然了你也可以

关于QT的系统总结

编译环境与开发流程 开发QT有两种IDE可以使用,一种是使用 VS + Qt 的插件,另一种就是使用QtCreator工具.前一种是微软的工具,用的都比较多容易上手,缺点是信号槽的支持不太好,需要手写,不能自动生成,另外可能有中文编码的问题.后一种是Qt的官方IDE,智能提示与调试功能不如VS强大,但是是跨平台的IDE,其QtDesigner设计UI界面操作比较方便,并且由于是QT官方的IDE,对编码等支持都比较好,里面集成了Qt的帮助文档.不得不说Qt的帮助文档做的是非常好的,集成进QtCre

QT开发(十四)——QT绘图系统

QT开发(十四)--QT绘图系统 一.QT绘图原理 Qt4中的2D绘图系统称为Arthur绘图系统,可以使用相同的API在屏幕上和绘图设备上进行绘制,主要基于QPainter.QPainterDevice和 QPainterEngine.QPainter执行绘图操作,QPainterDevice提供绘图设备,是一个二维空间的抽象,QPainterEngine提供一些接口.QPainter用来执行具体的绘图相关操作,如画点,画线,填充,变换,alpha通道等.QPaintDevice类是能够进行绘

汽车辐射监测系统-Qt开发[转]发

1.Wait()和Notify.NotifyAll都是Object的方法 2.多线程的协作是通过控制同一个对象的Wait()和Notify()完成 3.当调用Wait()方法时,当前线程进入阻塞状态,直到有另一线程调用了该对象的Notify()方法 package Thread.Wait; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.con

QT绘制系统简介

#3个类:QPainter,QPainterDevice 和 QPaintEngine 三个类 #qpainter用于执行绘制操作 #QPainterDevice是一个二维空间抽象,允许qpainter在其上面进行绘制,也就是qpainter工作空间 #QPaintEngine 提供了画笔(qpainter)在不同设备上进行绘制统一接口,QPaintEngine类应用于QPainter和QPaintDevice 之间,如果需要自定义一个设备时 #则不需要关心QPaintEngine 这个类的QP