Qt 中的属性系统(Property System)
21 人赞同了该文章
本节内容主要讲解我对 Qt 属性系统的理解。官方文档参考 The Property System。
如何理解“属性系统”这个概念?
一般我们说一个类有什么属性,指的就是这个类有啥成员变量。比如 People 类中有个 int age 的私有成员变量,我们就可以说这个 People 类有个“年龄”属性可以更改读取。
Qt 提供的这个属性系统,作用就是把类的信息暴露出来成为通用的大家都认识的信息。比如用 C++ 语言写的People 类中有个 int age 变量,但是如果用 QML 语言去读取就会出问题,因为 QML 有自己的规则,它不认识 C++ 啊。怎么办呢?用 Qt 的属性系统就可以解决这个问题。属性系统可以这样理解:当一个类的成员变量或者成员函数用属性系统处理一下,它们就从 C++ 内部中暴露出来,而且大家都认得。
属性系统是专门为元对象系统服务的。
如何声明一个属性并赋予读/写操作?
Qt 有自己的语法,只需要在 QObject 及其子类中用 Q_PROPERTY 宏写就可以了。比如 QWidget 这个类,其中使用属性系统的代码如下所示:
Q_PROPERTY(QCursor cursor READ cursor WRITE setCursor RESET unsetCursor)
上面这一行代码就声明了一个 cursor 属性,指明了它是 QCursor 类型的,而且指明了需要用自己的 cursor() 函数来读取这个属性值,指明了用自己的 setCursor() 函数来修改属性值,还指明了用自己的 unsetCursor() 函数进行默认值设置。一行语句就把一个属性声明好了,代码还算是很简洁的。
如何将类的变量导出为一个属性值?
注意上述的属性值 cursor 可不是 QWidget 的一个成员变量,要想将 QWidget 类中的某个变量导出成为一个属性值,应该用 MEMBER 关键字将变量注册到 Qt 的属性系统。如下:
Q_PROPERTY(QColor color MEMBER m_color NOTIFY colorChanged)
...
signals:
void colorChanged();
private:
QColor m_color;
上面的代码把 QWidget 类中的 m_color 成员变量导出为一个属性值,并且起了个新名字“color”。那么外界所能看到的或者说只认可的属性值只有 color 而不是 m_color。
我们要时刻记住,一个属性在行为上是类似于类的成员变量的。
Q_PROPERTY 中的常用格式
我们声明了一个属性值,常用的操作无非就是读、写、将成员变量导出为属性值、关联信号等。下文就从语法的角度来说说怎么做。
- 指定读取属性值的函数
假设有个布尔类型的属性值 focus,用 READ 来指定读取的函数为 hasFocus()。因为是读操作,所以养成好的习惯,hasFocus() 函数最好也是 const 类型的。代码如下:
Class Widget : public QObject
{
Q_PROPERTY(bool focus READ hasFocus)
Q_OBJECT
public:
bool hasFocus() const;
}
- 指定修改属性值的函数
还是 focus 这个属性值,要对它进行操作,用 WRITE 关键字来指定修改属性值的函数为 setFocus()。设置函数有个限制,就是函数返回值必须是 void。这也好理解,我就是修改数值而已,不需要返回什么。第二个限制是函数参数只能有一个,把目标值作为参数即可。代码如下:
Class Widget : public QObject
{
Q_PROPERTY(bool focus WRITE setFocus)
Q_OBJECT
public:
bool hasFocus() const;
void setFocus(bool on);
}
- 导出成员变量为一个属性值
上面两个读/写的属性值都不是类中的成员变量,是凭空声明出来的一个属性值。要想将类中已有的成员变量设置为属性值,需要用 MEMBER 关键字。这样的话 focus 这个属性值就变的可读可写了。要读的话用类自己的 hasFocus() 函数,要写的话用自带的 setFocus() 修改 m_focus 变量就可以了,属性值会自动跟着变的。
虽然 READ、WRITE、MEMBER 这三个关键字都可以赋予属性值可读可写的特性,但是 READ、WRITE 和 MEMBER 不能同时使用,赋予可读可写特性一次就够了,不能赋予两次。就好像一个对象不能被析构两次一样。
代码如下:
Class Widget : public QObject
{
Q_PROPERTY(bool focus MEMBER m_focus)
Q_OBJECT
public:
bool hasFocus() const;
void setFocus(bool on);
private:
bool m_focus;
}
- 给属性值设置关联的信号
如果我们希望某个属性值变化时能发射出信号,Qt 的属性系统是用 NOTIFY 关键字来指定信号。代码如下:
Class Widget : public QObject
{
Q_PROPERTY(bool focus MEMBER m_focus NOTIFY focusChanged)
Q_OBJECT
public:
bool hasFocus() const;
void setFocus(bool on);
signals:
void focusChanged();
private:
bool m_focus;
}
以上就是最常用的几个操作。除此之外还有 RESET、REVISION、DESIGNABLE、SCRIPTABLE、STORED、USER、CONSTANT、FINAL。这些关键字的含义及用法参考官方文档 The Property System。
实际操作一下读/写属性值?
上文我们创建了 QObject 的子类 Widget,并且指定了修改 focus 属性值的函数,现在我们创建 Widget 类的一个对象 w 来看看实际代码中如何写。
由于赋予属性值读/写有两种办法(方法一是用 READ、WRITE;方法二是 MEMBER ),那么实际使用中针对这两种方式使用略有不同。
如果是用 READ、WRITE,那么直接调用指定的函数即可,如:
Widget *w = new Widget;
w->setFocus(true);
如果是用 MEMBER,那么用 QObject 的 property() 和 setProperty() 两个函数,如:
Widget *w = new Widget;
w->property("focus");
w->setProperty("focus", true);
两种方法哪个好?
当然是 WRITE。它的效率跟高、速度更快,而且在编译阶段就可以进行类型检查。缺点就是还没运行前你就得了解这个类是有 setFocus() 这个函数。而采用 MEMBER 方式时,我们不需要知道这个类有啥函数、有啥变量,只需要知道这个类有一个叫“focus”的属性值就可以了。
我怎么知道一个类中有啥属性?
那既然 MEMBER 的好处是只需要知道这个类有一个叫 focus 的属性值,再极端点,我连这个类有啥属性值都不知道时怎么办?Qt 的元对象系统是如此的强大,已经为你准备好了相关的函数了。那就是 QMetaObject、QMetaProperties。下列代码输出了 Widget 类中所有的属性名称和属性值:
Widget *w = new Widget;
const QMetaObject *metaobject = w->metaObject();
int count = metaobject->propertyCount();
for (int i = 0; i < count; ++i) {
QMetaProperty metaproperty = metaobject->property(i);
const char *name = metaproperty.name();
QVariant value = w->property(name);
...
}
完整的示例
上文讲解的 Widget 类由于代码分散在各处,可能对一个类如何操作属性值没有直观的感受,下面用完整的代码来演示属性的一系列操作。
声明代码
class Widget : public QObject
{
Q_OBJECT
Q_PROPERTY(bool focus READ hasFocus WRITE setFocus NOTIFY focusChanged)
public:
Widget(QObject *parent = 0);
~Widget();
bool hasFocus() const
{
return m_focus;
}
void setFocus(bool on)
{
m_focus = on;
}
signals:
void focusChanged();
private:
bool m_focus;
}
解读
我们有一个继承于 QObject 的 Widget 类。我们用 Q_PROPERTY 宏声明了一个属性来跟踪私有变量 m_focus 值,属性名使用 focus,属性类型是个布尔类型。用 READ 指定了读取函数 hasFocus(),用 WRITE 指定了修改函数 setFocus,用 NOTIFY 指定了发射信号 focusChanged()。
使用代码
现在我们有个 Widget 指针和一个 QObject 指针,设置属性值的方法是:
Widget *w = new Widget;
w->setFocus(true);
QObject *o = w;
o->setProperty("focus", true);
在程序运行过程中添加属性
QObject::setProperty() 函数也可以用于在运行期添加新的属性。如果对象中已经存在该属性,则新添加的属性值会更改原有值并返回true;如果对象中不存在该属性,那么会自动添加到QObject中,注意了,此时返回值仍有可能是 false。所以根据返回值是不能确定属性值是否被设置成功。
说的有点拗口,这样说吧,在已知类中存在某属性的情况下,可以根据返回值判断是否设置成功。如果添加新的属性值,就不能用返回值判断是否设置成功。
注意事项
因为是在程序运行过程中新增的属性,所以这个属性可以理解为是“临时的”。它们只会加到 QObject 实例中,不会加到最为核心的 QMetaObject 实例中。就好比一个公司已经发展起来了,后来新入职的员工就不是核心人员。
那么要想删除这个“临时的”属性,只需要掉用 QObject::setProperty() 函数将空的 QVariant 值传进去即可。
如何自定义属性类型?
自定义的属性类型,需要用 Q_DECLARE_METATYPE 宏进行注册,这样就可以存储在QVariant中了。
如何给类添加额外的属性信息?
除了正规常用的属性外,我们还可以用 Q_CLASSINFO 宏给类添加额外的属性信息,语法就是“键值-对”形式。例如:
Q_CLASSINFO("Version", "3.0.0")
那么在程序运行的过程中,随时都可以调用 QMetaObject::classInfo() 函数来获取这些额外属性信息。
以上就是我对 Qt 属性系统的理解。
Qt 中的属性系统(Property System)的更多相关文章
- Android 属性系统 Property service 设定分析 (转载)
转自:http://blog.csdn.net/andyhuabing/article/details/7381879 Android 属性系统 Property service 设定分析 在Wind ...
- IOS中的属性列表----Property List
属性列表,是一种用来存储串行化后的对象的文件.因为扩展名为plist ,因此通常被称为 plist文件. plist文件通常用于储存用户设置,也可以用于存储捆绑的信息,其内容为xml格式.它可以在程序 ...
- C#中的自定义控件中的属性、事件及一些相关特性的总结(转)
摘要: C#中的自定义控件中的属性(Property).事件(Event)及一些相关特性(Attribute)的总结 今天学习了下C#用户控件开发添加自定义属性的事件,主要参考了MSDN,总结并实 ...
- Qt属性系统(Qt Property System)
Qt提供了巧妙的属性系统,它与某些编译器支持的属性系统相似.然而,作为平台和编译器无关的库,Qt不能够依赖于那些非标准的编译器特性,比如__property 或者 [property].Qt的解决方案 ...
- Java中系统属性Properties介绍 System.getProperty()参数大全
在JDK文档中System类中有这样的方法getProperties()在此方法的详细介绍中有下面的参数可供使用: java.version Java 运行时环境版本 java.vendor J ...
- Qt之属性系统
简述 Qt提供一个类似于其它编译器供应商提供的复杂属性系统(Property System).然而,作为一个编译器和平台无关的库,Qt不能够依赖于那些非标准的编译器特性,比如:__property或者 ...
- Qt属性系统
The Property System Qt提供一个类似于其他编译器供应商提供的精致的属性系统.然而,作为一个编译器和平台独立的库,Qt并不依赖于非标准编译器特性,如__property 或 [pro ...
- Qt 元对象系统(Meta-Object System)
(转自:http://blog.csdn.net/aladdina/article/details/5496891) Qt的元对象系统基于如下三件事情: 类:QObject,为所有需要利用原对象系统的 ...
- Qt 元对象系统(Meta-Object System)(不管是否使用信号槽,都推荐使用)
Qt 元对象系统(Meta-Object System) Qt的元对象系统基于如下三件事情: 类:QObject,为所有需要利用原对象系统的对象提供了一个基类. 宏:Q_OBJECT,通常可以声明在类 ...
随机推荐
- 让5G技术“智慧”生活
1.通讯技术的发展历程 2.5G技术的指标和具体概述 3. 5G的三个关键技术及概述 4.5G的应用场景及业务及安全挑战 如果你认为5G带来的只是下载视频 ...
- JAVA中自增自减运算符(i++与++i的区别)
注意: 自增运算符和自减运算符只能用于变量,而不能用于常亮或表达式 运算符 运算 范例 结果 ++ 自增(前):先运算后取值 a=2;b=++a; a=3;b=3; ++ 自增(后):先取值后运算 a ...
- Day1 Markdown学习!
Markdown学习 标题 一级标题:# (空格)+内容 二级标题:##(空格)+内容 同理可支持到六级标题 字体 Hello,World! 两边两个** 加粗 Hello,World! 两边一个* ...
- ZooKeeper 分布式锁 Curator 源码 04:分布式信号量和互斥锁
前言 分布式信号量,之前在 Redisson 中也介绍过,Redisson 的信号量是将计数维护在 Redis 中的,那现在来看一下 Curator 是如何基于 ZooKeeper 实现信号量的. 使 ...
- maven手动添加库文件
项目应用到了ojdbc,dubbo等私有库,maven无法直接下载,需要手动下载后添加到maven本地库里面. 以下以ojdbc为例: 1.下载jar后,cmd添加到本地库: mvn install: ...
- PHP如何接收json数据
以前一直在写一些网站,很少涉及到接口的东西.最近公司在做一个平台,需要往接口上发送json数据.闲话少叙,直接上干货. 在php中可以通过如下方式获取: file_get_contents(" ...
- P6753 [BalticOI 2013 Day1] Ball Machine
P6753 [BalticOI 2013 Day1] Ball Machine 题意 给你一个树,每次从根节点放一个求,如果其子节点有空这个球会向下滚,若有多个节点为空则找儿子中以子树内编号的最小值为 ...
- transform和tolower
transform:<algorithm> tolower:<ctype.h> transform有两种使用方法 第一种(参数): 源目标起始迭代器地址 源目标结束迭代器地址 ...
- 【贪心】数列分段Section I luogu-1181
题目描述 对于给定的一个长度为\(N\)的正整数数列\(A_i\),现要将其分成连续的若干段,并且每段和不超过\(M\)(可以等于\(M\)),问最少能将其分成多少段使得满足要求. 分析 简单思考一下 ...
- 自动化测试(1)selenium+python+chrome 连接测试
环境准备: python版本:3.8.4 开发工具:pycharm 使用chrome和对应的webdriver http://npm.taobao.org/mirrors/chromedriver/ ...