QML学习笔记(五十四)QML与C++交互:数据转换——QVariantList与QVariantMap
前言
之前已经介绍了基本数据类型、时间和日期、序列数组的转换,除此之外还有一种QVariantList与QVariantMap的转换,熟悉QVariant的朋友应该知道,它是 Qt 框架中的一个通用数据类型,用于存储多种数据类型值,如基本类型、QString、QWidget等。它通过类型擦除实现,内部维护一个类型标识器来识别存储的数据类型,并支持类型转换。QVariantList 是存储 QVariant 的线性列表,支持动态扩展,允许存储不同类型的值。QVariantMap 则是键值对的存储形式,键为 QString,值为 QVariant,同样支持存储多种类型值。需要注意的是,QVariant 支持的类型需通过 Q_DECLARE_METATYPE 注册,以确保正确存储和转换。
而本节示例的就是C++到QML的QVariantList与QVariantMap转换。
一、代码示例
基于之前的工程,我们添加一个新的类:
#ifndef CPPCLASS1_H
#define CPPCLASS1_H#include <QObject>
#include <QtQml>
#include <QColor>
#include <QFont>class CppClass1 : public QObject
{Q_OBJECT
public:explicit CppClass1(QObject *parent = nullptr);Q_INVOKABLE void passFromQmlToCpp(QVariantList list, QVariantMap map);Q_INVOKABLE QVariantList getVariantListFromCpp();Q_INVOKABLE QVariantMap getVariantMapFromCpp();void setQmlRootObject(QObject *value);signals:public slots:void triggerJsCall();private:QObject * qmlRootObject;};#endif // CPPCLASS1_H
#include "cppclass1.h"CppClass1::CppClass1(QObject *parent): QObject{parent}
{}
void CppClass1::passFromQmlToCpp(QVariantList list/*array*/, QVariantMap map/*object*/)
{qDebug() << "Received variant list and map from QML";qDebug() << "List :";for( int i{0};i < list.size();i++){qDebug() << "List item :" << list.at(i).toString();}qDebug() << "Map :";for( int i{0};i < map.keys().size();i++){qDebug() << "Map item :" << map[map.keys().at(i)].toString();}
}QVariantList CppClass1::getVariantListFromCpp()
{QVariantList list;list << 123.3 << QColor(Qt::cyan) << "Qt is great" << 10;return list;
}QVariantMap CppClass1::getVariantMapFromCpp()
{QVariantMap map;map.insert("movie","Game of Thrones");map.insert("names","John Snow");map.insert("role","Main Character");map.insert("release", QDate(2011, 4, 17));return map;
}void CppClass1::setQmlRootObject(QObject *value)
{qDebug()<<"setQmlRootObject";qmlRootObject = value;
}void CppClass1::triggerJsCall()
{qDebug() << "Calling JS";QVariantList list;//arraylist << 123.3 << QColor(Qt::cyan) << "Qt is great" << 10;QVariantMap map;//objectmap.insert("movie","Game of Thrones");map.insert("names", "John Snow");map.insert("role","Main Character");map.insert("release", QDate(2011, 4, 17));QMetaObject::invokeMethod(qmlRootObject,"arrayObjectFunc",Q_ARG(QVariant, QVariant::fromValue(list)),Q_ARG(QVariant,QVariant::fromValue(map)));qDebug() << "Called JS";
}
这里介绍一下几个接口的作用:
1.passFromQmlToCpp:顾名思义,传递qml端的参数到C++端进行打印,接口的参数是QVariantList和QVariantMap,对应qml端的话是array和object的概念,这一点在之后的qml代码中有所呈现。
2.getVariantListFromCpp:从c++端获取一个QVariantList,可以看到传入这个list的数据类型可以是不同的:
list << 123.3 << QColor(Qt::cyan) << "Qt is great" << 10;
3.getVariantMapFromCpp:从c++端获取一个QVariantMap,可以看到传入这个的数据类型可以是不同的,但键值需要时字符串:
QVariantMap map;
map.insert("movie","Game of Thrones");
map.insert("names","John Snow");
map.insert("role","Main Character");
map.insert("release", QDate(2011, 4, 17));
4.setQmlRootObject:设置qml的根对象,我们需要在main中专门设置这个,这样才能在C++中直接调用qml侧的代码:
5.triggerJsCall:C++侧直接调用qml的接口arrayObjectFunc,传递QVariantList和QVariantMap两个参数。
然后,我们看main代码:
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <cppclass.h>
#include <cppclass1.h>int main(int argc, char *argv[])
{QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);QGuiApplication app(argc, argv);QQmlApplicationEngine engine;CppClass1 cppclass1;engine.rootContext()->setContextProperty("CppClass1",&cppclass1);const QUrl url(QStringLiteral("qrc:/main.qml"));QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,&app, [url](QObject *obj, const QUrl &objUrl) {if (!obj && url == objUrl)QCoreApplication::exit(-1);}, Qt::QueuedConnection);engine.load(url);if(engine.rootObjects().isEmpty()){qDebug()<<"engine.rootObjects().isEmpty()";return -1;}else{cppclass1.setQmlRootObject(engine.rootObjects().first());}return app.exec();
}
这里创建了一个cppclass1对象,并上下文暴露给qml侧。最重要是它还设置了qml的根对象。
然后是qml侧:
function arrayObjectFunc(array,object){console.log("---Printing array---")array.forEach(function(element){console.log("Array item :"+ element)})console.log("---Printing object---")for (var mkey in object){console.log("Object["+mkey+"]:"+ object[mkey])}}Button{id :button1anchors.left: button2Id.righttext : "Pass data to Cpp"onClicked:{var arr = ['Africa','Asia',"Europe","North America","South America","Oceania","Antarctica"]var obj={firstName:"John",lastName:"Doe",location:"Earth"}CppClass1.passFromQmlToCpp(arr,obj);// cppClassId.passFromQmlToCpp(arr,obj);}}Button{id :button2anchors.top : button1.bottomx : button1.xtext :"GetVariantListFromCpp"onClicked:{var data = CppClass1.getVariantListFromCpp() //returns arraydata.forEach(function(element){console.log("Array item:" + element)})}}Button{id : button3anchors.top : button2.bottomx : button2.xtext :"GetVariantMapFromCpp"onClicked:{var data = CppClass1.getVariantMapFromCpp() //returns objectfor ( var mKey in data){console.log("Object[" +mKey+"] :"+ data[mKey])}}}Button {id : button4text : "TriggerJSCall"anchors.top: button3.bottomx : button2.xonClicked:{CppClass1.triggerJsCall();}}
这里首先设置了一个JS函数arrayObjectFunc,里面可以打印list和map的参数。
二、演示效果
qml中主要设置了四个按钮,作用分别是:
1.向C++传入qml的数组和对象,在c++中打印QVariantList与QVariantMap
2.qml从C++获取QVariantList,并打印数据
3.qml从C++获取QVariantMap,并打印数据
4.触发C++函数,让其调用qml侧的函数,并最终打印数据
看下效果:

依次点击,打印:
1.
Received variant list and map from QML
List :
List item : "Africa"
List item : "Asia"
List item : "Europe"
List item : "North America"
List item : "South America"
List item : "Oceania"
List item : "Antarctica"
Map :
Map item : "John"
Map item : "Doe"
Map item : "Earth"
qml: Array item:123.3
qml: Array item:#00ffff
qml: Array item:Qt is great
qml: Array item:10
qml: Array item:123.3
qml: Array item:#00ffff
qml: Array item:Qt is great
qml: Array item:10
Calling JS
qml: ---Printing array---
qml: Array item :123.3
qml: Array item :#00ffff
qml: Array item :Qt is great
qml: Array item :10
qml: ---Printing object---
qml: Object[movie]:Game of Thrones
qml: Object[names]:John Snow
qml: Object[release]:Sun Apr 17 08:00:00 2011 GMT+0800
qml: Object[role]:Main Character
Called JS
可以看到,这些数据的传输都没有什么大问题。唯一一个是颜色,传递的是QColor,解析出来是#00ffff,可能被当做了字符串,当然直接拿来使用也没有问题。
三、总结
QVariantList与QVariantMap虽然不那么常用,但能很好的批量传递一些参数和信息,这在某些设计方式中可能有帮助。
至此,我们应该能很好地处理C++和qml之间的数据类型转换了。
