令人头疼的QML和C++交互
最近公司使用了QML进行界面开发,开发界面确实很高效,而且QML对于触屏、动画的开发尤其友好,用我同事的话说,就是同样是画个矩形,填个颜色,QT需要实例化QPainter,之后画矩形,然后设置画笔,画刷,最后填颜色,QML直接一个Rectangle,甩你18条街。
但是QML也有不好用的地方,就是QML做界面是好做,各种底层功能的支持还是需要使用C++,这就带来了QML和C++的交互问题。
类型转化和糟心的QML文档
以前开发QT,使用QWidgets有什么问题只需要查找文档就好,而使用QML,感觉这个方法失效了,有些文档不仔细找,根本找不到。
这篇文件先说,C++和QML的类型转化问题。我个人总结的方法有两个:
1)使用C++和QML原生都支持的类型,进行参数传递;
2)将C++相关的类,注册进QML的组件或者QML的运行环境,之后进行调用。
由于第二种方法,其实和如何QML调用C++方法这个问题差不多,因此之后再进行讨论,本次只讨论法1.
类型转换-文档推荐用法
QT当中QML和C++类型转化的文档,可在帮助文档中直接搜索**“Data Type Conversion Between QML and C++”**。该文档详细描述了,QML和C++之间的类型转换,我个人总结如下。
首先是基本类型,QML和C++的类型之间有直接的对应关系,可直接进行交互。
Qt Type | QML Basic Type |
---|---|
bool | bool |
unsigned int, int | int |
double | double |
float, qreal | real |
QString | string |
QUrl | url |
QColor | color |
QFont | font |
QDateTime | date |
QPoint, QPointF | point |
QSize, QSizeF | size |
QRect, QRect | Frect |
QMatrix4x4 | matrix4x4 |
QQuaternion | quaternion |
QVector2D, QVector3D, QVector4D | vector2d, vector3d, vector4d |
Enums declared with Q_ENUM() or Q_ENUMS() | enumeration |
以上的是基本的类型,而由基本类型组合成的复杂类型,如何进行交互呢?
由于JS是弱类型语言,因此QT使用了C++中的QVariant和JS当中的各种值建立了一种联系。对应的复杂类型之间的对应关系为:
Qt Type | QML Basic Type |
---|---|
QVariantList | Array |
QVariantMap | Object |
因此对于同种类型的组成的数组,可以使用QVariantList和QML进行交互,当然QVariantList可以承载不同的数据类型组成的列表,但这会引起歧义,而且很容易导致错误,所以不推荐使用这种方法。另外有以下的类型支持从C++转化为QML的array类型,但不支持从QML转化为C++,以下的类型包括:
QList, QList, QList, QList and QStringList, QVector, std::vector,QList,QVector,,std::vector,QVector,QVector,QVector,std::vector,std::vector,
std::vector。
对于不同类型的元素,推荐使用的方法是QVariantMap和QML中Object对应之间的转换。这样可以在转换之后,通过键值对进行查询,所以相比于QVariantList存储不同的类型,QVariantMap存储不同的类型,更加不同意出错。
有人要问了?C++当中的结构体,如何转到QML当中?
如果你要简单,不考虑效率,可以将结构体转化为QVariantMap之后,向QML发送,例如:
//转化为QVariantMap之前
struct Person
{
int num;
int age;
QString name;
}
//转化为QVariantMap
{
...
QVariantMap personMap;
personMap.insert("num",QVariant("num"));
personMap.insert("age",QVariant("age"));
personMap.insert("name",QVariant("name"));
...
}
//之后在QML当中,直接像JS的Object一样调用
var num=person.num;
//或者 var num=person["num"]
如果你考虑程序的运行效率,建议将结构体继承QObject写成类,编写对应的函数,之后注册到QML当中,这样可能效率高点?个人不太确定,主要是QObject夹杂太多东西。
夹杂私货
C++和QML还有一种比较好用的数据交换格式,那就是使用Json进行交互。
先说优点:
1)JS对于Json的天然支持,Json字符串可直接转化为JS的对象;
2)可以存储为文件,方便持久化,也方便排查错误;
缺点:
1)JS对于Json天然支持,而对应的C++需要编写解析或者生成Json字符串的方法(Qt解析Json,可以使用自带的QJsonValue,QJsonArray,QJsonObject等类);
2)效率较低,Json本身是字符串,而不是二进制,占体积大,需要解析,因此效率较低;
所以,你可以根据自己工程的实际情况考虑,是否需要使用Json进行数据交互。
关于QT当中C++和QML之间的转换,就说这些。有错误请指正,下次在写如何将C++的类,注册到QML当中。