这篇博客主要讲解在qml端如何直接调用c++的函数并获得返回值,在c++端如何直接调用qml的函数并获得返回值;
主要以 map 或者 jsonobject、list 或者 jsonarray为主!
其他单个类型,常见的类型,例如QString、int等,就不演示了;一通百通。
目录
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_OBJECT
public:
MyObject(QObject *parent = nullptr); // 构造函数
~MyObject();
static MyObject *getInstance();
/// 返回obj
Q_INVOKABLE QJsonObject funcObj(QString name, int age);
/// 返回list
Q_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的首个元素就是window
QObject *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"] = name
obj["age"] = age
obj["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: 50
objectName: "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>();
// 方式一,转换为 QJsonObject
QJsonObject jsonObj = jsValue.toVariant().toJsonObject();
qDebug() << jsonObj << " " << jsonObj["name"] << " " << jsonObj["age"] << " " << jsonObj["sex"];
// 方式二,转换为QMap
QMap<QString, QVariant> map = jsValue.toVariant().toMap();
qDebug() << map;
}
注意,以上前提是调用qml函数返回的是QJSValue类型;
当然,返回时,也可以直接返回json字符串,那么接收到后就可以直接转成QJsonDocument去处理了。
在qml函数中,将obj转成json字符串后再返回,如下:
function qmlFuncObj(name, age) {
let obj = { };
obj["name"] = name
obj["age"] = age
obj["sex"] = "man"
//return obj
return 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:转换为 QJsonArray
QJsonArray 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的首个元素就是window
QObject *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.14
import MyObj 1.0 // 导入自定义模块
Window {
id: root
visible: true
width: SCREEN_WIDTH
height: 500
title: 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"] = name
obj["age"] = age
obj["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)
}
Button {
width: 100; height: 50
objectName: "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++相互调用函数已经介绍完毕,具体用法看具体项目吧!