QVariant的使用

2023-10-30

在有些情况下,我们希望把数据存储在一个变量中。例如,我有一个数组,既希望存整数,又希望存浮点数,还希望存string。

想了一个方法,创建一个object类,这是一个很大的类,里面几乎包含了所有类型的数据,如下:

class Object  

{  

public:  

    int intValue;  

    float floatValue;  

    string stringValue;  

   ......

}; 

那这个类足以存储int,float,string了,这就类似于QVariant的思路。

Qt提供的QVariant类型,可以把这很多类型都存放进去,到需要使用的时候使用一系列的to函数取出来即可。比如你把int包装成一个QVariant,使用的时候要用QVariant::toInt()重新取出来。需要注意QVariant类型的放入和取出必须是相对应的,也就是你放入一个int就必须按int取出,不能用toString(),Qt不会帮你自动转换。

说白了,存储数据的核心无非就是一个 union,和一个标记类型的type,即假设传递的是整数123,那么它union存储整数123,同时type标志为int;如果传递字符串,union存储字符串的指针,同时type标志QString。

QVariant可以保存很多Qt的数据类型,包括QBrush、QColor、QCursor、QDateTime、QFont、QKeySequence、QPalette、QPen、QPixmap、QPoint、QRect、QRegion、QSize和QString,并且还有C++基本类型,如int、float等。QVariant还能保存很多集合类型,如QMap<QString, QVariant>, QStringList和QList<QVariant>。item view classes,数据库模块和QSettings都大量使用了QVariant类,,以方便我们读写数据。

基本用法:

(1)构造

QVariant v(1);
QVariant v1;
v1.setValue("boy");

(2)使用

if (v.canConvert<int>())
{
    bool ok;
    int i = v.toInt(&ok); 
    //int i = v.value<int>(); 
}

自定义存储QVariant类型

存储自定义类型需要在类声明后加上Q_DECLARE_METATYPE(),如下:

struct MyClass
{
    QString name;
    int age;
}
Q_DECLARE_METATYPE(MyClass)

上面这个类就可以像QMetaType::Type类一样使用,没多少区别,有一点区别就是,上面的类的使用只能如下使用:

MyClass myClass;
QVariant v3 = QVairant::fromValue(myClass);
//
v2.canConvert<MyClass>();
MyClass myClass2 = v2.value<MyClass>();

QVariant被用于构建Qt Meta-Object,因此是QtCore的一部分。当然,我们也可以在GUI模块中使用,例如:

QIcon icon("open.png");  

QVariant variant = icon;  

// other function  

QIcon icon = variant.value<QIcon>(); 

我们使用了value<T>()模版函数,获取存储在QVariant中的数据。这种函数在非GUI数据中同样适用,但是,在非GUI模块中,我们通常使用toInt()这样的一系列to...()函数,如toString()等。

如果你觉得QVariant提供的存储数据类型太少,也可以自定义QVariant的存储类型。被QVariant存储的数据类型需要有一个默认的构造函数和一个拷贝构造函数。为了实现这个功能,首先必须使用Q_DECLARE_METATYPE()宏。通常会将这个宏放在类的声明所在头文件的下面:

Q_DECLARE_METATYPE(BusinessCard) 

然后就可以这样使用:

BusinessCard businessCard;  

QVariant variant = QVariant::fromValue(businessCard);  

// ...  

if (variant.canConvert<BusinessCard>())

 {  

     BusinessCard card = variant.value<BusinessCard>();  

    // ...  

}

VC 6的编译器有所限制,所以不能使用这些模板函数,如果使用这个编译器,需要使用qVariantFromValue(), qVariantValue<T>()和qVariantCanConvert<T>()这三个宏 。

应用场景1:
Qt一些类可以附带额外数据, 最常用的有QTreeWidgetItem.setData(int column, int role, QVariant &value).

有时经常需要在不同列上或者不同的role上增加一个额外的数据,
当额外数据多一点不会显示杂乱且有时不知存放在哪一列上.这样我们可以定义一个类将所有额外数据放在类中,只需将数据放在一个地方,不仅数据统一还好扩展:
item.setData(0, Qt::UserRole+1, QVariant::fromValue(myClass));

