个人发现一篇关于在Qt中使用元对象系统支持自定义类型的好博文,记录下防止丢失(如有侵权,烦请告知删除)。博文原地址:http://qtdebug.com/qtbook-misc-qvariant/

QVariant 非常重要,可以存储很多种不同的类型,例如 int, QString, QRect, QPoint 等,其构造函数有很多个,参数是很多种不同的常用类型,还内置了可以直接转换 QVaraint 到某些类型的函数,如 toInt(), toString(), toPoint(), toSize() 等,还是 QObject 动态 property 机制的关键,除了支持内置的类型外,QVariant 还被设计成可以存储我们自己定义的类型。

关键术语:

  • Q_DECLARE_METATYPE
  • qRegisterMetaType
  • qRegisterMetaTypeStreamOperators
  • operator QVariant()

自定义类型和 QVariant 互相转换

想要能够使得自定义类型的对象和 QVariant 能够互相转换,自定义类型需要在类声明的头文件中使用宏 Q_DECLARE_METATYPE() 声明一下,告知 Qt 的 Meta System:

  • 使用 QVariant::fromValue(customClassObject) 把自定义类型的对象转换为 QVariant 对象
  • 使用 variantObject.value<CustomClass>() 把 QVariant 对象转换为自定义类型的对象

Adding a Q_DECLARE_METATYPE() makes the type known to all template based functions, including QVariant.

下面使用自定义类 User 和 QVaraint 互转为例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 文件名: User.h
#ifndef USER_H
#define USER_H

#include <QString>
#include <QVariant>
#include <QMetaType>

class User {
public:
User(int id = 50);

int id;
QString username;
QString password;
};

Q_DECLARE_METATYPE(User) // [1] 向 Qt Meta System 声明

#endif // USER_H
1
2
3
4
5
// 文件名: User.cpp
#include "User.h"

