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)的更多相关文章

  1. Android 属性系统 Property service 设定分析 (转载)

    转自:http://blog.csdn.net/andyhuabing/article/details/7381879 Android 属性系统 Property service 设定分析 在Wind ...

  2. IOS中的属性列表----Property List

    属性列表,是一种用来存储串行化后的对象的文件.因为扩展名为plist ,因此通常被称为 plist文件. plist文件通常用于储存用户设置,也可以用于存储捆绑的信息,其内容为xml格式.它可以在程序 ...

  3. C#中的自定义控件中的属性、事件及一些相关特性的总结(转)

      摘要: C#中的自定义控件中的属性(Property).事件(Event)及一些相关特性(Attribute)的总结 今天学习了下C#用户控件开发添加自定义属性的事件,主要参考了MSDN,总结并实 ...

  4. Qt属性系统(Qt Property System)

    Qt提供了巧妙的属性系统,它与某些编译器支持的属性系统相似.然而,作为平台和编译器无关的库,Qt不能够依赖于那些非标准的编译器特性,比如__property 或者 [property].Qt的解决方案 ...

  5. Java中系统属性Properties介绍 System.getProperty()参数大全

       在JDK文档中System类中有这样的方法getProperties()在此方法的详细介绍中有下面的参数可供使用: java.version Java 运行时环境版本 java.vendor J ...

  6. Qt之属性系统

    简述 Qt提供一个类似于其它编译器供应商提供的复杂属性系统(Property System).然而,作为一个编译器和平台无关的库,Qt不能够依赖于那些非标准的编译器特性,比如:__property或者 ...

  7. Qt属性系统

    The Property System Qt提供一个类似于其他编译器供应商提供的精致的属性系统.然而,作为一个编译器和平台独立的库,Qt并不依赖于非标准编译器特性,如__property 或 [pro ...

  8. Qt 元对象系统(Meta-Object System)

    (转自:http://blog.csdn.net/aladdina/article/details/5496891) Qt的元对象系统基于如下三件事情: 类:QObject,为所有需要利用原对象系统的 ...

  9. Qt 元对象系统(Meta-Object System)(不管是否使用信号槽,都推荐使用)

    Qt 元对象系统(Meta-Object System) Qt的元对象系统基于如下三件事情: 类:QObject,为所有需要利用原对象系统的对象提供了一个基类. 宏:Q_OBJECT,通常可以声明在类 ...

随机推荐

  1. 如何使用Meter-WebSocketSampler

    安装 JMeter-WebSocketSampler 下载最新的 JMeter-WebSocketSampler,如 JMeterWebSocketSamplers-1.2.1.jar. 下载地址:h ...

  2. 小刻也能看懂的Unraid系统使用手册:基础篇

    小刻也能看懂的Unraid系统使用手册 基础篇 Unraid系统简介 Unraid 的本体其实是 Linux,它主要安装在 NAS 和 All in One 服务器上,经常可以在 Linus 的视频里 ...

  3. chown、chgrp 改变所有者、所属组

    chown [option] [所有者][:[所属组]] file... chown指定文件的拥有者或者所属组,可以通过用户名或者用户id.组名.组id来修改,同时可以修改多个文件,文件以空格分割,支 ...

  4. 一、Java预科学习

    1.1.什么是计算机 计算机(computer)俗称电脑,是现代一种用于高速计算的电子计算机器,可以进行数值计算,又可以进行逻辑计算,还具有存储记忆功能.是能够按照程序运行,自动.高速处理海量数据的现 ...

  5. IO编程之NIO

    从jdk1.4开始,java提供了一系列改进的输入/输出处理的新功能,这些功能被统称为新IO(New IO,简称NIO),这些类都被放在java.nio包以及子包中,并且对原java.io包中的很多类 ...

  6. 前端开发入门到进阶第三集【js进行url解析】

    https://www.cnblogs.com/yuanzhiguo/p/8241644.html

  7. 深入学习Netty(5)——Netty是如何解决TCP粘包/拆包问题的?

    前言 学习Netty避免不了要去了解TCP粘包/拆包问题,熟悉各个编解码器是如何解决TCP粘包/拆包问题的,同时需要知道TCP粘包/拆包问题是怎么产生的. 在此博文前,可以先学习了解前几篇博文: 深入 ...

  8. 医疗器械软件产品经理必读的法规及标准-YY/T0664(二)

    上节主要讲了软件开发策划.软件需求分析.软件系统结构设计三个阶段,这节来分析以下几个阶段. 1.软件单元实现 2.软件集成和集成测试 3.软件系统测试 软件开发过程由若干个活动组成,主要包括软件开发策 ...

  9. HTML5 socket

    client: <!DOCTYPE html> <html> <head> <title></title> <meta http-eq ...

  10. Vue单点登录控件代码分享

    这里提供一个Vue单点登录的demo给大家参考,希望对想了解的朋友有一些帮助. 具体的原理大家可以查看我的上篇文章 vue实现单点登录的N种方式 废话不多少直接上代码 这里分两套系统,一是登录系统的主 ...