应用场景2
对于QMap<QString, QVariant>结构, 可以很清晰的实现全局配置存储, QString作为键, 而QVariant可存储配置值。

应用场景3
使用Qt的postEvent()
VxCommonLib的IMsgObserver中有封装了一个PostMsg, 可是它有个不友好的地方:每次需要PostMsg一个新的数据时必须做两件事

(1)定义一个转换函数
(2)定义一个转换的QEvent类.

一个简单的例子如下:

// 一个QEvent类
class CAudioDataEvent:public QEvent
{
public:
    CAudioDataEvent(QEvent::Type type);
    AUDIOUVMETER m_audioData;
};

// 转换函数
static QEvent *_createAudioDataEvent(void *theParam,int type)
{
    CAudioDataEvent *pAudioEvent = new CAudioDataEvent(QEvent::Type(type));
    memcpy(&pAudioEvent->m_audioData, theParam, sizeof(AUDIOUVMETER));
   return pAudioEvent;
}

//事件发出
void CVxPlayViewController_X::__audiovu(AUDIOUVMETER* theParam)
{
    IMsgObserver *pMsg = GetIMsgObserver();
    pMsg->PostMsg(m_pParent, _createAudioDataEvent, TURBO_EDIT_PLAYVIEW_AUDIODATA,theParam ,m_pParent);
}

//事件接收
CAudioDataEvent *vxDataEvent = static_cast<CAudioDataEvent *>(event);
AUDIOUVMETER audioData = vxDataEvent->m_audioData;

出现以上问题主要原因是找不到一个可以储存任意类型的结构导致每次都要重定义事件, 没错你想到了,根据上面的知识找到了解决方案.
我们先定义好数据,改进如下:

//事件类
class  CDsVariantDataEvent : public QEvent
{
public:
    CDsVariantDataEvent(Type type, QVariant variant);

    QEvent(type)
    {
        m_variant = variant;
    }

    inline QVariant data() const { return m_variant; }

protected:
    QVariant m_variant;
};

// 发送类
 void CDsMsgEngine::PostMsg(int eventType, QVariant variant/* = QVariant()*/)
 {
    QObject *anObject = NULL;
    MSGMAPITER it = m_msgMapTable.find(eventType);
    if(it != m_msgMapTable.end())
    {
        ObserverList *pObserverList = (*it).second;
        for(int i = 0; i < pObserverList->size(); ++i)
       {
             //csj 2010-7-3
            if((*pObserverList)[i].anObject==NULL ||  (*pObserverList)[i].observer==anObject)
            {
                   CDsVariantDataEvent *pEvent = new CDsVariantDataEvent(static_cast<QEvent::Type>(eventType),variant);
                   QApplication::postEvent((*pObserverList)[i].observer,pEvent);
             }
       }
    }
}

这样以后每次使用异步事件就可以:

//发送
GetIMsgObserver()->PostMsg(TURBO_EDIT_PLAYVIEW_AUDIODATA,QVariant::fromValue(MyClass)));
//接收
CDsVariantDataEvent *pEvent = static_cast<CDsVariantDataEvent *>(event);
MyClass myClass = pEvent->data().value<MyClass>();

应用场景4
自定义类型要能在队列信号槽中使用,使用Q_DECLARE_METATYPE()后还要使用qRegisterMetaType()注册成QMetaType类型, 使用队列信号槽其实是很强大的,但我们常会使用其它的方式解决问题。

3、序列化使用
要能序列化自定义类,还需要qRegisterMetaTypeStreamOperators()注册,还需要实现两个函数:

QDataStream &operator<<(QDataStream &out, const MyClass &myObj);
QDataStream &operator>>(QDataStream &in, MyClass &myObj);

这样就可以将数据序列化, 到底这样有什么用呢。。。还未搞懂

正如上面实现的全局配置QMap<QString, QVariant>, 你是否想保存成配置文件等下次程序启动的时候再回复呢, 是的可以而且和你想的一样简单.
(1)保存配置

QFile file("file.dat");
file.open(QIODevice::WriteOnly);
QDataStream out(&file);   // we will serialize the data into the file
out << map; 

(2)读取配置

QFile file("file.dat");
file.open(QIODevice::ReadOnly);
QDataStream in(&file);    // read the data serialized from the file
in >> map;  