User::User(int id) : id(id) {
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 文件名: main.cpp
#include <QDebug>
#include <QVariant>

#include "User.h"

int main(int argc, char *argv[]) {
Q_UNUSED(argc)
Q_UNUSED(argv)

// [2] 对象转换为 QVariant
User ali(33);
QVariant var = QVariant::fromValue(ali);
// 只有使用 Q_DECLARE_METATYPE 声明过的类 canConvert 才返回 true
qDebug() << var.canConvert<User>(); // 输出 true

// [3] QVarint 转换为对象
User alex = var.value<User>();
qDebug() << alex.id; // 输出 33

return 0;
}

信号槽中使用自定义类型

如果 connect 的类型是 Qt::DirectConnection,也就是同一个线程中使用,那么不需要做什么,自定义类型的对象可以直接在信号槽中作为参数,但是如果 connect 的类型是 Qt::QueuedConnection,自定义类型除了使用宏 Q_DECLARE_METATYPE() 声明外,还必须调用 qRegisterMetaType 注册后才可以:

1
qRegisterMetaType<User>(); // 注意: qRegisterMetaType 是函数,不是宏

Adding a Q_DECLARE_METATYPE() makes the type known to all template based functions, including QVariant. Note that if you intend to use the type in queued signal and slot connections or in QObject’s property system, you also have to call qRegisterMetaType() since the names are resolved at runtime.

To use the type T in QVariant, using Q_DECLARE_METATYPE() is sufficient. To use the type T in queued signal and slot connections, qRegisterMetaType() must be called before the first connection is established.

Also, to use type T with the QObject::property() API, qRegisterMetaType() must be called before it is used, typically in the constructor of the class that uses T, or in the main() function.

QObject property 中使用自定义类型

*QObject::setProperty(const char name, const QVariant &value) 可以动态地存储数据,这样我们就不需要为了保存数据而定义很多变量了,使用 *QObject::property(const char name) 就能够取得存储的数据,是不是非常方便!

为了让 QObject 的 property 支持自定义类型的对象,自定义的类需要实现运算符 **QVariant()**,如下 User 的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 文件名: User.h
#ifndef USER_H
#define USER_H

#include <QString>
#include <QVariant>
#include <QMetaType>

class User {
public:
User(int id = 50);
operator QVariant() const; // [1] 为了支持 QObject 的 property 特性

int id;
QString username;
QString password;
};

Q_DECLARE_METATYPE(User)

#endif // USER_H
1
2
3
4
5
6
7
8
9
// 文件名: User.cpp
#include "User.h"

User::User(int id) : id(id) {
}

User::operator QVariant() const {
return QVariant::fromValue(*this);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 文件名: main.cpp
#include <QDebug>
#include <QVariant>

#include "User.h"

int main(int argc, char *argv[]) {
Q_UNUSED(argc)
Q_UNUSED(argv)

User alex(33);
QObject obj;
obj.setProperty("user", alex); // [2] 存储自定义类型的对象
User ronnie = obj.property("user").value<User>(); // [3] 取得自定义类型的对象
qDebug() << ronnie.id; // 输出 33

return 0;
}

QDataStream 序列化自定义类型的 QVariant

自定义类型的对象转换为 QVaraint 对象后,如果要使用 QDataStream 操作这个 varaint 的话,需要:

  • 自定义类型实现 QDataStream& operator<<(QDataStream &stream, const CustomClass &obj)
  • 自定义类型实现 QDataStream& operator>>(QDataStream &stream, CustomClass &obj)
  • 使用 qRegisterMetaTypeStreamOperators 进行注册

任然以 User 为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 文件名: User.h
#ifndef USER_H
#define USER_H

#include <QString>
#include <QVariant>
#include <QMetaType>
#include <QDataStream>

class User {
public:
User(int id = 50, const QString &username = QString(), const QString &password = QString());

friend QDataStream& operator<<(QDataStream &stream, const User &user);
friend QDataStream& operator>>(QDataStream &stream, User &user);

int id;
QString username;
QString password;
};

Q_DECLARE_METATYPE(User)

#endif // USER_H
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 文件名: User.cpp
#include "User.h"

User::User(int id, const QString &username, const QString &password)
: id(id), username(username), password(password) {
}

QDataStream& operator<<(QDataStream &stream, const User &user) {
stream << user.id << user.username << user.password;
return stream;
}

QDataStream& operator>>(QDataStream &stream, User &user) {
stream >> user.id >> user.username >> user.password;
return stream;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// 文件名: main.cpp
#include <QDebug>
#include <QVariant>
#include <QByteArray>
#include <QDataStream>

#include "User.h"

int main(int argc, char *argv[]) {
Q_UNUSED(argc)
Q_UNUSED(argv)

// [1] QDataStream 操作自定义类型的 QVariant 时需要先注册
qRegisterMetaTypeStreamOperators<User>();

// [2] 把对象转换为 QVariant
QVariant aliVar = QVariant::fromValue(User(100, "Ali", "Secret"));
QByteArray buffer; // 存储的载体

// [3] 把 User 的 QVariant 写入 buffer
QDataStream out(&buffer, QIODevice::WriteOnly);
out << aliVar;

// [4] 从 buffer 中读取对象
QDataStream in(&buffer, QIODevice::ReadOnly);
QVariant readedAliVar;
in >> readedAliVar;
User readedAli = readedAliVar.value<User>();

// 输出: "ID: 100, Username: Ali, Password: Secret"
qDebug() << QString("ID: %1, Username: %2, Password: %3")
.arg(readedAli.id).arg(readedAli.username).arg(readedAli.password);

return 0;
}

自定义类型与Qt元对象系统的更多相关文章

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

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

  2. 解析Qt元对象系统(五) Q_INVOKABLE与invokeMethod(automatic connection从Qt4.8开始的解释已经与之前不同,发送对象驻足于哪一个线程并不重要,起到决定作用的是接收者对象所驻足的线程以及发射信号(该信号与接受者连接)的线程是不是在同一个线程)good

    概述查看Qt源码可知,Q_INVOKABLE是个空宏,目的在于让moc识别. 使用Q_INVOKABLE来修饰成员函数,目的在于被修饰的成员函数能够被元对象系统所唤起. Q_INVOKABLE与QMe ...

  3. Qt元对象系统简介

    在Qt中提供了c++的扩展,提供了一种元对象系统的机制,(meta-object-system)的机制.其中包含了信号与槽的内部机制,能够访问到QObject子类的元对象信息的功能. Q_OBJECT ...

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

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

  5. 解析Qt元对象系统(四) 属性系统(确实比较方便)

    官方解释 我们在Qt源码中可以看到一个QObject的子类经常会用到一些Q_开头的宏,例如QMainWindow类开始部分代码是这样的: Q_PROPERTY(QSize iconSize READ ...

  6. qt 元对象系统

    元对象系统 Qt中的元对象系统是用来处理对象间通讯的信号/槽机制.运行时的类型信息和 动态属性系统. 它基于下列三类: QObject类: 类声明中的私有段中的Q_OBJECT宏: 元对象编译器(mo ...

  7. Qt笔记——元对象系统

    Qt元对象系统提供了对象间的通信机制:信号和槽.以及执行类形信息和动态属性系统的支持.是标注C++的一个扩展,它使得Qt可以更好的实现GUI图形用户界面编程.Qt的元对象系统不支持C++模板.虽然模板 ...

  8. 深入了解Qt(二)之元对象系统(Meta-Object System)

    深入了解Qt主要内容来源于Inside Qt系列,本文做了部分删改,以便于理解.在此向原作者表示感谢! 在Qt Meta Object System-元对象系统这篇文章中,从底层实现的源码剖析了元对象 ...

  9. Qt元对象(Meta-Object)系统与反射

    反射 -在计算机科学中,反射是指计算机程序在运行时(Run time)可以访问.检测和修改它本身状态或行为的一种能力.[1]用比喻来说,反射就是程序在运行的时候能够“观察”并且修改自己的行为. 要注意 ...

  10. Qt对象模型之二:对象树与元对象系统

    一.对象树的概念 Qt中使用对象树(object tree)来组织和管理所有的QObject类及其子类的对象.当创建一个QObject时,如果使用了其他的对象作为其父对象(parent),那么这个 Q ...

随机推荐

  1. VIT论文笔记

    VIT An image is worth 16x16 words: transformers for image recognition at scale 将transformer首次应用在视觉任务 ...

  2. java运算符相关学习

    java运算符 面试题1: 计算2*8如何操作效率更高? 剖析: 2 * 8 => 实际上是2 * 2 * 2 * 2 2<<3 System.out.println(2<&l ...

  3. 有关谷歌下载PDF文件而不是直接打开预览的问题

    找了很多帖子,都不行, 解决方法把链接处理成blob文件再调用下载. 但是仍然不行,最终原因找到: 缺少了这一部创建,需要把二进制数据重新用 Blob对象申明下 let data = new Blob ...

  4. jmeterGUI&非GUI模式之如何减负性能调优

    jmeter之如何减负-实现稳定超高并发测试(性能调优)在测试过程中,初学者使用工具不当,添加众多监控组件,非常想看到实时报告,跑不了一会,jmeter就卡死甚至内存耗尽,只得重启,之前的统计报告没了 ...

  5. 沁恒蓝牙系列芯片USB烧录故障排查

    目录 使用USB烧录时让ISP工具能够识别芯片的操作是按住评估板上的"Download"按键,或者将PB22短接到GND,同时给板子上电.若是第一次拿到芯片,codeflash中是 ...

  6. DP5340:国产兼容替代CS5340立体声音频A/D转换器芯片

    DP5340简介 DP5340 是一款完整的采样.模数音频信号转换. 抗混叠滤波的芯片,在串行格式下以每声道最高 200kHz 采样率高达 24 位宽,并支持大部分的音频 数据格式. DP5340 基 ...

  7. 课程表及事件提醒app-界面原型设计

    前端设计: 暂定为8个主要界面:程序初始界面.主界面(首页).课表界面."我的"界面.登录界面.注册界面.创建事件界面.新建课表界面 设计思路: 项目结构 三个主要界面 首页: 课 ...

  8. JAVA查漏补缺 1

    JAVA查漏补缺 1 目录 JAVA查漏补缺 1 基本数据类型 数组 方法参数传递机制 基本数据类型 数据类型 关键字 取值范围 内存占用(字节数) 整型 byte -128~127 1 整型 sho ...

  9. 简谈CPU峰值性能怎么计算[转载]

    CPU峰值性能就是CPU运算能力满打满算最最理想情况下的性能,这只有理论意义,实际性能要以软件实测为准.有人问寡人峰值性能怎么算,这里就很简单地说两句.搞计算化学的一般只关注浮点性能,所以这里只提峰值 ...

  10. Jquery 简单实现demo

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...