前言
最近开始做新的项目开发,准备采用Qt框架,使用QML作为前端框架,用C++编写底层结构和算法,接触QML时间不长,用此篇文章记录一下C++代码与QML之间的交互方法,以供日后回顾查看与温习。
在我们新创建一个QuickApplication项目之后,我们的项目文件中存在两个初始文件,一个是main.cpp,另一个是main.qml。
图1 main.cpp文件
图2 main.qml文件
在main.cpp文件中我们可以看到两个不同类型的对象,一个是QGuiApplication类对象,一个是QQmlApplicationEngine类对象,这两个类都是Qt框架中用于处理应用程序生命周期和QML集成的类,但它们在Qt的不同模块中使用,并且有不同的用途和特点。
QGUIApplication
是Qt GUI模块中的一个类,主要用于传统的Qt Widgets应用程序。它继承自
QApplication
类,并添加了一些特定于图形用户界面应用程序的功能。以下是
QGUIApplication
的一些关键特点:
- 管理应用程序的生命周期,包括初始化和退出。
- 处理事件循环,响应用户输入和系统事件。
- 可以与QML集成,但通常用于非QML的Widgets应用程序。
QQmlApplicationEngine
是Qt QML模块中的一个类,专门为QML应用程序设计。它提供了一个用于加载和执行QML代码的高级API。以下是
QQmlApplicationEngine
的一些关键特点:
- 用于加载和运行QML文件。
- 支持动态QML内容的加载,可以在运行时加载和卸载QML组件。
- 提供了与QML集成的高级功能,如JavaScript和C++的绑定。
- 可以访问QML中的属性和方法,实现QML与C++的双向通信。
QML和C++代码的通讯都是通过Qt Meta-Object System系统实现的,例如信号和槽的连接,属性的获取和修改、C++的槽函数或者被标记为Q_INVOKABLE的方法可以直接被QML调用以及QML可以直接获取Q_ENUM的枚举对象等。
因此QML和C++代码的整合可以通过几种方式实现:
- 通过上下文属性,利用setContextProperty()方法;
- 在qml引擎中注册类型,通过在main.cpp中调用qmlRegisterType
- 使用QML扩展插件
接下来我们来介绍几种C++与QML交互方法
一、SetContextProperty()方法
实现通讯的最重要的一点是:QObjects对象需要被显示地暴露给QML运行时的上下文(QObjects(and value type objects) need to be explicitly exposed to the QML runtime context in order to be accesible),这其中最重要的关键类是QQmlContext。
- 在main.cpp文件的main函数中,我们创建一个QQmlContext对象context,并调用rootContext()指向了engine的根上下文,这个上下文包含了QML代码可以访问的所有初始属性和对象;
- 调用context的成员函数setContextProperty(),将自定义的属性暴露给context;
- 之后在main.qml文件中就可以使用qml创建对象,并使用刚才暴露给context的属性“myBackgroundColor"和“myText"的值
void **QQmlContext::setContextProperty(*const QString &name, QObject value)*方法会传入两个参数,一个是自定义属性的名称,一个是自定义属性的值,这个值是一个QObject对象,因此常见的类型都可以直接传入。
例如上图所示,你可以将QGuiApplication对象的指针传入,这样就可以在qml中调用相关的函数。
这个方法会作用于全局,一般来说用到的情况比较少
二、Q_PROPERTY()宏与qmlRegisterSingletonInstance()
上面提到了用setContextObject()方法将属性直接暴露给context,但是这种方式并不安全,也不便于内存管理,因此可以通过qt提供的另一种方法,将C++类暴露给QML进行使用。
首先,我们先创建一个自定义的类MyDataSet用来暴露给QML使用:
为了让该类与QML进行通讯,我们需要做一下几件事情:
- 在类中声明Q_OBJECT宏,并继承QObject类,该宏非常重要,是qt框架元对象系统meta-object system的入口,能够让Qt的构建系统知道该类需要元对象编译器(moc:meta-object compiler)进行特殊处理;
- 使用Q_PROPERTY宏创建需要暴露给QML的属性,该宏需要定义: - 属性类型和名称,读取器名称,写入器名称,属性改变通知信号名称。- 这其中还有一个特殊的关键字MEMBER,该关键字允许你为该属性设置一个字段,qt会自动生成读写该字段的读取器和写入器,建议不要与READ和WRITE一起使用,因此建议用法如下: - READ + WRITE,可读写,自己实现读写器- READ,只读,自己实现读取器- MEMBER,可读写,指定字段,自动实现读写器
完成自定义类和Q_PROPERTY的编写之后,我们就可以进行注册:
这里我们使用qmlRegisterSingletonInstance()方法,在qml中注册一个单例对象dataSet, 这样注册有以下的优点:
- 集中管理: 单例模式确保了一个类只有一个实例,并提供一个全局访问点。这使得状态和资源可以跨多个组件或对象共享。
- 生命周期控制: 单例的生命周期与应用程序的生命周期保持一致,不需要手动创建和销毁,降低了内存泄漏的风险。
- QML 访问: 通过注册单例,你可以在 QML 中直接访问该单例的属性和方法,而无需担心对象的创建和销毁。
- 类型安全: QML 引擎会在编译时检查单例的使用,确保只有正确的类型被访问,提高了代码的健壮性。
- 避免重复实例化: 单例确保了在整个应用程序中只有一个实例,避免了重复创建相同功能的多个实例。
- 跨组件共享: 单例可以在不同的组件之间共享,使得状态和行为可以在应用程序的多个部分中保持一致。
- 简化 QML 代码: 在 QML 中使用单例,可以避免复杂的对象创建和传递,简化了 QML 代码。
- 性能优化: 由于单例是在应用程序启动时创建的,并且持续存在,因此避免了每次需要时都创建新实例的开销。
- 更好的封装性: 单例模式隐藏了实现细节,只暴露出一个访问点,使得内部实现可以在不影响使用者的情况下进行更改。
- 易于维护和扩展: 当需要添加新功能或修改现有功能时,单例提供了一个清晰的路径来扩展或修改应用程序的行为。
注册完成之后我们就可以在QML中使用我们所注册的对象了:
版权归原作者 MajorTom33 所有, 如有侵权,请联系我们删除。