概述
QML 其实是对 JavaScript 的扩展,融合了 Qt Object 系统,它是一种新的解释型的语言, QML 引擎虽然由 Qt C++ 实现,但 QML 对象的运行环境,说到底和 C++ 对象的上下文环境是不同的,是平行的两个世界。如果你想在 QML 中访问 C++ 对象,那么必然要找到一种途径来在两个运行环境之间建立沟通桥梁。
Qt 提供了两种在 QML 环境中使用 C++ 对象的方式:
- 在 C++ 中实现一个类,注册到 QML 环境中, QML 环境中使用该类型创建对象
- 在 C++ 中构造一个对象,将这个对象设置为 QML 的上下文属性,在 QML 环境中直接使用改属性
不管哪种方式,对要导出的 C++ 类都有要求,不是一个类的所有方法、变量都可以被 QML 使用。
实现可以导出的 C++ 类
前提条件
- 从 QObject 或 QObject 的派生类继承
- 使用 Q_OBJECT 宏
好像和使用信号与槽的前提条件一样……没错,的确是一样的。这两个条件是为了让一个类能够进入 Qt 强大的
元对象系统
(meta-object system)中,只有使用元对象系统,一个类的某些方法或属性才可能通过字符串形式的名字来调用,才具有了在 QML 中访问的基础条件。
什么特征的属性或方法才可以被 QML 访问呢?
信号,槽
只要是信号或者槽,都可以在 QML 中访问,你可以把 C++ 对象的信号连接到 QML 中定义的方法上,也可以把 QML 对象的信号连接到 C++ 对象的槽上,还可以直接调用 C++ 对象的槽或信号。
// ColorMaker 类的声明
class ColorMaker : public QObject
{
Q_OBJECT
public:
ColorMaker(QObject *parent = 0);
~ColorMaker();
signals:
void colorChanged(const QColor & color);
void currentTime(const QString &strTime);
public slots:
void start();
void stop();
};
定义了 start() / stop() 两个槽, colorChanged() / currentTime() 两个信号,都可以在 QML 中使用。
Q_INVOKABLE 宏
在定义一个类的成员函数时使用 Q_INVOKABLE 宏来修饰,就可以让该方法被元对象系统调用。这个宏必须放在返回类型前面。
// 给 ColorMaker 添加了两个使用 Q_INVOKABLE 宏修饰的方法
class ColorMaker : public QObject
{
Q_OBJECT
public:
ColorMaker(QObject *parent = 0);
~ColorMaker();
Q_INVOKABLE GenerateAlgorithm algorithm() const;
Q_INVOKABLE void setAlgorithm(GenerateAlgorithm algorithm);
signals:
void colorChanged(const QColor & color);
void currentTime(const QString &strTime);
public slots:
void start();
void stop();
};
在 QML 中可以用 {Object}.{method} 来访问。
colorMaker 的 main.qml 中有使用 algorithm() 和 setAlgorithm() 的 QML 代码 :
// 在根元素创建完成时初始化 colorMaker 对象,给它设定了颜色生成算法,同时改变界面上切换颜色生成算法的按钮的文字。
Component.onCompleted: {
colorMaker.color = Qt.rgba(0,180,120, 255);
colorMaker.setAlgorithm(ColorMaker.LinearIncrease);
changeAlgorithm(colorAlgorithm, colorMaker.algorithm());
}
Q_ENUMS
在 QML 中使用枚举类型,可以使用 Q_ENUMS 宏将该枚举注册到元对象系统中。
// ColorMaker 类定义了 GenerateAlgorithm 枚举类型,支持 RandomRGB / RandomRed 等颜色生成算法。
class ColorMaker : public QObject
{
Q_OBJECT
Q_ENUMS(GenerateAlgorithm)
public:
ColorMaker(QObject *parent = 0);
~ColorMaker();
enum GenerateAlgorithm{
RandomRGB,
RandomRed,
RandomGreen,
RandomBlue,
LinearIncrease
};
Q_INVOKABLE GenerateAlgorithm algorithm() const;
Q_INVOKABLE void setAlgorithm(GenerateAlgorithm algorithm);
signals:
void colorChanged(const QColor & color);
void currentTime(const QString &strTime);
public slots:
void start();
void stop();
};
在 QML 中可以用 {CLASS_NAME}.{ENUM_VALUE} 的形式来访问,比如 ColorMaker.LinearIncrease ,上节展示的 QML 代码片段已经使用了导出的枚举类型。
Q_PROPERTY
Q_PROPERTY 宏用来定义可通过元对象系统访问的属性,通过它定义的属性,可以在 QML 中访问、修改,也可以在属性变化时发射特定的信号。要想使用 Q_PROPERTY 宏,你的类必须是 QObject 的后裔,必须在类首使用 Q_OBJECT 宏。
Q_PROPERTY 宏的原型:
Q_PROPERTY(type name
(READ getFunction [WRITE setFunction] |
MEMBER memberName [(READ getFunction | WRITE setFunction)])
[RESET resetFunction]
[NOTIFY notifySignal]
[REVISION int]
[DESIGNABLE bool]
[SCRIPTABLE bool]
[STORED bool]
[USER bool]
[CONSTANT]
[FINAL])
看一个最简短的属性声明:
Q_PROPERTY(int x READ x)
上面的声明定义了一个类型为 int 名为 x 的属性,通过方法 x() 来访问。
type name 这两个字段想必不用细说了吧? type 是属性的类型,可以是 int / float / QString / QObject / QColor / QFont 等等, name 就是属性的名字。
在实际使用中,很少能够用全 Q_PROPERTY 的所有选项,就往 QML 导出类这种场景来说,比较常用的是 READ / WRITE / NOTIFY 三个选项。
- READ 标记,如果你没有为属性指定 MEMBER 标记,则 READ 标记必不可少;声明一个读取属性的函数,该函数一般没有参数,返回定义的属性。
- WRITE 标记,可选配置。声明一个设定属性的函数。它指定的函数,只能有一个与属性类型匹配的参数,必须返回 void 。
- NOTIFY 标记,可选配置。给属性关联一个信号(该信号必须是已经在类中声明过的),当属性的值发生变化时就会触发该信号。信号的参数,一般就是你定义的属性。
// 给 ColorMaker 类添加一些属性,以便 QML 可以获取、设置颜色值。
class ColorMaker : public QObject
{
Q_OBJECT
Q_ENUMS(GenerateAlgorithm)
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
Q_PROPERTY(QColor timeColor READ timeColor)
public:
ColorMaker(QObject *parent = 0);
~