1.C++对象注册到元对象系统
第一步:新建一个类,QmlCpp
qmlcpp.h文件:
#ifndef QMLCPP_H
#define QMLCPP_H
#include <QObject>
class QmlCpp : public QObject
{
Q_OBJECT
public:
explicit QmlCpp(QObject *parent = nullptr);
Q_INVOKABLE void setValue(int nValue);
Q_INVOKABLE int getValue();
signals:
public slots:
private:
int m_nValue;
};
#endif // QMLCPP_H
可以看出首先使用 Q_INVOKABLE 这个宏修饰了两个成员函数。
官方文档中的描述:
Apply this macro to declarations of member functions to allow them to be invoked via the meta-object system. The macro is written before the return type
翻译:将此宏应用于成员函数的声明,以允许它们通过元对象系统被调用,宏将在返回类型之前写入。
Q_INVOKABLE这个宏是将函数申明为元对象系统可调用的函数。
QtQuick 也在元对象系统中,这样就可以在QtQuick中可以访问到这2个c++的函数了。
第二步:在main.cpp中进行注册:
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "qmlcpp.h"
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);// 在支持的平台上的Qt中启用高DPI缩放
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
QmlCpp qmlcpp; // 先初始化一个类的实例
qmlcpp.setValue(898); // 设初值
// 将这个 C++ 实例注册到 Qml 引擎上下文中标示为 “qmlpro” 的名字, 这样 Qml 中就可以通过 qmlpro 来访问这个 C++ 实例。
engine.rootContext()->setContextProperty("qmlpro",&qmlcpp);
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
我们将 QmlCpp 的头文件引用进来,然后定义一个 QmlCpp 类的实例,调用设置整型的函数,并将这个 C++ 实例注册到 Qml 引擎上下文中标示为 “QmlCpp” 的名字, 这样 Qml 中就可以通过 QmlCpp 来访问这个 C++ 实例。
第三步:在Qml文件中进行调用:
import QtQuick 2.12
import QtQuick.Controls 2.12
Item {
Button{
id:btn
height: 48
width: 120
text: "1"
anchors.centerIn: parent
onClicked: {
text=qmlpro.getValue();
console.log(text);
}
}
Button{
id:btn1
height: 48
width: 120
anchors.top: btn.bottom
anchors.topMargin: 12
anchors.horizontalCenter: btn.horizontalCenter
onClicked: {
qmlpro.setValue(999);
}
}
}
其中在上述代码中,最关键的步骤是第二步,所以仔细来看一下第二步 在main.cpp中是是如何进行注册的:
QQmlApplicationEngine engine;
首先看的是这个类 QQmlApplicationEngine,官方文档中的描述如下:
QQmlApplicationEngine提供了一种从单个QML文件加载应用程序的便捷方法。
此类结合了QQmlEngine和QQmlComponent来提供一种方便的方式来加载单个QML文件。它还向QML公开了一些中央应用程序功能,而C ++ / QML混合应用程序通常会从C ++控制这些功能。
通过官方描述得知是用来加载qml文件的。
QmlCpp qmlcpp; // 先初始化一个类的实例
qmlcpp.setValue(898); // 设初值
// 将这个 C++ 实例注册到 Qml 引擎上下文中标示为 “qmlpro” 的名字, 这样 Qml 中就可以通过 qmlpro 来访问这个 C++ 实例。
engine.rootContext()->setContextProperty("qmlpro",&qmlcpp);
接着初始化了一个实例qmlcpp,调用setValue()函数。
engine.rootContext() 返回的是一个引擎的根上下文,返回的是 QQmlContext 类型指针;
engine.rootContext()->setContextProperty(“qmlpro”,&qmlcpp);
设置根上下文 属性名为 “qmlpro”的值为 一个指针,该指针指向qmlcpp这个实例。
之后再qml中直接使用 qmlpro 调用2个公共函数。
Button{
id:btn
height: 48
width: 120
text: "1"
anchors.centerIn: parent
onClicked: {
text= qmlpro.getValue()
}
}
2、补充:上述流程实现了在qml文件中调用 C++类中两个函数,这两个函数必须被Q_INVOKABLE宏进行修饰。
但是要直接访问 C++类中的属性,还需要使用到 Q_PROPERTY,
#ifndef QMLCPP_H
#define QMLCPP_H
#include <QObject>
#include <QJSValue>
#include <QQmlEngine>
class QmlCpp : public QObject
{
Q_OBJECT
// 新加的内容
Q_PROPERTY(int value READ getValue WRITE setValue NOTIFY valueChange)
public:
explicit QmlCpp(QObject *parent = nullptr);
Q_INVOKABLE void setValue(int nValue);
Q_INVOKABLE int getValue();
signals:
//新增内容
void valueChange();
public slots:
private:
int m_nValue;
};
#endif // QMLCPP_H
C++类中新增一行代码:
Q_PROPERTY(int value READ getValue WRITE setValue NOTIFY valueChange)
这行代码的意思是:定义一个名称为value的属性,读取属性的函数是getValue,写入属性的函数是setValue,属性发生变化时触发的信号是valueChange。
所以在类中又新增了一个信号:
void valueChange();
然后就可以在QML文件中直接访问这个value:
Button{
id:btn
height: 48
width: 120
text: "1"
anchors.centerIn: parent
onClicked: {
text= qmlpro.value;
}
}
3.qml中直接访问qmlCpp中的槽函数:
因为C++类中的槽函数本身就在元对象系统中,而我们已经把qmlCpp注册到元对象系统中,所以Qml可以直接对qmlCpp类中槽函数进行调用;
第一步:现在qmlCpp类中新建一个槽函数,addValue()
void QmlCpp::addValue(int nValue)
{
m_nValue += nValue;
}
在QML文件中直接调用:
Button{
id:btn
height: 48
width: 120
text: "1"
anchors.centerIn: parent
onClicked: {
qmlpro.addValue(10);
text= qmlpro.value
console.log(text)
}
}
总结:
至此,这篇博客讲述了如何在QML文件中访问 c++类的 成员函数、数据成员、槽函数的基本步骤。
-
先将自定义的C++类注册到元对象系统中。
-
使用Q_INVOKABLE宏修饰 类的成员函数。
-
使用Q_PROPERTY这个宏来定义 属性的名称 读取、写入、以及值发生变化所触发的信号。
-
直接调用槽函数。