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. 求数组的子数组之和的最大值IV

    在之前的基础上又安排了二维数组的,在课上一开始是理解错要求了,简单的以为用循环数组就能解决,但是却忽视了子数组是否能构成矩形,之后课下和同学们讨论,主要是多重遍历,但是我还是没搞明白怎么构成新的二维数 ...

  2. mybatis-5-关联查询

    外键查询 1.回忆外键约束 注意要在tbl_dept中添加外键 #添加外键约束 # 此处Employee为外键表,dept为主键表 # 删除Employee的数据不会影响dapt,而删除dept一定会 ...

  3. go反射原理

    go反射原理 本文基于go1.13.15 1.go汇编 1.1 基本语法 go采用plan9的汇编器完成汇编,有下面几个重要的伪寄存器 FP: Frame pointer: 局部变量访问 PC: Pr ...

  4. Zookeeper简介(五)

    1.定义 Zookeeper:是一个分布式的,开发源码的分布式应用程序协调服务,是hadoop的子项. 2. 特点 具有高可靠性,可扩展性,分布式,可配置的协调机制. 3. 作用 3.1 数据发布与订 ...

  5. 常见的BI软件有哪些_BI工具软件哪个好用

    世界越来越以数据的驱动.数据分析是帮助企业深入了解自身业务表现(例如正在做什么或哪块业务需要注意和改进)的重要元素.为了获得更直观的展现,数据分析软件可帮助公司通过报告.数据可视化.应用程序等从数据中 ...

  6. Thinkphp 生成的验证码不显示问题解决

    在调用验证码之前加上   ob_clean(); 将: public function verify(){                $verify = new \Think\Verify();  ...

  7. 浅谈Blazor开发的那些事

    在这篇文章中,我们将解决一些常见的Blazor问题.具体来说就是"什么是Blazor",但更重要的是"为什么要用Blazor".既然我们已经有了Angular. ...

  8. C# 事件与继承

    在窗体编程过程中,常常会封装一个基类,包含未来业务中常用的属性.方法.委托.事件等,但是事件作为一个特殊的委托,只能在声明类中调用,派生类都不可以调用,所以在基类中必须实现一个虚函数,实现事件的调用, ...

  9. 涨姿势啦!Java程序员装X必备词汇之对象标记Mark Word!

    大家好,我是庆哥Java,一个专注于干货分享的Java自学者! 写在前面 如果你已经知道什么是Mark Word,那我也希望你都好好阅读下本篇文章,因为你有可能发现不一样的切入点来帮助你更加深入的了解 ...

  10. Upload-labs 文件上传靶场通关攻略(下)

    Upload-Labs靶场攻略(下) Pass-11 GET型传参,上传目录可设置,考虑00截断,在/upload/后添加1.php%00,即可上传 Pass-12 POST型传参,上传目录可设置,P ...