QML与C++相互调用函数并获得返回值
这篇博客主要讲解在qml端如何直接调用c++的函数并获得返回值,在c++端如何直接调用qml的函数并获得返回值;
主要以 map 或者 jsonobject、list 或者 jsonarray为主!
其他单个类型,常见的类型,例如QString、int等,就不演示了;一通百通。
目录
1 准备工作
1.1 C++端
1.2 QML端
2 qml端直接调用c++端函数
3 c++端直接调用qml端函数
3.1 调用qml的qmlFuncObj函数
3.2 调用qml的qmlFuncList函数
4 代码汇总
1 准备工作
1.1 C++端
定义自定义类型MyObject,并提供QJsonObject funcObj(QString name, int age); 和 QList<QString> funcList(QString name1, QString name2); 两个函数供qml调用;
注意在头文件定义时需要使用 Q_INVOKABLE 去修饰,否则qml端无法调用。
myobject.h
#ifndef MYOBJECT_H
#define MYOBJECT_H#include <QObject>
#include <QDebug>
#include <QJsonObject>
#include <QList>class MyObject : public QObject
{Q_OBJECTpublic:MyObject(QObject *parent = nullptr); // 构造函数~MyObject();static MyObject *getInstance();/// 返回objQ_INVOKABLE QJsonObject funcObj(QString name, int age);/// 返回listQ_INVOKABLE QList<QString> funcList(QString name1, QString name2);
};#endif // MYOBJECT_H
myobject.cpp
#include "myobject.h"MyObject::MyObject(QObject *parent) : QObject(parent)
{}MyObject::~MyObject()
{
}MyObject *MyObject::getInstance()
{static MyObject *obj = nullptr;if (!obj) {obj = new MyObject;}return obj;
}QJsonObject MyObject::funcObj(QString name, int age)
{QJsonObject obj;obj.insert("name", name);obj.insert("age", age);return obj;
}QList<QString> MyObject::funcList(QString name1, QString name2)
{QList<QString> list;list << name1 << name2 << "第三";return list;
}
这里的QJsonObject也可以是QVariantMap,注意不能是QMap类型,qml无法识别;
这里的QList<QString>也可以是QVector,QQJsonArray等类型;
main.cpp
然后再main函数中将MyObject注册为全局单例对象;
注意代码中已经提前获得了windowObj,即是qml的对象,用于下面调用qml函数使用。
#include <QGuiApplication>
#include <QQmlApplicationEngine>#include <QQmlContext>
#include "myobject.h"#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>int main(int argc, char *argv[])
{QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);QGuiApplication app(argc, argv);QQmlApplicationEngine engine;// 获得全局对象,上下文对象QQmlContext *context = engine.rootContext();// 给qml设置一个全局变量;如果qml内部有定义重名变量,那么会优先使用qml内部定义的变量;另外,定义全局变量会有性能问题context->setContextProperty("SCREEN_WIDTH", 800);// 注册,在需要使用的地方 import MyObj 1.0
// qmlRegisterType<MyObject>("MyObj", 1, 0, "MyObject");// 注册全局单例对象qmlRegisterSingletonInstance("MyObj", 1, 0, "MyObject", MyObject::getInstance());engine.load(QUrl(QStringLiteral("qrc:/main.qml")));if (engine.rootObjects().isEmpty())return -1;// 在engine加载完成后,就可以获取qml的所有对象了QList<QObject*> list = engine.rootObjects();// list的首个元素就是windowQObject *windowObj = list.first();return app.exec();
}
1.2 QML端
定义两个函数,function qmlSlot(name, age) 和 function qmlFuncObj(name, age) 供C++调用;
// 定义qml函数
function qmlFuncObj(name, age) {let obj = { };obj["name"] = nameobj["age"] = ageobj["sex"] = "man"return obj//return JSON.stringify(obj) // 返回JSON字符串
}// 定义qml函数
function qmlFuncList(name, age) {let list = [];list.push(name)list.push(age)list.push("666")return list//return JSON.stringify(list) // 返回JSON字符串
}
2 qml端直接调用c++端函数
注意,调用的c++函数,在定义时,必须使用 Q_INVOKABLE 去修饰,否则在qml这里是无法调用的。
如下:
/// 返回obj
Q_INVOKABLE QJsonObject funcObj(QString name, int age);
/// 返回list
Q_INVOKABLE QList<QString> funcList(QString name1, QString name2);
然后,就可以在qml端直接调用c++的函数了,使用 let 定义变量接收返回值即可!
Button {width: 100; height: 50objectName: "myButton"onClicked: {// 直接调用c++函数let obj = MyObject.funcObj("小明", 99)console.log("name:", obj["name"], " age:", obj["age"])let list = MyObject.funcList("第一", "第二");console.log("count:", list.length)// 遍历方式一for (let i = 0; i < list.length; ++i) {console.log("list:", list[i])}// 遍历方式二for (const item of list) {console.log(item);}}
}
通过点击按钮后,即可调用c++函数,并且得到返回值做打印:
3 c++端直接调用qml端函数
在c++中调用qml的函数,需要使用到QMetaObject::invokeMethod函数,其是重载函数;
函数原型:
static inline bool invokeMethod(QObject *obj, const char *member,QGenericReturnArgument ret,QGenericArgument val0 = QGenericArgument(nullptr),QGenericArgument val1 = QGenericArgument(),QGenericArgument val2 = QGenericArgument(),QGenericArgument val3 = QGenericArgument(),QGenericArgument val4 = QGenericArgument(),QGenericArgument val5 = QGenericArgument(),QGenericArgument val6 = QGenericArgument(),QGenericArgument val7 = QGenericArgument(),QGenericArgument val8 = QGenericArgument(),QGenericArgument val9 = QGenericArgument())
{return invokeMethod(obj, member, Qt::AutoConnection, ret, val0, val1, val2, val3,val4, val5, val6, val7, val8, val9);
}
参数一:qml对象指针;
参数二:调用qml的函数名;
参数三:调用函数的返回值;
剩余参数:调用qml函数的传参参数;
返回值:调用成功返回true,其他返回false。
当然,还有很多函数重载,这里主要介绍有返回值的,其他那些都类似。
注意,定义接收函数返回的类型变量和传参的函数变量使用的类型均是QVaraint类型。有兴趣的可以尝试一下目标类型,看下行不行。
3.1 调用qml的qmlFuncObj函数
// 定义接受的返回值
QVariant res;
// 定义参数
QVariant arg_name = "jtom";
QVariant arg_age = 26;
// 调用qml函数
bool flag = QMetaObject::invokeMethod(windowObj, "qmlFuncObj",Q_RETURN_ARG(QVariant, res),Q_ARG(QVariant, arg_name),Q_ARG(QVariant, arg_age));
qDebug() << "res = " << res;
windowObj 是 qml的对象指针,在准备工作中已经获得!
通过打印res返回查看,得到返回结果是一个QJSValue类型,是一个JavaScript类型。
如果需要获得QJsonObject类型,则需要将res转成QJSValue类型后,再转成QVaraint类型,再转成QJsonObject类型;
如果需要获得QMap类型,则需要将res转成QJSValue类型后,再转成QVaraint类型,再转成QMap类型;
if (flag) {QJSValue jsValue = res.value<QJSValue>();// 方式一,转换为 QJsonObjectQJsonObject jsonObj = jsValue.toVariant().toJsonObject();qDebug() << jsonObj << " " << jsonObj["name"] << " " << jsonObj["age"] << " " << jsonObj["sex"];// 方式二,转换为QMapQMap<QString, QVariant> map = jsValue.toVariant().toMap();qDebug() << map;
}
注意,以上前提是调用qml函数返回的是QJSValue类型;
当然,返回时,也可以直接返回json字符串,那么接收到后就可以直接转成QJsonDocument去处理了。
在qml函数中,将obj转成json字符串后再返回,如下:
function qmlFuncObj(name, age) {let obj = { };obj["name"] = nameobj["age"] = ageobj["sex"] = "man"//return objreturn JSON.stringify(obj) // 返回JSON字符串
}
然后就可以当作json字符串的方式去处理解析了,如下:
if (flag) {QJsonDocument doc = QJsonDocument::fromJson(res.toString().toUtf8());QJsonObject jsonObj = doc.object();qDebug() << jsonObj << " " << jsonObj["name"] << " " << jsonObj["age"] << " " << jsonObj["sex"];
}
可以看出,转为json字符串返回后,c++接收到的类型不再是QJSValue类型,而是QString类型。
3.2 调用qml的qmlFuncList函数
// 定义接收返回值变量
QVariant res;
// 定义参数
QVariant arg_name = "jtom";
QVariant arg_age = "266";
// 调用qml函数
bool flag = QMetaObject::invokeMethod(windowObj, "qmlFuncList",Q_RETURN_ARG(QVariant, res),Q_ARG(QVariant, arg_name),Q_ARG(QVariant, arg_age));qDebug() << "res = " << res;
可以看出,返回值也是一个QJSValue类型,处理方式与上面类似了。
if (flag) {// 方法1:转换为 QVariantList(推荐)QVariantList list = res.toList();qDebug() << "List:" << list;qDebug() << "Elements:" << list[0] << list[1] << list[2];// 方法2:转换为 QJsonArrayQJsonArray jsonArray = QJsonArray::fromVariantList(res.toList());qDebug() << "JSON Array:" << jsonArray;
}
注意,以上前提是调用qml函数返回的是QJSValue类型;
当然,返回时,也可以直接返回json字符串,那么接收到后就可以直接转成QJsonDocument去处理了。
在qml函数中,将list转成json字符串后再返回,如下:
function qmlFuncList(name, age) {let list = [];list.push(name)list.push(age)list.push("666")// return list;return JSON.stringify(list)
}
然后就可以当作json字符串的方式去处理解析了,如下:
if (flag) {QJsonDocument doc = QJsonDocument::fromJson(res.toString().toUtf8());QJsonArray jsonArr = doc.array();qDebug() << jsonArr;
}
可以看出,转为json字符串返回后,c++接收到的类型不再是QJSValue类型,而是QString类型。
4 代码汇总
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>#include <QQmlContext>
#include "myobject.h"#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>int main(int argc, char *argv[])
{QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);QGuiApplication app(argc, argv);QQmlApplicationEngine engine;// 获得全局对象,上下文对象QQmlContext *context = engine.rootContext();// 给qml设置一个全局变量;如果qml内部有定义重名变量,那么会优先使用qml内部定义的变量;另外,定义全局变量会有性能问题context->setContextProperty("SCREEN_WIDTH", 800);// 注册,在需要使用的地方 import MyObj 1.0
// qmlRegisterType<MyObject>("MyObj", 1, 0, "MyObject");qmlRegisterSingletonInstance("MyObj", 1, 0, "MyObject", MyObject::getInstance());engine.load(QUrl(QStringLiteral("qrc:/main.qml")));if (engine.rootObjects().isEmpty())return -1;// 在engine加载完成后,就可以获取qml的所有对象了QList<QObject*> list = engine.rootObjects();// list的首个元素就是windowQObject *windowObj = list.first();{// 定义接受的返回值QVariant res;// 定义参数QVariant arg_name = "jtom";QVariant arg_age = 26;// 调用qml函数bool flag = QMetaObject::invokeMethod(windowObj, "qmlFuncObj",Q_RETURN_ARG(QVariant, res),Q_ARG(QVariant, arg_name),Q_ARG(QVariant, arg_age));qDebug() << "res = " << res;
// if (flag) {
// QJSValue jsValue = res.value<QJSValue>();// // 方式一,转换为 QJsonObject
// QJsonObject jsonObj = jsValue.toVariant().toJsonObject();
// qDebug() << jsonObj << " " << jsonObj["name"] << " " << jsonObj["age"] << " " << jsonObj["sex"];// // 方式二,转换为QMap
// QMap<QString, QVariant> map = jsValue.toVariant().toMap();
// qDebug() << map;
// }if (flag) {QJsonDocument doc = QJsonDocument::fromJson(res.toString().toUtf8());QJsonObject jsonObj = doc.object();qDebug() << jsonObj << " " << jsonObj["name"] << " " << jsonObj["age"] << " " << jsonObj["sex"];}}{// 定义接受的返回值QVariant res;// 定义参数QVariant arg_name = "jtom";QVariant arg_age = "266";// 调用qml函数bool flag = QMetaObject::invokeMethod(windowObj, "qmlFuncList",Q_RETURN_ARG(QVariant, res),Q_ARG(QVariant, arg_name),Q_ARG(QVariant, arg_age));qDebug() << "res = " << res;
// if (flag) {
// // 方法1:转换为 QVariantList(推荐)
// QVariantList list = res.toList();
// qDebug() << "List:" << list;
// qDebug() << "Elements:" << list[0] << list[1] << list[2];// // 方法2:转换为 QJsonArray
// QJsonArray jsonArray = QJsonArray::fromVariantList(res.toList());
// qDebug() << "JSON Array:" << jsonArray;
// }if (flag) {QJsonDocument doc = QJsonDocument::fromJson(res.toString().toUtf8());QJsonArray jsonArr = doc.array();qDebug() << jsonArr;}}return app.exec();
}
main.qml
import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Controls 2.14import MyObj 1.0 // 导入自定义模块Window {id: rootvisible: truewidth: SCREEN_WIDTHheight: 500title: qsTr("Hello World")color: "white"objectName: "window"// 定义qml端槽函数function qmlSlot(name, age) {console.log("qml:name = ", name, " age = ", age);}// 定义qml函数function qmlFuncObj(name, age) {let obj = { };obj["name"] = nameobj["age"] = ageobj["sex"] = "man"//return objreturn JSON.stringify(obj) // 返回JSON字符串}// 定义qml函数function qmlFuncList(name, age) {let list = [];list.push(name)list.push(age)list.push("666")// return list;return JSON.stringify(list)}Button {width: 100; height: 50objectName: "myButton"onClicked: {let obj = MyObject.funcObj("小明", 99)console.log("obj:", obj, " name:", obj["name"], " age:", obj["age"])let list = MyObject.funcList("第一", "第二");console.log("list:", list, " count:", list.length)// // 遍历方式一for (let i = 0; i < list.length; ++i) {console.log("list:", list[i])}
// // 遍历方式二for (const item of list) {console.log(item);}}}
}
qml与c++相互调用函数已经介绍完毕,具体用法看具体项目吧!