唯一需要注意的是所有的qRegisterMetaTypeStreamOperators()都要在读取配置文件之前, 否则会因为无法识别自定义类取序列化失败.
需要说明的是当你保存的配置是QFont,QRect,QKeySequence, QList<>等你才是知道他的强大之处, 可惜的是我们现在的程序往往用不着.

应用场景2
只要是继承QIODevice都可以序列化写和读, 如果我告诉你QAbstractSocket, QLocalSocket, QNetworkReply, QProcess都是继承QIODevice,
那么你是否会想到, 应用场景1中的功能都可以应用到网络数据交换和进程间通信. 实现过程其它和场景1一样,只不过是将QFile换成你想要
的类而已, 可惜的是我们也少用到.

4.QVariant源码实现
内部使用一个union管理所有类型, 对于复杂的类主要使用o, prt, shared管理. shared中存储的会使用隐式共享, 其它会进行深拷贝.
(1)内部存储结构

union Data
{
    char c;
    int i;
    uint u;
    bool b;
    double d;
    float f;
    qreal real;
    qlonglong ll;
    qulonglong ull;
    QObject *o;
    void *ptr;
    PrivateShared *shared;
} data;

当是几个常用基本类型, 刚直接将数据存储在char, int, bool等等上面, 继续QObject类将会放到o上,其它Qt内置类型,则存储在shared上, 而用户自定义的数据存储在ptr上.在构造QVariant
时有个大的switch负责存储责任.

static void construct(QVariant::Private *x, const void *copy)
{
    x->is_shared = false;
    switch (x->type)

{
    case QVariant::String:
            v_construct<QString>(x, copy);
            break;
    case QVariant::Char:
            v_construct<QChar>(x, copy);
            break;
    case QVariant::StringList:
           v_construct<QStringList>(x, copy);
           break;
    case QVariant::Map:
           v_construct<QVariantMap>(x, copy);
           break;
    case QVariant::Hash:
            v_construct<QVariantHash>(x, copy);
            break;
    case QVariant::List:
            v_construct<QVariantList>(x, copy);
            break;
    case QVariant::Date:
            v_construct<QDate>(x, copy);
            break;
    case QVariant::Time:
            v_construct<QTime>(x, copy);
            break;
    case QVariant::DateTime:
            v_construct<QDateTime>(x, copy);
            break;
    case QVariant::ByteArray:
            v_construct<QByteArray>(x, copy);
            break;
    case QVariant::BitArray:
           v_construct<QBitArray>(x, copy);
           break;
#ifndef QT_NO_GEOM_VARIANT
    case QVariant::Size:
           v_construct<QSize>(x, copy);
           break;
    case QVariant::SizeF:
           v_construct<QSizeF>(x, copy);
           break;
    case QVariant::Rect:
           v_construct<QRect>(x, copy);
           break;
    case QVariant::LineF:
           v_construct<QLineF>(x, copy);
           break;
    case QVariant::Line:
           v_construct<QLine>(x, copy);
           break;
    case QVariant::RectF:
           v_construct<QRectF>(x, copy);
           break;
    case QVariant::Point:
           v_construct<QPoint>(x, copy);
           break;
.............................. // 省略
    default:
        void *ptr = QMetaType::construct(x->type, copy);
        if (!ptr)

       {
              x->type = QVariant::Invalid;
        }

       else

      {
            x->is_shared = true;
            x->data.shared = new QVariant::PrivateShared(ptr);
       }
      break;
    }
    x->is_null = !copy;
}

//调用v.value()
inline T value() const
{ return qvariant_cast<T>(*this); }
//
template<typename T> inline T qvariant_cast(const QVariant &v)
{
    const int vid = qMetaTypeId<T>(static_cast<T *>(0));
    if (vid == v.userType())
        return *reinterpret_cast<const T *>(v.constData());  // 自定义数据使用reinterpret_cast<const T *>返回
    if (vid < int(QMetaType::User)) {
        T t;
        if (qvariant_cast_helper(v, QVariant::Type(vid), &t)) // Qt内置,使用一个模版返回相对应数据, switch太大就不放出来了.
            return t;
    }
    return T();
}

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

