百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

Qt QVariant的使用(使用qt二次开发周立功can)

zhezhongyun 2025-06-24 18:09 3 浏览

在有些情况下,我们希望把数据存储在一个变量中。例如,我有一个数组,既希望存整数,又希望存浮点数,还希望存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();
}

相关推荐

DevExpress使用教程:GridView经验小结

下面是笔者自己总结的使用DevExpressGridview的一些经验小结,分享给大家:1、去除GridView头上的"Dragacolumnheaderheretogroup...

ComponentOne 新版本发布,新增 .NET 6 和 Blazor 平台控件支持

ComponentOneEnterprise是葡萄城推出的一款内置300多种开发控件的.NET控件集,可满足WinForm、WPF、Blazor、ASP.NETMVC等平台下的系统开发...

Wijmo5 Flexgrid基础教程:数据绑定

WijmoEnterprise下载>FlexGrid在JavaScript程序中启动添加Wijmo引用;添加wijmo控件的扩展;在JavaScript中初始化wijmo控件;(可选)添加cs...

Wijmo5 Flexgrid基础教程:InlineEdit

WijmoEnterprise下载>对于flexgrid,可以直接在单元格内进行编辑。但另外还有一种编辑方式,即在一行添加按钮,统一的编辑和提交数据。本文主要介绍给flexgrid添加编辑按钮...

WinForms Data Grid控件升级(winform devexpress控件)

告诉大家一个好消息:慧都将于近期隆重推出“DevExpress14.2新版发布会”。心动不如行动,赶快报名吧!我们期待与您相约DevExpress14.2新版发布会。>>新增Wind...

XAML控件宽度为另一控件的一半、静态属性绑定

控件上当某些数据需要根据其他数据的变化而变化很多时候,想让某个控件的宽度或者高度是另一个已有控件的一半,一开始打算使用ObjectDataProvider来实现,因为在控件上当某些数据需要根据其他数据...

用 CSS Grid 布局制作一个响应式柱状图

最新一段时间比较喜欢玩弄图表,出于好奇,我想找出比较好的用CSS制作图表的方案。开始学习网上开源图表库,它对我学习新的和不熟悉的前端技术很有帮助,比如这个:CSSGrid。今天和大家分享我学到的...

Grid 移动端双列瀑布流(移动端瀑布流布局)

预览图:原理合理使用Grid的属性:display:设置为grid指明当前容器为Grid布局grid-template-columns:定义每一列的列宽(百分比或绝对单位)grid-templa...

DevExpress导出GridControl控件数据

前言:使用C#做桌面应用时,我们会常常使用Winform作为我们的开发界面,但是windows自带的控件由于长时间不更新,已经不能够满足当前开发需要所以使用DevExpress控件作为Winform...

css grid 布局的那些事儿(css grid布局和flex布局)

CSSGrid是一种为Web开发创建网站布局的方式。它已经存在了很多年,随着更多浏览器的支持,它终于变得越来越流行。接下来我们将了解下CSSGrid及其工作原理。了解下它如何使用。CSS...

Grid.js - 跨框架的前端表格插件(前端table框架)

只想简简单单画个表格,但React,Vue,Angular,…,这么多前端框架,各自都有不同的表格渲染库。就没有表格库能“一次画表,到处运行”吗?来看看Grid.js这个跨框架的前端表格插件吧!...

WPF开发教程01-布局控件(wpf tablecontrol控件)

布局控件是用于进行控件布局的容器类控件,其内部控件按照一定规律自动排列,且在父控件改变大小时,会自动适应。常用布局控件如下:1.一维布局控件(StackPanel)其内部控件按照某个维度自动排列,排...

wxPython - 高级控件之表格Grid(wxpython grid刷新数据)

实战wxPython系列-043wx.grid.Grid及其相关类用于显示和编辑表格数据。它们提供了一组丰富的功能,用于显示、编辑和与各种数据源交互。wx.grid.Grid是一个功能强大的但是又稍微...

前端 BFC、IFC、GFC 和 FFC,这些你都知道吗?

如果觉得我的文章不错,可以关注我,想要看其他的进阶知识可以查看我发布过的文章!编辑搜图请点击输入图片描述BFC(Blockformattingcontexts):块级格式上下文页面上的一个隔离的...

20多个好用的 Vue 组件库,请查收

在本文中,我们将探讨一些最常见的vuejs组件。你可以收藏一波。VueTables-2地址:https://github.com/matfish2/vue-tables-2VueTables2...