个人发现一篇关于在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. eclipse调整控制台长度

  2. VUE学习-监听事件

    监听事件 事件处理方法可以用 v-on 指令监听 DOM 事件,并在触发时运行一些 JavaScript 代码. <div id="app"> <button v ...

  3. 浅谈zookeeper

    zookeeper用来解决高可用问题,具有高可用,高性能,具有严格的顺序(只要是分布式系统就会是一个严格的顺序)访问控制能力的分布式协调服务,做分布式协调的作用,可以做服务的同步,维护配置文件和命名服 ...

  4. js截取数组

    在JavaScript中,可以使用 slice() 方法来截取数组的一部分.该方法接受两个参数,第一个参数是截取的起始位置(包括该位置),第二个参数是截取的结束位置(不包括该位置). 例如,假设有一个 ...

  5. Hadoop2.7.3源码编译

    一.编译源码步骤演示详解 需求:官网下载的hadoop包,执行hadoop命令时,会有警告信息,为去除此警告,需要重新编译hadoop相应版本的源码,替换hadoop安装包lib目录下的native( ...

  6. CentOS7安裝 Nginx + php7 + php-fpm

    原文 : https://ivanagyro.medium.com/於centos7安裝-nginx-php7-php-fpm-laravel5-6-df8631681acf安装nginx 1/ 用y ...

  7. C# 实时显示时间

    c#实时显示时间 - vv彭 - 博客园 (cnblogs.com)

  8. Rabbit MQ的几种模式

    RabbitMQ是实现了高级消息队列协议(AMQP)的开源消息代理软件(亦称面向消息的中间件). 官网文档:https://www.rabbitmq.com/getstarted.html Rabbi ...

  9. ORA-28001 口令已经失效(密码过期)相关问题处理

    Oracle 提示错误消息 ORA-28001: the password has expired, 经调查是由于 Oracle 11G 的新特性所致, Oracle 11G 创建用户时缺省密码过期限 ...

  10. Docker CLI docker buildx build 常用命令

    Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中,然后发布到任何流行的 Linux或Windows操作系统的机器上,也可以实现虚拟化.Docker是内核 ...