1. 说明
在QML与C++混合开发时,QML界面元素可以通过某种方式去调用C++中的函数,相关文章可查看C++注册函数和属性供QML使用。但是,在某种情况下也需要在C++端去调用QML中定义的函数,完成相应的功能,本篇文章将简单介绍几种方式进行实现。
2. 方式一
这种方式比较灵活,可以在QML界面的某个控件中触发C++去调用函数,也就是QML先去调用C++当中的某一个函数A,这个函数A再去调用QML中的函数,好处在于我们可以控制函数执行的时间。
第一步:自定义一个类并命名为callqmlfunc,在其中定义一个函数sayHello,这个函数是需要在QML端调用的,代码如下:
callqmlfunc.h:
#ifndef CALLQMLFUNC_H
#define CALLQMLFUNC_H#include <QObject>
#include <QDebug>
#include <QQmlApplicationEngine>class CallQMLFunc : public QObject
{Q_OBJECT
public:explicit CallQMLFunc(QObject *parent = nullptr);Q_INVOKABLE void sayHello();//需要在QML中调用,所以要用 Q_INVOKABLE 修饰signals:};#endif // CALLQMLFUNC_H
第二步:将上面的自定义类注册到QML的上下文背景中,可参考文章:C++类注册到QML中使用,这样才能在QML中调用类中的sayHello函数。在注册C++类时关键一步是需要直接指定其父对象,这样在下文使用时才能根据父对象找到其内部的对应名称的函数。相关代码如下:
在main.cpp中进行注册:
//第一种方式:(函数在根节点中)注册类时指定父类,在QML中像正常类对象一样调用函数
QObject *root=engine.rootObjects().at(0);//获取QML中的根节点,就是Window
qDebug()<<root->objectName();//这里的输出为:mainWindow
CallQMLFunc *callQmlFunc = new CallQMLFunc(root);//创建C++类对象,并指定其父类为 root ,也就是根节点
engine.rootContext()->setContextProperty("callQmlFunc",callQmlFunc);//将类对象注册到QML的上下文背景中
如果QML中函数没有写在根节点下,而是写到了其他子节点下,此时我们可以给子节点设置以下 objectName 属性,然后使用下面的方式去查找父类,也是可以的:
//第一种方式:(函数在根节点中)注册类时指定父类,在QML中像正常类对象一样调用函数
QObject *root=engine.rootObjects().at(0);//获取QML中的根节点,就是Window
QObject* parentObj = root->findChild<QObject*>("mainText");//根据根节点查找字节点
CallQMLFunc *callQmlFunc = new CallQMLFunc(parentObj);//创建C++类对象,并指定其父类为 parentObj ,也就是根节点
engine.rootContext()->setContextProperty("callQmlFunc",callQmlFunc);//将类对象注册到QML的上下文背景中
第三步:注册完成后,在callqmlfunc.cpp文件中调用QML中的函数
callqmlfunc.cpp:
#include "callqmlfunc.h"CallQMLFunc::CallQMLFunc(QObject *parent): QObject{parent}
{}void CallQMLFunc::sayHello()
{//调用QML中函数sayHello//注意://第一个参数是父类,这里能够使用parent(),是因为在上面注册C++类到QML中时已经指定了父对象//第二个参数是需要调用的QML中的函数名称//第三个参数是传递到QML中函数的参数,参数类型必须是QVariant类型QMetaObject::invokeMethod(parent(),"sayHello",Q_ARG(QVariant,"Hello..."));
}
注意:
如果QML中函数有返回值,C++调用这个函数之后,需要接收函数的返回值,那么可以采用下述方式:
void CallQMLFunc::sayHello()
{QVariant ret;//声明变量用于接收返回值//此时第三个参数是接收返回值的变量,类型必须是QVariant类型//第四个参数是传递到QML中函数的参数,参数类型必须是QVariant类型QMetaObject::invokeMethod(parent(),"sayHello",Q_RETURN_ARG(QVariant,ret),Q_ARG(QVariant,"Hello..."));qDebug()<<ret.toString();//输出为:"hahahaha..."
}
3. 方式二
这种方式与方式一的不同点在于:注册C++类时并未指定父对象是谁,无法使用QML中的元素控制函数的执行,而是在main.cpp中直接调用了QML中的函数(了解即可,用处不大),相关代码如下:
main.cpp:
auto root2 = engine.rootObjects();
auto parentObj = root2.first()->findChild<QObject*>("mainText");
QMetaObject::invokeMethod(parentObj,"sayHello",Q_ARG(QVariant,"Hello..."));
4. QML主界面代码:
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15Window {id:rootobjectName: "mainWindow" //设置控件属性名,供C++中的engine查找width: 640height: 480visible: truetitle: qsTr("Hello Signal")//定义函数,供C++调用function sayHello(_str){console.log(_str)helloText.text = _strreturn "hahahaha..."}Button{id:btnobjectName: "helloBtn" //设置控件属性名,供C++中的engine查找width: 100height: 50anchors.horizontalCenter: parent.horizontalCenteranchors.top:parent.topanchors.topMargin: 10text: "callQmlFunc"onClicked: {callQmlFunc.sayHello()}}Text{id:helloTextobjectName: "mainText" //设置控件属性名,供C++中的engine查找width: 100height: 50anchors.centerIn: parentfont.pixelSize: 20//定义函数,供C++调用function sayHello(_str){console.log(_str)helloText.text = _strreturn "hahahaha..."}}}