(1)前/后端交互内容
QML APP前后端交互的内容按目标(拍脑袋)可以分为2个部分:
方向 | 内容 |
后端(C++) → 前端(QML) | 前端展示所需的数据,形式:简单类型、列表、详情 |
前端(QML) → 后端(C++) | 后端获取数据所需的参数,形式:简单类型、对象 |
但是,问题来了:前后端怎么互认数据形式(数格格式、数据类型)?
官方文档 Data Type Conversion Between QML and C++ 介绍得比较全面,不再赘述。
作者小结(再次拍脑袋)QML与C++之间的类型转换可为5个部分:
- 简单类型,如:bool、int、QString、QDateTime等QT C++类型与QML基本对应
- 枚举类型,使用Q_ENUM标记后对应QML的enum类型
- 对象类型,QVariantMap、QJsonObject类型对应 js 对象类型
- 序列类型,QVariantList、QList<…>、QStringList等类型对应json数组类型
- 自定义值类型,对应 js 对象类型;使用QVariantList、QList包装后则对应json数组类型
可见,相比C++,QML所支持的类型较为简单,除了简单类型和枚举类型,就是json对象和json数组。
(2)定义类型
(2.1)自定义值类型
- 使用 Q_GADGET 宏进行注释(视为QObject的简化,不能有信号和槽,可以有属性和方法)
- 使用 Q_PROPERTY 定义属性以及 getter 和 setter 方法
- 使用 Q_DECLARE_METATYPE 标记类型
示例如下:
class AtlasCat {
Q_GADGET
Q_PROPERTY(int code READ getCode WRITE setCode)
Q_PROPERTY(QString title READ getTitle WRITE setTitle)
Q_PROPERTY(int dispno READ getDispno WRITE setDispno)
Q_PROPERTY(int level READ getLevel WRITE setLevel)
Q_PROPERTY(int parentCode READ getParentCode WRITE setParentCode)
public:
explicit AtlasCat();
~AtlasCat();
// getter|setter
int getCode() const { return (m_code); }
void setCode(const int code) { this->m_code = code; }
QString getTitle() const { return (m_title); }
void setTitle(const QString &title) { this->m_title = title; }
int getDispno() const { return (m_dispno); }
void setDispno(const int dispno) { this->m_dispno = dispno; }
int getLevel() const { return (m_level); }
void setLevel(const int level) { this->m_level = level; }
int getParentCode() const { return (m_parentCode); }
void setParentCode(const int parentCode) { this->m_parentCode = parentCode; }
private:
int m_code = -1;
QString m_title;
int m_dispno = -1;
int m_level = -1;
int m_parentCode = -1;
};
Q_DECLARE_METATYPE(AtlasCat)
(2.2)自定义枚举类型
/** Gender */
enum Gender {
Male = 0, //Male
Female = 1, //Female
};
Q_ENUM(Gender)
(3)值传递
(3.1)后端 → 前端
- 业务对象的定义
以下是相关业务对象所定义的属性,其中引用到了2.1中的自定义类型:
/** 分类列表|前端读 */
Q_PROPERTY(QVariantList atlasCatList READ getAtlasCatList NOTIFY atlasCatListChanged)
/** 所选分类|前端读写 */
Q_PROPERTY(AtlasCat atlasCat READ getAtlasCat NOTIFY atlasCatChanged)
实际上,属性【atlasCatList】亦可用QList<AtlasCat>类型。
- 业务对象传值处理
AtlasCat cat1;
cat4.setCode(101);
cat4.setTitle("aaaa");
cat4.setDispno(1);
cat4.setLevel(1);
cat4.setParentCode(m_atlas);
this->m_atlasCatList.clear();
this->m_atlasCatList.append(QVariant::fromValue(cat1));
...
emit atlasCatListChanged();
在准备好数据之后,即通知前端进行获取。
- 前端(QML)接收并解析
在QML的业务对象的信号回调处理函数中对所传值进行接收和解析。
AtlasManObj {
id: atlasManObj1
...
onAtlasCatListChanged: {
console.log("FRONTEND RETURNS @ onAtlasCatListChanged");
var list1 = atlasManObj1.getAtlasCatList();
for(var i in list1) {
console.log("item #" + (parseInt(i)+1) + " = {" + list1[i] + " }")
}
}
...
输出如下:
qml: FRONTEND RETURNS @ onAtlasCatListChanged
qml: item #1 = {AtlasCat(101, aaaa, 1, 1, 0) }
qml: item #2 = {AtlasCat(102, bbbb, 2, 1, 0) }
可见已经遍历出列表中的各个元素,即:列表按数组解析,元素按json对象解析。
(3.2)前端 → 后端
相对而言,前端传给后端的内容没有后端传给前端的那么复杂,因为QML端不会有自定义值类型(从(3.1.3)中代码可看出,在qml端,atlasCatList的类型是var,而不是也不能是QList<atlasCat>)。也就是说,前端传给后端的类型无非就是简单类型,枚举类型,对象和数组。即:
在QML端不仅不识别接收AtlasCat类型,而且试图将json对象类型转为AtlasCat类型也不会被允许:
"Could not convert argument 0 at"
"onClicked@qrc:/xxxxx/main.qml:61"
"Passing incompatible arguments to C++ functions from JavaScript is dangerous and deprecated."
"This will throw a JavaScript TypeError in future releases of Qt!"
但是可以将json对象类型转为 QJsonObject 类型:
- QML端发起请求
Button {
。。。
text: qsTr("updateXxx")
onClicked: {
var obj = {};
obj["uid"] = "uid444";
obj["sn"] = "sn434323";
obj["code"] = "code9988";
obj["name"] = "张三";
。。。
bizObj1.updateXxx(obj);
}
}
- 后端业务对象方法
前端所调用的方法如下:
void BizObj::updateXxx(const QJsonObject &obj) {
qDebug() << "BACKEND INVOKED ====";
qDebug() << "BizObj::updateXxx() uid = " << m_uid;
。。。
qDebug() << endoscopy;
qDebug() << "uid = " << obj.value("uid").toString();
qDebug() << "sn= " << obj.value("sn").toString();
qDebug() << "code= " << obj.value("code").toString();
qDebug() << "name = " << obj.value("name").toString();
emit updateXxxFinished("OK");
}
其中参数类型不能是自定义值类型,是QJsonObject。
代码输出为:
QJsonObject({"code":"code9988","sn":"sn434323","name":"张三","uid":"uid444"})
uid = "uid444"
sn = "sn434323"
coden = "code9988"
name = "张三"
(4)小结
- 有关QML APP开发中,前后端数据的传递,还请认真阅读官方文档 Data Type Conversion Between QML and C++ 。
- 相比C++,QML所支持的类型较为简单,除了简单类型和枚举类型,就是json对象和json数组。
- 前端传给后端的JSON对象,不可以使用自定义类型转换(该部分有待进一步调研),可以考虑使用QJsonObject。