需求
qt 中 qml 和 c++ 中的类如何进行交互
解决
c++ 类注册到元对象系统
通过使用上下文属性,可以将C++对象嵌入到QML环境中。上下文属性适用于简单的应用程序。它们将您的对象导出为全局对象。在QML引擎实例化之后,上下文被暴露给QML环境。
调用函数
QQmlApplicationEngine engine;
QmlCpp qmlcpp; // 先初始化一个类的实例
qmlcpp.setValue(898); // 设初值
// 将这个 C++ 实例注册到 Qml 引擎上下文中标示为 “qmlpro” 的名字, 这样 Qml 中就可以通过 qmlpro 来访问这个 C++ 实例。
engine.rootContext()->setContextProperty("qmlpro",&qmlcpp);
class QmlCpp : public QObject
{
Q_OBJECT
public:
explicit QmlCpp(QObject *parent = nullptr);
// 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 void setValue(int nValue);
Q_INVOKABLE int getValue();
}
Item {
Button{
id:btn
text: "1"
onClicked: {
text=qmlpro.getValue();
console.log(text);
}
}
Button{
id:btn1
onClicked: {
qmlpro.setValue(999);
}
}
}
总结,步骤如下:
- Q_INVOKABLE这个宏是将函数申明为元对象系统可调用的函数。QtQuick 也在元对象系统中,这样就可以在QtQuick中可以访问到这2个c++的函数了。
- QQmlApplicationEngine提供了一种从单个QML文件加载应用程序的便捷方法。结合了QQmlEngine和QQmlComponent来提供一种方便的方式来加载单个QML文件。它还向QML公开了一些中央应用程序功能,而C ++ / QML混合应用程序通常会从C ++控制这些功能。
- engine.rootContext() 返回的是一个引擎的根上下文,返回的是 QQmlContext 类型指针;
- engine.rootContext()->setContextProperty(“qmlpro”,&qmlcpp);
- 设置根上下文 属性名为 “qmlpro”的值为 一个指针,该指针指向qmlcpp这个实例。
- 之后再qml中直接使用 qmlpro 调用2个公共函数。
属性
如果还需要使用 c++ 类的属性:
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();
private:
int m_nValue;
};
Button{
id:btn
onClicked: {
text= qmlpro.value;
}
}
直接调用槽函数
public slots:
void addValue(int nValue)
Button{
id:btn
text: "1"
onClicked: {
qmlpro.addValue(10);
text= qmlpro.value
console.log(text)
}
}
c++ 类注册到 qml 系统
注册QML类型允许开发人员从QML环境中控制C++对象的生命周期。这不能通过上下文属性来实现,也不能填充全局名称空间。尽管如此,所有的类型都需要首先进行注册,因此,所有的库都需要在应用程序启动时进行链接,这在大多数情况下并不是问题。这些方法可以是公共插槽,也可以是用Q_INVOKABLE标记的公共方法。
函数
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;
};
QQmlApplicationEngine engine;
qmlRegisterType<QmlCpp>("myqml",1,0,"QmlCpp");
import myqml 1.0
Item {
QmlCpp{
id:qmlpro
}
Button{
id:btn
text: "1"
onClicked: {
text=qmlpro.getValue();
console.log(text);
}
}
Button{
id:btn1
onClicked: {
var value = qmlpro.getValue()
qmlpro.setValue(++value)
}
}
}
单例注册
static QJSValue singletontype_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
{
Q_UNUSED(engine)
static int nValue = 5;
QJSValue example = scriptEngine->newObject();
example.setProperty("nValue",nValue++);
return example;
}
int main(int argc, char *argv[])
{
...;
QQmlApplicationEngine engine;
qmlRegisterSingletonType("myJsvalueApi", 1, 0, "MyQmlFunction", singletontype_provider);
...;
}
import myJsvalueApi 1.0
Item {
Button{
id:btn
text: "1"
onClicked: {
text = MyQmlFunction.nValue;
}
}
Button{
id:btn1
onClicked: {
MyQmlFunction.nValue++;
}
}
}
信号使用
void QmlCpp::addValue(int nValue)
{
m_nValue += nValue;
emit valueChanged();
}
import myqml 1.0
Item {
QmlCpp{
id:qmlpro
value:111
onValueChanged:{
btn.text = qmlpro.value
}
}
Button{
id:btn
text: "1"
}
}
注册到元系统和注册到 qml 系统的区别
- 注册进元对象系统,需要在main.cpp中实现C++类的实例化。
- 注册进QML系统环境中,在QML文件中实现对C++类的实例化,并且可以直接接收C++发射过来的信号。
c++ 创建和获取 qml 对象
qml 使用 c++ 获取的 qml 对象
Window{
width: 200
height: 200
color: "red"
}
#include <QQmlComponent>
QQmlApplicationEngine engine;
...;
engine.load(url);
...;
QQmlComponent qmlComponent(&engine);
qmlComponent.loadUrl(QUrl(QStringLiteral("qrc:/QmlWindow.qml")));
QObject* qmlWindows = qmlComponent.create();
qmlWindows->setParent(engine.rootObjects()[0]);
engine.rootContext()->setContextProperty("qmlWindows",qmlWindows);
Window {
visible: true
width: 640
height: 480
title: qsTr("demo")
Button{ //Button 用于显示
id: viewBtn //按钮控件,唯一标识ID:getBtn
text: "显示" //按钮显示文字
width: 120 //按钮宽度
height: 40 //按钮高度
anchors.centerIn: parent //按钮放到窗口中心
onClicked: { //点击按钮事件;
qmlWindows.visible = true;
}
}
Button{ //Button 用于改变颜色
id: changeColorBtn //按钮控件,唯一标识ID:getBtn
text: "变色" //按钮显示文字
width: 120 //按钮宽度
height: 40 //按钮高度
anchors.top: viewBtn.bottom //按钮放到窗口中心
anchors.topMargin: 10
anchors.left: viewBtn.left
onClicked: { //点击按钮事件;
qmlWindows.color = "blue"
qmlWindows.visible = true;
}
}
}
cpp 直接控制 qml 对象
Window{
width: 200
height: 200
color: "red"
visible: false
Rectangle {
id: rectangle
objectName: "myRectangle"
width: 50
height: 50
anchors.centerIn: parent
color: "black"
}
QObject *rect = qmlWindow->findChild<QObject*>("myRectangle");
rect->setProperty("color", "white");
通过 C++ 创建的窗口对象的findChild 来在 qmlWindow 中寻找到名字为“myRectangle” 的控件。这里的QObject *rect 就是我们找到的Window.qml 中的 Rectangle; 然后通过设置 rect 设置属性来更改QML中 Rectangle 的颜色。
QML_ELEMENT
在Qt 6中,您可以通过使用QML_ELEMENT宏来实现C++的集成。此宏将封闭类型声明为QML中可用,使用其类或名称空间名称作为QML元素名称。要在C++头文件中使用这个宏,您必须添加qml.h头文件 #include <QtQml>
声明QML中可用的封闭类型或命名空间,将其类名或命名空间名作为QML元素名。
class Slider : public QObject
{
Q_OBJECT
QML_ELEMENT
...
}
CMakeLists
中添加:
qt6_add_qml_module(myapp
URI com.mycompany.qmlcomponents
VERSION 1.0
)
import com.mycompany.qmlcomponents 1.0
Slider {
// ...
}
插件
QML扩展插件提供了与C++集成的最灵活的方式。它允许您在第一个QML文件调用导入标识符时加载的插件中注册类型。您可以跨项目使用插件,这在构建复杂项目时非常方便。
C++类中调用QML方法
所有QML方法都公开给元对象系统,可以使用QMetaObject::invokeMethod()调用 QML。您可以指定参数的类型和之后的返回值。当您想要将C++中具有特定签名的信号连接到QML定义的方法时,这可能非常有用。如果省略这些类型,C++签名将使用QVariant。
import QtQuick
Item {
function qmlMethod(msg: string) : string {
console.log("Received message:", msg)
return "Success"
}
Component.onCompleted: {
console.log("Component created successfully.")
}
}
#include <QQmlComponent>
QQmlApplicationEngine engine;
QQmlComponent component(&engine, "qrc:/CustomItem.qml");
QObject *myObject = component.create();
QString retValue = "";
QString msg = "Message from C++";
QMetaObject::invokeMethod(myObject, "qmlMethod",
Q_RETURN_ARG(QString, retValue), Q_ARG(QString, msg));
qDebug() << "QML method returned:" << retValue;
delete myObject;
请注意,必须指定参数和返回类型。 基本类型和对象类型都可以作为类型名称。如果 QML 类型系统中没有提到该类型,那么您必须在调用 QMetaObject::invokeMethod 时将 QVariant 声明为带有 Q_RETURN_ARG() 和 Q_ARG() 的类型。 或者,如果不需要任何返回值,则可以仅使用两个参数调用 invokeMethod(),如下所示:
QMetaObject::invokeMethod(myObject, "qmlMethod");
向C++公开QML对象指针
有时,您可能希望通过C++修改QML对象的属性,例如修改控件的文本、更改控件的可见性或更改自定义属性。QML引擎允许您将QML对象注册为C++类型,这将自动公开QML对象的属性。
#ifndef CUSTOMOBJECT_H
#define CUSTOMOBJECT_H
#include <QObject>
#include <QVariant>
class CustomObject : public QObject
{
Q_OBJECT
public:
explicit CustomObject(QObject *parent = nullptr);
Q_INVOKABLE void setObject(QObject* object)
{
object->setProperty("text", QVariant("Clicked!"));
}
};
#endif // CUSTOMOBJECT_H
import QtQuick
import QtQuick.Window
import QtQuick.Controls
import MyCustomObject
Window {
width: 640; height: 480;
visible: true
title: qsTr("QML Object in C++")
CustomObject{
id: customObject
}
Button {
id: button
anchors.centerIn: parent
text: qsTr("Click Me!")
onClicked: {
customObject.setObject(button);
}
}
}
其他
如果想要更好的控制,对于某些自定义 QML 对象类型,某些情况可能需要延迟特定数据的初始化直到创建对象并设置其所有属性。例如初始化成本高,或者在所有属性值都已初始化之前不应执行初始化等情况。
Qt QML 模块提供的 QQmlParserStatus 类用于此目的。它定义了许多在组件实例化期间的各个阶段调用的虚拟方法。 要接收这些通知,C++ 类应该继承 QQmlParserStatus 并使用 Q_INTERFACES() 宏通知 Qt 元系统。
class MyQmlType : public QObject, public QQmlParserStatus
{
Q_OBJECT
Q_INTERFACES(QQmlParserStatus)
QML_ELEMENT
public:
void classBegin()
{
//QML中本类型对象创建完成,但是任何属性没有被赋值,可执行自定义操作
}
virtual void componentComplete()
{
//QML中对象已经完全创建,可执行自定义操作
}
};