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. C++11标准特性的一些理解

    (1)auto 和 decltype 关键字 在C++11之前,auto关键字用来指定存储期(C++98中指的是自动生命周期).在新标准中,它的功能变为类型推断.C++11引入auto关键词与之前C语 ...

  2. 如何在cmd中运行.py文件

    C:\Users\mf>cd C:\Program Files\Python36\ C:\Program Files\Python36>python const.py 切换到.py文件所在 ...

  3. C# CheckedListBox控件的用法总结

    1. 添加项目 checkedListBox1.Items.Add("一级"); checkedListBox1.Items.Add("二级"); checke ...

  4. 光学动作捕捉系统中的反光标识点(Marker点)

    动作捕捉系统本质上是一种定位系统,通常需要在目标物布置定位设备进行追踪.以红外光学为原理的动作捕捉系统,主要由由光学镜头.动作捕捉软件.反光标识点.POE交换机.和若干配件组成,其中反光标识点(Mar ...

  5. node.js背后的引擎V8及优化技术

    本文将挖掘V8引擎在其它方面的代码优化,如何写出高性能的代码,及V8的性能诊断工具.V8是chrome背后的javascript引擎,因此本文的相关优化经验也适用于基于chrome浏览器的javasc ...

  6. JS文件延迟和异步加载:defer和async属性

    -般情况下,在文档的 <head> 标签中包含 JavaScript 脚本,或者导入的 JavaScript 文件.这意味着必须等到全部 JavaScript 代码都被加载.解析和执行完以 ...

  7. SpringBoot读取Resource下文件的几种方式(十五)

    需求:提供接口下载resources目录下的模板文件,(或者读取resources下的文件)给后续批量导入数据提供模板文件. 方式一:ClassPathResource //获取模板文件:注意此处需要 ...

  8. vue tab实现右定位

    呈现效果 利用v-if进行判断,登页面完全加载完毕后,显示tab页, 利用name标签,实现选择哪个tab <template> <el-tabs v-if="displa ...

  9. Geoserver通过ajax跨域访问服务数据的方法(含用户名密码认证的配置方式)

    Goeserver数据有两种,一种需进行用户密码的权限认证,一种无须用户密码.对于网上跨域访问Geoserver数据的种种方法,对这2种数据并非通用. 笔者将Geoserver官方下载的Geoserv ...

  10. python解析excel

    import xlrd, base64excel_obj = xlrd.open_workbook(file_contents=base64.decodestring(filename)).#打开要解 ...