QML与C++集成<二>——<使用C++属性及注册QML类型>
前言
在开始讲之前首先讲一个使用属性(setContextProperty)和注册类型(qmlRegisterType)的区别,在这主要讲一些我个人工作中的情况,其实二者都是将c++类暴露给QML的方法,只不过在使用时存在一些区别,根据使用方式不同我个人分为C++的形式和QML的定义形式。
1、C++定义方式(主要使用setContextProperty()函数)
- a)、比如我们有一个功能单一的Configure类,我们需要把它暴露给QML,在使用之前必须要先创建类对象m_configuration,就是说类实例化一次,QML中可以直接使用这个类,注意功能单一的类只适合该方式;
- b)、比如我们的业务比较复杂,我们有很多类,若要供QML调用,我们就要写一个总的被调用类Complex(包含所有的业务类),然后实例化一次这个Complex,然后QML中直接使用实例化后的对象;
两种业务方式的使用方式如下:
QQuickView viewer;
viewer.rootContext()->setContextProperty("_configuration",&m_configuration);
_configuration便可直接在qml中使用,_configuration自然也是一个全局变量。
2、QML的方式,(主要使用qmlRegisterType()函数)
该方式都是使用在业务复杂情况下,还是上面的例子,我们有一堆业务类,这个时候我们使用注册的方式,用在QML中定义的方式去定义各个实例,也就不用再需要一个总类:
qmlRegisterType<Foo>("App", 1, 0, "Foo"); qmlRegisterType<Bar>("Bar", 1, 0, "Bar");//注册不可实例化的类型
我们可以再QML中直接使用Foo去定义实例:
import App 1.0 Foo { bar.baz: "abc" Component.onCompleted: print(bar.baz) }
3、二者比较
与C++方式相比,QML具有如下优势:
-
变量名前面可以加$(
全局变量可用),
从而方便区分全局变量和局部变量,这个在C++定义属性的时候是不允许的; -
如果某个全局变量(一般是QML对象)构造很慢,可以通过QML中的
Loader
来很方便异步构造,从而加速程序启动:
一、在QML中使用C++属性
QObject子类的所有属性都能够被QML访问,QObject子类使用Q_PROPERTY宏定义一个属性,该宏的作用是向Qt元对象系统注册类的属性,一个类的属性就是类的数据成员,通常会有一个用于读取的READ函数和一个可选的用于修改的WRITE函数。
该宏定义如下:
一些常见的申明示例:
使用我们一般使用setContextProperty()函数,注意和注册的区别,使用一版如下,m_configuration就是一个全局变量,便可在qml中直接使用
QQuickView viewer; viewer.rootContext()->setContextProperty("_configuration",&m_configuration);
1.1 使用函数和槽
QML可以有条件地访问QObject子类的函数:
- 使用Q_INVOKABLE宏标记的public函数
Q_INVOKABLE bool postMessage(const QString &msg){ qDebug()<<"postMessage"; }
- public槽函数
public slot: void refresh() { qDebug()<"refresh"; }
1.2 使用信号
QML代码可以使用QObject子类的任意public信号,QML引擎会为每一个来自QObject子类的信号自动创建一个信号处理器,其命名规则如为:on<Signal>,其中<Signal>为信号的名字,首字母要大写,信号传递的参数通过其名字在信号处理器中使用。
注意:QML中的信号与前面提到的函数重载不同,如果C++类中具有参数列表不同的多个同名信号,但只有最后一个信号才能被QML访问到。
二、注册QML类型
QObject子类可以注册到QML类型系统中,以便在QML程序中作为一个数据类型使用,前面提到的所有操作数据的基础就是先注册Qml类型。可被注册的类分为可实例化和不可实例化两种,注册可实例化的类意味着这个类定义为一个QML对象类型,QML对象类型通过这种注册能够获取这种类型的元数据,以及相关属性信号等操作,注册不可实例化的C++类,意味着这种类型不可实例化,比如我们要把枚举类型暴露给QML,但这个类型本身不需要被实例化。
2.1 注册可实例化对象类型
QObject子类都可以注册为QML对象类型,注册成功后这个类就可以在QML代码中像其他类型一样声明和初始化,创建成功后就可以在QML中使用其属性值、函数和信号等特征,注册QObject子类,需要使用qmlRegisterType()函数,下面有两个类Bar,Foo:
class Bar : public QObject { Q_OBJECT Q_PROPERTY(QString baz READ baz WRITE setBaz NOTIFY bazChanged) public: Bar() {} QString baz() const { return mBaz; } void setBaz(const QString &baz) { if (baz == mBaz) return; mBaz = baz; emit bazChanged(); } signals: void bazChanged(); private: QString mBaz; }; class Foo : public QObject { Q_OBJECT Q_PROPERTY(Bar *bar READ bar CONSTANT FINAL) public: Foo() {} Bar *bar() { return &mBar; } private: Bar mBar; };
我们使用qmlRegisterType将其注册到QML系统类型,该函数需要一个命名空间和版本号:
qmlRegisterType<Foo>("App", 1, 0, "Foo"); qmlRegisterType<Bar>();//注册不可实例化的类型
在QML中使用:
import App 1.0
Foo { bar.baz: "abc" Component.onCompleted: print(bar.baz) }
2.2 注册不可实例化对象类型
有时需要注册一个不可实例化的对象类型,比如一个C++类:
- 是接口类型;
- 一个基类,不需要通过QML代码访问;
- 仅仅提供一些有用的枚举;
- 是一个单例,只能使用其唯一的实例,不应该从QML进行实例化。
注册方法:
- 使用无参的qmlRegisterType()函数;
- 使用qmlRegisterInterface()注册指定QML类型名称的Qt接口类型;
- 使用qmlRegisterUncreatableType()函数
- 使用qmlRegisterSingletonType()函数