个人发现一篇关于在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. leecode70. 爬楼梯

    70. 爬楼梯 假设你正在爬楼梯.需要 n 阶你才能到达楼顶. 每次你可以爬 1 或 2 个台阶.你有多少种不同的方法可以爬到楼顶呢? 示例 1: 输入:n = 2 输出:2 解释:有两种方法可以爬到 ...

  2. PK获取面积

    BOOL PK_AskFaceAreas(tag_t face_tag, double tol, double &areas) {//获得面积 tag_t ps_tag = NULL_TAG; ...

  3. C++ CLI string

    String^ to std::string ` include <msclr\marshal_cppstd.h> marshal_as(String^) `

  4. 负载均衡集群ipvsadm常见参数以及基本用法

    ---- 固定调度 rr:论调 wrr:加权论调 weight,加权 sh:source hash ,源地址hash 动态调度lc:最少链接 active*256+inactivewlc:加权最少链接 ...

  5. Java语言中的复合运算符会自动进行类型转换

    计算1/1-1/2+1/3+--+1/99-1/100 public class LoopControlExercise08{ public static void main(String[] arg ...

  6. Linux下aria2详细配置,以及接管浏览器下载项

    本篇是在Deepin的环境下操作,大概可适用与Ubuntu和Debian,其他系的Linux不确定, 首先,我们安装aria2,使用命令行(在桌面右键可以打开)输入sudo apt install a ...

  7. sleep(0)的意义

    Thread.Sleep(0) 并非是真的要线程挂起0毫秒,意义在于这次调用Thread.Sleep(0)的当前线程确实的被冻结了一下,让其他线程有机会优先执行. Thread.Sleep(0) 是你 ...

  8. (原创)odoo中字段默认值的获取顺序

    odoo中某个字段的默认值的取值顺序 1.搜索当前记录集(recordset)中的context中是否存在"default_字段名"的键,如果存在则取值 2.搜索模型(ir.def ...

  9. maven jar包新版本检测工具推荐

    为什么需要 经常使用maven来构建项目的朋友,应该遇到过类似这样的情况:项目通过maven引入了很多jar包,随着时间推移,这些jar包都有了更优的新版本出来,想升级,但又觉得很繁琐.主要是因为两方 ...

  10. MyBatis_10(分页插件)

    主题:分页插件 --> 针对:查询功能 一.分页插件使用步骤: 1-添加依赖 <!-- https://mvnrepository.com/artifact/com.github.page ...