QVariant的使用 的相关文章

  • 新浪期货数据接口

    附注 对于期货数据 一直在寻找互联网上能够获得的比较好的数据源 最近发现了 新浪期货 的数据源 可以提供一分钟的期货K线数据 http hq sinajs cn list TICKER 对于国内期货连续 Ticker为正常Ticker加个0
  • Deep Residual Network

    先阅读者两篇写的比较好的博客 https blog csdn net dulingtingzi article details 79870486 https blog csdn net qq 31050167 article details
  • 高德地图绘制标记点,点击弹出弹框进入第三方地图软件

    需求 根据经纬度绘制标记点 点击标记点弹出弹框和底部按钮 点击顶部弹框进入二级界面 点击底部按钮弹出第三方地图软件选择页 实现跨进程跳转 效果图 项目是公司项目 只放出重要部分代码 final Marker marker1 aMap add
  • 计算机图形学入门(八)-着色(插值、高级纹理映射)

    目录 重心坐标 应用纹理 1 纹理分辨率很小 2 纹理分辨率过大 纹理的应用 颜色 环境贴图 Environment Map 法线贴图 凹凸贴图 位移贴图 三维纹理和三维噪声 着色信息的记录 学习视频来源 GAMES101 现代计算机图形学
  • 【操作系统】进程间通信的五种方式

    引言 1 进程对白 管道 记名管道 套接字 1 管道 2 虫洞 套接字 3 信号 4 信号旗语 信号量 5 进程拥抱 共享内存 引言 进程作为人类的发明 自然免不了脱离人类的习性 也有通信需求 如果进程之间不进行任何通信 那么进程所能完成的
  • 关于你STM32F407片内FLASH擦除失败的问题

    近日 发现STM32F407片内FLASH在写入数据时某些情况下会出现扇区擦除不成功的情况 使用正点原子的代码测试也出一样的情况 网上搜索也没找到较为理想的答案 只有一篇文章遇到同样问题 也是使用了一个不太可靠的办法 但是好在可以解决问题
  • DM8无图形界面安装与使用

    本机实验环境 centos7 9 参考文档 数据库安装 达梦云适配中心文档 dameng com https eco dameng com docs zh cn start install dm linux prepare html 1 系
  • “红山开源”创新论坛

    ChinaOSC 2022 红山开源 创新论坛将于2022年8月20日14 00 17 00在陕西省西安高新国际会议中心召开 红山开源 创新论坛重点聚焦战略科技领域相关需求 邀请知名院士和专家共同探讨战略科研任务开源众创组织模式和项目孵化方
  • 部署tomcat时出现No artifacts marked for deployment

    这种错误主要是因为没有设置导出包 解决方法 File gt Project Structure gt Artifacts 然后点击ok 然后记得apply ok 此时再回到问题所在 点击Fix即可 或者如下
  • JAVA中的正则表达式

    一 正则表达式的构成 1 字符类 abc a b c abc 任何字符 除了a b c a zA Z a到z或A到Z 两头的字母包含在内 范围 2 预定义字符类 任何字符 d 数字 0 9 D 非数字 0 9 s 空白字符 t n x0B
  • Python爬虫JS解密详解,学会直接破解80%的网站(二)!!!

    文章目录 前言 1 网页查看 2 JS解密过程 细心看哦 3 解密答案 完整代码 CSDN独家福利降临 Python爬虫JS解密详解 学会直接破解80 的网站 25个爬虫项目宝藏教程 你值得拥有 前言 Glidedsky这关的JS解密不同于
  • js rsa加密_Python实现RSA(jsencrypt)加密的两种方式

    RSA是一种常用加密算法 经常用在前端向后端传送密码的时候 一 通过运行rsa js文件加密 rsa js文件下载地址 https gitee com youchuanming rsa jsencrypt import urllib pur
  • 常见排序算法的js实现

    常见排序算法的js实现 冒泡排序 选择排序
  • git 笔记/常见命令/as的fetch,pull ,update project的区别/标签管理

    Git概念汇总 头 HEAD 头 HEAD HEAD类似一个 指针 指向当前活动 分支 的 最新版本 工作区 Workspace 就是在电脑里能看到的项目代码库目录 是我们搬砖的地方 在这里我们可以新增文件 修改文件内容 或删除文件 此时的
  • Java线程的5种状态及状态之间转换

    Java中的线程的生命周期大体可分为5种状态 1 新建 NEW 新创建了一个线程对象 2 可运行 RUNNABLE 线程对象创建后 其他线程 比如main线程 调用了该对象的start 方法 该状态的线程位于可运行线程池中 等待被线程调度选
  • 电子设计竞赛应该如何准备?

    全国大学生电子设计竞赛 当初我听到这个比赛的时候心中还有些胆怯 毕竟它含金量确实高 而且要想在全国去的好的名次 也确实不是一件容易的事情 但经过我大二一年的准备 我还是在自己的努力下 拿到了全国二等奖 现在都还清楚地记得但是得知我们的团队获
  • ChatGPT对高校人才培养模式的挑战与应对策略思考

    酷吗 输入指令后直接就能生成一大串代码 即使不懂相关技术也能玩转编程 这就是ChatGPT赋予你的 新能力 除了写代码 ChatGPT还能帮你执行各种五花八门的任务 AI工具如ChatGPT在行业中的广泛应用对于行业的人才结构和能力要求产生
  • 部署Vista – 第18部分:管理Windows部署服务

    原创作品 允许转载 转载时请务必以超链接形式标明文章 原始出处 作者信息和本声明 否则将追究法律责任 http iwantfly blog 51cto com 1048259 240872 介绍如何管理和配置Windows部署服务服务器 本
  • ms project 入门_Microsoft Project 2010入门

    ms project 入门 Would you like to keep your projects on track and keep track of how time and resources are used Let s take
  • React直接渲染从后台传过来的标签

    在工作中使用react 遇到需要渲染从后台获取到的标签语言 发现直接放在react中是不能解析标签语言的 解决办法如下 var content strong content strong 假设content是从接口获取到的数据 react

