Qt移动应用开发:QML与C++的交互
上一篇讲到了在Qt Quick中实现场景切换的一种可能的方法,场景切换是诸如游戏等应用在内必须要面临的技术难点,所以场景切换并没有通行的方法,根据自己的使用习惯进行设计即可。
本文主要介绍的是如何使用QML和C++进行交互,难度稍微偏大,适合有经验的Qt开发者进行学习交流。
Qt 5吸收了Qt 4的declarative模块的优点,对底层进行了更改,新建了QPA层,隔离了不同操作系统API和上层Qt代码,同时QML/QtQuick也可以顺利在不同平台上运行。另外由于考虑到让Qt程序接入不同的库函数,因此Qt开放了接口让QML层和C++代码进行交互。之前已经有较多介绍QML与C++交互的文章了,本文仅作为一种有益的补充,更多相关的知识可以查询Qt帮助文档或向我留言。
本文的例子在Qt 5.3.1中顺利编译运行通过。
原创文章,反对未声明的引用。原博客地址:http://blog.csdn.net/gamesdev/article/details/37359873
首先一个较为简单的方法就是注册上下文属性(Context Property),让QML访问C++的变量。代码如下:
- #include <QApplication>
- #include <QQmlApplicationEngine>
- #include <QQmlContext>
- int main(int argc, char *argv[])
- {
- QApplication app(argc, argv);
- QQmlApplicationEngine engine;
- engine.rootContext( )->setContextProperty(
- "Greeting",
- QObject::tr( "Hello QML from C++" ) );
- engine.load(QUrl(QStringLiteral("qrc:///main.qml")));
- return app.exec();
- }
然后在QML中简单地调用”Greeting”变量名就可以顺利访问了。
- import QtQuick 2.2
- import QtQuick.Controls 1.1
- ApplicationWindow
- {
- visible: true
- width: 640
- height: 480
- title: qsTr("测试QML于C++的交互")
- menuBar: MenuBar
- {
- Menu
- {
- title: qsTr("文件")
- MenuItem
- {
- text: qsTr("退出")
- onTriggered: Qt.quit( );
- }
- }
- }
- Text
- {
- text: qsTr("本例用来测试QML和C++的交互")
- anchors.right: parent.right
- anchors.bottom: parent.bottom
- }
- Text
- {
- text: Greeting
- anchors.centerIn: parent
- }
- }
演示程序的截图如下:
本例重要的部分是QQmlContext实例指针。它通过QQmlApplicationEngine::rootContext()来获得,也可以通过QQmlApplicationEngine:: contextForObject(constQObject * object)来获得。在QQmlObject创建的时候,都会实例化一个QQmlContext,用来支持为运行环境提供的上下文属性。
使用上下文属性可以让QML访问C++数据,那么如何使用QML来访问C++的函数呢?这里我们在C++中注册QML类或者单例来让QML来获得访问C++函数的机会。首先介绍一下如何将QML中注册C++类到QML中。首先需要定义一个C++类继承于QObject,然后这么写:
- #ifndef CPLUSPLUSCLASS_H
- #define CPLUSPLUSCLASS_H
- #include <QObject>
- class CPlusPlusClass: public QObject
- {
- Q_OBJECT
- Q_PROPERTY( int rating READ rating )
- public:
- explicit CPlusPlusClass( QObject* pParent = Q_NULLPTR ):
- QObject( pParent )
- {
- m_Rating = 5;
- }
- Q_INVOKABLE void method( void )
- {
- qDebug( "[C++]%s is called.", __FUNCTION__ );
- }
- int rating( void ) { return m_Rating; }
- private:
- int m_Rating;
- };
- #endif // CPLUSPLUSCLASS_H
然后再main.cpp中需要调用qmlRegisterType()模板函数来注册C++类到QML中,一个典型的用法如下:
- #include <QApplication>
- #include <QQmlApplicationEngine>
- #include <QtQml>
- #include "CPlusPlusClass.h"
- int main(int argc, char *argv[])
- {
- QApplication app(argc, argv);
- // 首先注册一下类
- qmlRegisterType<CPlusPlusClass>(
- "CPlusPlus.Test", // 统一资源标识符
- 1, // 主版本
- 0, // 次版本
- "CPlusPlusType" ); // QML类名称
- QQmlApplicationEngine engine;
- engine.load(QUrl(QStringLiteral("qrc:///main.qml")));
- return app.exec();
- }
最后在QML中就可以顺利地访问C++类的属性和方法了:
- import QtQuick 2.2
- import QtQuick.Controls 1.1
- import CPlusPlus.Test 1.0
- ApplicationWindow
- {
- visible: true
- width: 640
- height: 480
- title: qsTr("测试QML于C++的交互")
- menuBar: MenuBar
- {
- Menu
- {
- title: qsTr("文件")
- MenuItem
- {
- text: qsTr("退出")
- onTriggered: Qt.quit( );
- }
- }
- }
- Text
- {
- text: qsTr("本例用来测试QML和C++的交互")
- anchors.right: parent.right
- anchors.bottom: parent.bottom
- }
- CPlusPlusType
- {
- id: theType
- }
- MouseArea
- {
- anchors.fill: parent
- onClicked:
- {
- console.log( "[qml] Rating is: " + theType.rating );
- theType.method( );
- }
- }
- }
点击窗体,控制台运行结果如下:
qml: [qml] Ratingis: 5
[C++]method iscalled.
如果不想在QML和C++环境中创建多个QObject或者说想要更加方便地访问C++的方法,那么可以考虑注册一个单例类,注册单例类和注册普通的类差不多,但也有一些显著的区别,首先建立这样一个继承于QObject的类,代码如下:
- #ifndef CPLUSPLUSCLASS_H
- #define CPLUSPLUSCLASS_H
- #include <QObject>
- class CPlusPlusClass: public QObject
- {
- Q_OBJECT
- Q_PROPERTY( int rating READ rating )
- public:
- explicit CPlusPlusClass( QObject* pParent = Q_NULLPTR ):
- QObject( pParent )
- {
- m_Rating = 5;
- }
- Q_INVOKABLE void method( void )
- {
- qDebug( "[C++]%s is called.", __FUNCTION__ );
- }
- int rating( void ) { return m_Rating; }
- private:
- int m_Rating;
- };
- #endif // CPLUSPLUSCLASS_H
然后关键在main.cpp中,除了调用qmlRegisterSingletonType()模板函数外,还需要写一个静态全局的注册函数。代码如下:
- #include <QApplication>
- #include <QQmlApplicationEngine>
- #include <QtQml>
- #include "CPlusPlusSingleton.h"
- // 注册单例函数
- static QObject* CPlusPlusSingletonRegisterFunc(
- QQmlEngine* pQMLEngine,
- QJSEngine* pJSEngine )
- {
- Q_UNUSED( pQMLEngine );
- Q_UNUSED( pJSEngine );
- CPlusPlusSingleton* pSingleton = new CPlusPlusSingleton;
- return pSingleton;
- }
- int main(int argc, char *argv[])
- {
- QApplication app(argc, argv);
- // 首先注册一下单例
- qmlRegisterSingletonType<CPlusPlusSingleton>(
- "CPlusPlus.Test", // 统一资源标识符
- 1, // 主版本
- 0, // 次版本
- "CPlusPlusSingleton", // 单例名称
- CPlusPlusSingletonRegisterFunc ); // 函数名
- QQmlApplicationEngine engine;
- engine.load(QUrl(QStringLiteral("qrc:///main.qml")));
- return app.exec();
- }
就这样,C++的部分就完成了。接下来在QML中就很简单了:
- import QtQuick 2.2
- import QtQuick.Controls 1.1
- import CPlusPlus.Test 1.0
- ApplicationWindow
- {
- visible: true
- width: 640
- height: 480
- title: qsTr("测试QML于C++的交互")
- menuBar: MenuBar
- {
- Menu
- {
- title: qsTr("文件")
- MenuItem
- {
- text: qsTr("退出")
- onTriggered: Qt.quit( );
- }
- }
- }
- Text
- {
- text: qsTr("本例用来测试QML和C++的交互")
- anchors.right: parent.right
- anchors.bottom: parent.bottom
- }
- MouseArea
- {
- anchors.fill: parent
- onClicked:
- {
- console.log( "[qml] Rating is: " + CPlusPlusSingleton.rating );
- CPlusPlusSingleton.method( );
- }
- }
- }
点击窗体,控制台结果如下:
qml: [qml] Ratingis: 5
[C++]method iscalled.
大家可以根据需要选择是否在C++中注册QML类和注册C++单例来获得相对应的特性。
在我的第一款独立游戏《吃药了》中,为了顺利地接入广告SDK,需要写C++代码来保证让QML能够访问到C++的函数,广告显示效果如下: