# 理解QML运行时

运行QML时,它是在运行时环境中执行的。 运行时在QtQml模块中由C++语言实现。 运行时由引擎(负责执行QML)、上下文(保存每个组件可访问的全局属性)和组件(可以在QML中实例化的QML元素)组成。

#include <QtGui>
#include <QtQml>

int main(int argc, char **argv)
{
    QGuiApplication app(argc, argv);
    QUrl source(QStringLiteral("qrc:/main.qml"));
    QQmlApplicationEngine engine;
    engine.load(source);
    return app.exec();
}

在示例中,QGuiApplication封装了与应用程序实例相关的所有内容(例如:应用程序名称、命令行参数、管理事件循环)。 QQmlApplicationEngine管理上下文和组件的层次顺序,它需要加载一个典型的QML文件作为应用程序的起始点。 在这种情况下,它是一个main.qml,它包含一个窗口和一个文本。

提示

通过QmlApplicationEngine加载以简单的Item作为根类型的main.qml文件将不会在显示器上显示任何内容,因为它需要一个窗口来管理用于渲染的外观。 该引擎能够加载不包含任何用户界面(例如,普通对象)的QML代码。 因此,默认情况下它不会创建窗口。 qml运行时将在内部首先检查主QML文件是否包含一个窗口作为根项,如果没有,则它则创建一个,并将根项设置为新创建窗口的子项。

import QtQuick 2.5
import QtQuick.Window 2.2

Window {
    visible: true
    width: 512
    height: 300

    Text {
        anchors.centerIn: parent
        text: "Hello World!"
    }
}

在QML文件中,声明依赖项QtQuickQtQuick.Window。 这些声明将触发在导入路径中查找这些模块,在查找成功后由引擎加载所需的插件。 然后,新加载的类型将通过一个声明提供给QML环境,这个声明在代表汇总的qmldir文件中。

也可以通过将类型直接添加到main.cpp文件的引擎来简化插件的创建。 这里假设有一个CurrentTime类,它是一个基于QObject的类。

QQmlApplicationEngine engine();

qmlRegisterType<CurrentTime>("org.example", 1, 0, "CurrentTime");

engine.load(source);

现在还可以在QML文件中使用CurrentTime类型。

import org.example 1.0

CurrentTime {
    // access properties, functions, signals
}

如果不需要从QML中实例化新类,可以使用上下文属性将C++对象暴露给QML,例如:

QScopedPointer<CurrentTime> current(new CurrentTime());

QQmlApplicationEngine engine();

engine.rootContext().setContextProperty("current", current.value())

engine.load(source);

提示

不要混淆setContextProperty()setProperty()setContextProperty()是在qml的上下文中设置上下文属性;而setProperty()是在QObject上设置动态属性值,这里需要的并不是它。

现在,可以在应用程序的任何地方使用current属性。 由于上下文的继承关系,它在QML代码中随处可用。 current对象注册在最外层的根上下文中,因此它能在任何地方都被继承。

import QtQuick
import QtQuick.Window

Window {
    visible: true
    width: 512
    height: 300

    Component.onCompleted: {
        console.log('current: ' + current)
    }
}

以下是通常扩展QML的不同方法:

  • 上下文属性(Context properties) - setContextProperty()
  • 使用引擎注册类型(Register type with engine) - 在main.cpp中调用qmlRegisterType
  • QML扩展插件(QML extension plugins) - 灵活性最高,将在下面讨论

上下文属性(Context properties) 容易应用到小型应用程序中。它们不需要花费太多努力,只需使用某种全局对象暴露系统API即可。确保没有命名冲突会很有用(例如,为此使用特殊字符($),例如$.currentTime)。 $是在JS变量中是有效字符。

注册QML类型(Registering QML types) 允许用户在QML中控制C++对象的生命周期。这在使用上下文属性的方式中是不可能完成的。此外,它不会污染全局的命名空间。这种方法仍然需要首先注册所有类型,因此,所有库都需要在应用程序启动时链接,这在大多数情况下并不是真正的问题。

QML扩展插件(QML extension plugins) 提供了最灵活的系统。它们允许在第一个调用导入标识符的QML文件时加载的插件中注册类型。此外,通过使用QML单例,不再需要污染全局命名空间。插件允许跨项目重用模块,这在使用Qt执行多个项目时非常方便。

返回去看简单示例main.qml文件:

import QtQuick 2.5
import QtQuick.Window 2.2

Window {
    visible: true
    width: 512
    height: 300

    Text {
        anchors.centerIn: parent
        text: "Hello World!"
    }
}

当导入QtQuickQtQuick.Window时,所做的就是告诉QML运行时找到相应的QML扩展插件并加载它们。 这是由QML引擎通过在QML导入路径中查找这些模块来完成的。 然后,新加载的类型将可用于QML环境。

本章剩余部分将重点介绍QML扩展插件。 它们提供了最大的灵活性和重用性。

最后更新: 2/10/2022, 8:12:32 PM