随机推荐

  • 大数据shell基础

    一 常用shell命令 1 管道命令 命令1 命令2 命令1的输入作为命令2的输入 2 抓取命令 grep命令 可以使用正则表达式来过滤 3 查找命令 find命令 选项参数 type name size perm 如果上面这个不行可以在
  • 蓝桥杯 java a组_2019年第十届蓝桥杯国赛总结(JavaA组)

    JavaA组国二 可以报销了 JA死亡之组可不是盖的 rank12的排名还是拿不到国一啊 只有五个 出成绩的一刻波澜不惊 毕竟去年有国一了不慌哈哈哈 不过对我来说这个结果还算意料之外吧 毕竟大三考研狗 这次再也不敢说蓝桥杯水了 十周年十道题
  • EMC的RS和CS和RI,CI一样吗是属于EMS 吗

    EMC Electromagnetic Compatibility 是指电子设备在电磁环境中能够正常工作 同时不对周围的其他设备和系统产生不可接受的电磁干扰的能力 在EMC设计中 RS Radiated Susceptibility 和CS
  • stm32电机驱动调试平台pid调试开发ros底盘里程计脉冲速度监测MPR

    MPRO 用于调试ros小车底盘 pid开发学学 电机驱动板反馈脉冲等数据的上位机工具 欢迎下载试用 MPRO搭载stm32单片机驱动GA370编码电机 实现PID速度调节 转向控制 实体图如下 采用杜邦线加模块设计 简单易用 可实现插拔
  • JLINK在ADS中的调试心得

    JLINK在ADS中的调试心得 分类 ARM 2010 01 03 19 39 138人阅读 评论 0 收藏 举报 分享 JLINK在ADS下调试心得 前两天一个客户用jlink在ADS来调试LPC2148总报错 这个错误我之前在调试LPC
  • 【论文精读】HumanNeRF

    目录 Abstract 1 Introduction 2 Related work Human specific rendering Neural radiance fields Human specific neural renderin
  • Java:抽象类和接口

    文章目录 抽象类 什么是抽象类 抽象类的特性 为什么会有抽象类这种东西 接口 什么是接口 接口的特性 匿名内部类 拓展 实现多个接口 接口间的继承 三个重要的接口 Comparable接口 Comparator接口 Clonable接口 浅
  • 关于 Cannot read property 'length' of null 报错的解决办法

    最近在搞前端的时候突然报了Cannot read property length of null的错 一开始都是在前端调试错 发现解决不了问题 后来发现如果你所查找的数据条数为0的时候 后端返回给前端是null 此时必然报错Cannot r
  • CityEngine三维建模几个常见问题解决方法(1)

    CityEngine被Esri收购以后 大踏步进入GIS三维建模领域 由于CityEngine独有的基于规则建模 使得GIS三维建模效率大增 不过不是规则就可以一刀切 解决所有问题的 有时我们还是要做这样或那样的一些处理才能顺利的使用规则达
  • Mysql8.0开启远程访问权限

    use mysql 登录后选择mysql数据库 select host user password from user 查看当前root对应host是否为 update user set host where user root 更新 se
  • 基于YOLOv5+Hough变换的目标检测和车道线检测

    这学期做的一个大作业 实现了对行驶过程中车辆 行人以及车道线的检测 1 B站视频演示 2 Github仓库链接 文章目录 一 实现效果 二 环境配置 三 基于YOLOv5的目标检测 四 基于Hough变换的车道线检测 4 1 前置工作 Ca
  • 鸿蒙3部曲先看哪部,“隋唐三部曲”“鸿蒙三部曲”“斗罗四部曲”谁才是网文巅峰之作...

    原标题 隋唐三部曲 鸿蒙三部曲 斗罗四部曲 谁才是网文巅峰之作 从网络小说诞生的那一刻起 续集就是一个绕不过去的话题 如同电视剧一样 一部网络小说红了之后 它的原作者很多时候会忍不住开发它的续集 形成一个系列 然后再现网文界 小编今天就给大
  • java开发转测试开发经历

    1 背景 我从毕业一直做java开发已经两年半了 到目前为止也挺喜欢开发的 2 为什么想转行 想转行是由多方面考虑的 一 我的开发技能没达标 只能找到外包里的开发工作 二 开发前景对女生不够友好 难以获得认可 个人感受 至于第一点其实也可以
  • 减少数据打拍翻转的低功耗设计方法

    在流水设计中 时常会遇到对某一路数据打多拍从而对齐另一路数据的场景 而除了最后一拍是真正需要的 中间的打拍从功耗上来看是有点浪费的 举个例子 对8bit in data打4拍 总共需要用到4个8bit寄存器 常规打拍方法传输4个数据 D0
  • android 屏幕适配--------解决方案

    以下是Demo首页的预览图 demo下载 http www eoeandroid com forum php mod attachment aid NjE0Njh8ZTIyZDA2M2N8MTMzODgyOTQxN3w1NzAwOTV8MT
  • Spring Boot实现QQ邮件发送,用户注册功能——前后端分离版

    1 准备工作 我们需要前往我们的QQ邮箱开启相关功能 登录QQ邮箱后 点击进入 设置 在账户在一栏中 我们可以找到这个界面 然后点击开启 POP3 SMTP服务 他们会让我们用QQ的密保手机发送一条短信 我们照着即可 验证成功之后 会获得一
  • ISP记1

    目录 0 参考 1 噪声分类 1 1 空间区域 1 1 1 高斯噪声 1 1 2 瑞利噪声 1 1 3 伽马噪声 1 1 4 均匀分布噪声 1 1 5 泊松噪声 1 1 6 加性噪声 乘性噪声 0 参考 数字图像滤波算法的研究及应用 倪层敏
  • anaconda+pytorch安装说明

    第一步 anconda的安装 大家可以参考以下博文 写的比较简约 看起来比较清爽 https blog csdn net ITLearnHall article details 81708148 Anacond的介绍 Anaconda指的是
  • chisel线网(wire)和寄存器(reg)详解(更新)

    主体内容摘自 https blog csdn net qq 34291505 article details 87714172 在Verilog里 模块内部主要有 线网 wire 和 四态变量 reg 两种硬件类型 它们用于描述数字电路的组
  • QVariant的使用

    在有些情况下 我们希望把数据存储在一个变量中 例如 我有一个数组 既希望存整数 又希望存浮点数 还希望存string 想了一个方法 创建一个object类 这是一个很大的类 里面几乎包含了所有类型的数据 如下 class Object pu