Qt 5.12--QML and C++ 混编概述
1 简介
QML被设计为可通过C ++代码轻松扩展。 Qt QML模块中的类允许从C ++加载和处理QML对象,并且QML引擎与Qt的元对象系统集成的性质使C ++功能可以直接从QML调用。 这允许开发混合应用程序,该混合应用程序将QML,JavaScript和C ++代码混合在一起实现。
2 优缺点
2.1 优点
- QML和JavaScript实现用户界面功能,C++实现逻辑功能。
- 使用和调用QML中的某些C ++功能(例如,调用您的应用程序逻辑,使用以C ++实现的数据模型或调用第三方C ++库中的某些函数)
- 访问Qt QML或Qt Quick C ++ API中的功能(例如,使用QQuickImageProvider动态生成图像)
- 从C ++实现自己的QML对象类型-无论是在自己的特定应用程序中使用,还是分发给其他人
2.2 缺点
3 QML使用C++步骤
3.1 从QObject派生
为了向QML提供一些C ++数据或功能,必须从QObject派生的类中使它可用。 将C ++类型的属性公开给QML后,由于QML引擎已与元对象系统集成,因此可以从QML访问任何QObject派生类的属性,方法和信号。
- 从 QObject 或 QObject 的派生类继承
- 使用 Q_OBJECT 宏
3.2 注册方法
可以通过多种方式将C++功能类公开给QML。
QML引擎允许实例化和非实例化类型的注册。
注册可实例化的类型可使C ++类用作QML对象类型的定义,从而允许将其用于QML代码的对象声明中以创建此类型的对象。注册还为引擎提供了其他类型的元数据,使该类型(以及该类声明的任何枚举)可用作属性值,方法参数和返回值以及在QML和C ++之间交换的信号参数的数据类型。
3.2.1 注册方法概述
- C++类可以注册为可实例化的QML类型,因此可以像QML代码中的任何普通QML对象类型一样进行实例化和使用。
- C++类可以注册为单例类型,以便可以从QML代码导入该类的单个实例,从而允许从QML访问该实例的属性,方法和信号。
- C++类的实例可以作为上下文属性或上下文对象嵌入到QML代码中,从而允许从QML访问该实例的属性,方法和信号。
3.2.2 注册实例化对象类型
任何QObject派生的C ++类都可以注册为QML对象类型的定义。
一旦在QML类型系统中注册了一个类,就可以像声明QML代码中的任何其他对象类型一样声明和实例化该类。 一旦创建了一个类实例,就可以从QML中对其进行操作。 正如将C ++类型的属性暴露给QML所解释的那样,可以从QML代码访问任何QObject派生类的属性,方法和信号。
要将QObject派生的类注册为可实例化的QML对象类型,请调用qmlRegisterType()将该类作为QML类型注册到特定的类型名称空间中。 然后,可以导入该名称空间以使用该类型。
class Message : public QObject
{
Q_OBJECT
Q_PROPERTY(QString author READ author WRITE setAuthor NOTIFY authorChanged)
Q_PROPERTY(QDateTime creationDate READ creationDate WRITE setCreationDate NOTIFY creationDateChanged)
public:
// ...
};
qmlRegisterType<Message>("com.mycompany.messaging", 1, 0, "Message");
import com.mycompany.messaging 1.0
Message {
author: "Amelie"
creationDate: new Date()
}
3.2.3 注册非实例类型
不需要实例化的场景
- 无需实例化的接口
- 不需要公开给QML的基类
- 声明一些应从QML访问的枚举,不需要实例化
- 单例类型,公开给QML
注册非实例类型方法
- qmlRegisterType()(无参数)注册无法实例化且无法从QML引用的C ++类型。这使引擎能够强制执行可从QML实例化的所有继承类型。
- qmlRegisterInterface()注册现有的Qt接口类型。该类型不能从QML实例化,并且不能使用它声明QML属性。但是,从QML使用这种类型的C ++属性将执行预期的接口强制转换。
- qmlRegisterUncreatableType()注册一个命名的C ++类型,该类型不可实例化,但应标识为QML类型系统的类型。如果应从QML访问类型的枚举或附加属性,但该类型本身不可实例化,则此方法很有用。
- qmlRegisterSingletonType()注册可以从QML导入的单例类型,如下所述。
3.2.4 注册单例类型
单例类型使属性,信号和方法可以在名称空间中公开,而无需客户端手动实例化对象实例。特别是QObject单例类型是一种提供功能或全局属性值的高效便捷的方法。
请注意,单例类型没有关联的QQmlContext,因为它们在引擎中的所有上下文之间共享。 QObject单例类型实例由QQmlEngine构造和拥有,并且在销毁引擎时将销毁。
int qmlRegisterSingletonType(const char *uri, int versionMajor, int versionMinor, const char *typeName, QJSValue (*)(QQmlEngine *, QJSEngine *) callback)
3.2.5 注册上下文属性或上下文对象
setContextProperty 是将对象暴露给 QML,一般默认就是全局单例。
4 C++使用QML步骤
Qt QML模块还提供了从C ++代码进行反向操作和操作QML对象的方法。
警告:尽管可以从C ++访问QML对象并对其进行操作,但是除了测试和原型设计目的之外,不建议使用这种方法。
5 选择正确的混编方法
6 实际使用
6.1 暴露Qt C++的对象或类型给QML
6.1.1 创建需要暴露给QML的数据类型
#ifndef MYCLASS_H
#define MYCLASS_H
#include <QObject>
#include <QString>
class MyClass : public QObject
{
Q_OBJECT
Q_PROPERTY(QString myString READ myString WRITE setmyString NOTIFY myStringChanged)
public:
explicit MyClass(QObject *parent = 0);
Q_INVOKABLE QString getMyString();
signals:
void myStringChanged();
public slots:
void setmyString(QString aString);
QString myString();
private:
QString m_string;
};
#endif // MYCLASS_H
若你想数据元素中的方法可以被QML直接调用有2种方法:
- 在函数申明前添加 Q_INVOKABLE 宏。
- 申明成public slots。
QML可以直接访问改数据元素的属性,该属性由QPROPERTY所申明。
6.1.2 暴露已存在的Qt C++对象给QML
//main.cpp
MyClass myObj;
QDeclarativeEngine *engine=viewer.engine();
QDeclarativeContext *context=engine->rootContext();
context->setContextProperty("myObjectExposeByCXProperty", &myObj);
qml中可以直接使用myObjectExposeByCxProperty对象。
//mainpage.qml
...
Button{
...
id:btn1
...
text: qsTr("PROPERTY")
//此处调用myString为MyClass的QPROPERTY的属性不是方法,所以没有括号。
onClicked: label.text=myObjectExposeByCXProperty.myString;
}
...
6.1.3 注册Qt C++类类型给QML
//main.cpp
qmlRegisterType<MyClass>("RegisterMyType", 1, 0, "MyClassType");
QML中这样使用
//mainpage.qml
...
import RegisterMyType 1.0
Button{
id:btn2
...
text: qsTr("INOVKABLE")
//此处调用的时INVOKABLE的方法,不是属性,所以有括号。
onClicked: label.text=myclassExposeByRegType.getMyString();
}
//创建对象,由于QML是解释执行的,所以放后面也没什么关系。
MyClassType
{
id:myclassExposeByRegType
}
步骤:
1. 导入import。
2. 创建对象。
3. id直接使用。
6.2 QML中的Signal Handler
6.2.1 无参数
在qml中点击按钮控件,改变其中对象的字符串,这时候在Qt C++中发送一个signal信号给qml端,qml端接收到使用signal handler响应,改变label2的值。具体代码如下。
qml中修改string的值。
//mainpage.qml
Button{
id:btn3
text: qsTr("emit stringchanged signal")
onClicked: myObjectExposeByCXProperty.myString="xxxxx";
}
Qt C++触发信号
//myclass.cpp
void MyClass::setmyString(QString aString)
{
if(aString==m_string)
{
return;
}
m_string=aString;
emit myStringChanged();
}
连接signal handler响应
//mainpage.qml
Connections
{
target: myObjectExposeByCXProperty
onMyStringChanged:label2.text="Signal handler received"
}
6.2.2 有参数
把你的Qt C++中的对象暴露给QML端,然后利用signals-slots 进行连接,并传递消息。
- 创建C++类
#include<QObject>
class NetConnectController : public QObject
{
Q_OBJECT
Q_PROPERTY(int status READ status WRITE setStatus NOTIFY statusChanged)
public:
explicit NetConnectController(QObject *parent = 0);
void Ready()
{
emit statusChanged( m_status);
}
signals:
void statusChanged(int aStatus);
private:
int status() const;
void setStatus(int aStatus);
private :
//表示网络不同的状态
int m_status;
};
- 暴露对象给QML
.....
NetConnectController netController
QDeclarativeEngine * engine = viewer.engine();
(engine->rootContext())->setContextProperty("NetController",&netController);
.....
3在QML中连接Signal-slot
......
Connections
{
target: NetController
onStatusChanged:changeStatus(aStatus)//Call JS Function
}
......
注意:上面的onStatusChanged 命名格式 “on”+"Qt C++中的signal名字"。在QML端可以直接使用Qt C++端的参数。例如上面的"aStatus"。
6.3 C++中enum注册到QML使用
枚举定义不能使用typedef
Qt C++与QML混合编程(2)- QML中使用C++的枚举、结构体、列表类型的函数
参考
1、Overview - QML and C++ Integration
2、QML与C++混合编程详解
3、Defining QML Types from C++
4、QML与C++交互
5、QT开发(六十九)——QML与C++混合编程
6、QML与Qt C++ 交互机制探讨与总结
7、good–Qt Quick 之 QML 与 C++ 混合编程详解
8、Qt C++与QML混合编程(2)- QML中使用C++的枚举、结构体、列表类型的函数