Qt6 QML Book/扩展QML/理解QML运行时

Understanding the QML Run-time

理解QML运行时

When running QML, it is being executed inside of a run-time environment. The run-time is implemented in C++ in the QtQml module. It consists of an engine, responsible for the execution of QML, contexts, holding global properties accessible for each component, and components - QML elements that can be instantiated from 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();
}

In the example, the QGuiApplication encapsulates all that is related to the application instance (e.g. application name, command line arguments and managing the event loop). The QQmlApplicationEngine manages the hierarchical order of contexts and components. It requires typical a QML file to be loaded as the starting point of your application. In this case, it is a main.qml containing a window and a text type.

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

TIP

Loading a main.qml with a simple Item as the root type through the QmlApplicationEngine will not show anything on your display, as it requires a window to manage a surface for rendering. The engine is capable of loading QML code which does not contain any user interface (e.g plain objects). Because of this, it does not create a window for you by default. The qml runtime will internally first check if the main QML file contains a window as a root item and if not create one for you and set the root item as a child to the newly created window.

main.qml中,通过QmlApplicationEngine简单地将Item作为根类型加载,将不会在显示器上显示任何内容,因为它需要一个窗口来管理要渲染的曲面。引擎能够加载不包含任何用户界面(例如普通对象)的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!"
    }
}

In the QML file we declare our dependencies here it is QtQuick and QtQuick.Window. These declarations will trigger a lookup for these modules in the import paths and on success will load the required plugins by the engine. The newly loaded types will then be made available to the QML environmetn through a declaration in a a qmldir file representing the report.

在QML文件中,我们在这里声明了我们的依赖关系,它是QtQuick和QtQuick.Window。这些声明将触发在导入路径中查找这些模块,一旦成功,引擎将加载所需的插件。然后,新加载的类型将通过qmldir文件中的声明提供给QML环境。

It is also possible to shortcut the plugin creation by adding our types directly to the engine in our main.cpp. Here we assume we have a CurrentTime, which is a class based on the QObject base class.

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

QQmlApplicationEngine engine();

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

engine.load(source);

Now we can also use the CurrentTime type within our QML file.

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

import org.example 1.0

CurrentTime {
    // access properties, functions, signals
}

If we don't need to be able to instantiate the new class from QML, we can use context properties to expose C++ objects into QML, e.g.

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

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

QQmlApplicationEngine engine();

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

engine.load(source);

TIP

Do not mix up setContextProperty() and setProperty(). The first one sets a context property on a qml context, and setProperty() sets a dynamic property value on a QObject and will not help you.

不要混淆setContextProperty()和setProperty()。第一种方法在qml上下文上设置上下文属性,而setProperty()在QObject上设置动态属性值,对您没有帮助。

Now you can use the current property everywhere in your application. It is availabe everywhere in the QML code thanks to context inheritance. The current object is registered in the outermost root context, which is inherited everywhere.

现在,您可以在应用程序中的任何地方使用当前属性。由于上下文继承,它在QML代码中随处可见。current对象在最外层的根上下文中注册,该根上下文在任何地方都会被继承。

import QtQuick
import QtQuick.Window

Window {
    visible: true
    width: 512
    height: 300

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

Here are the different ways you can extend QML in general:

以下是扩展QML的几种方法:

  • Context properties - setContextProperty()
  • 上下文属性-setContextProperty()
  • Register type with engine - calling qmlRegisterType in your main.cpp
  • 向engine注册类型-在main.cpp中调用qmlRegisterType
  • QML extension plugins - maximum flexibility, to be discussed next
  • QML扩展插件-最大的灵活性,将在下一步讨论

Context properties are easy to use for small applications. They do not require any effort you just expose your system API with kind of global objects. It is helpful to ensure there will be no naming conflicts (e.g by using a special character for this ($) for example $.currentTime). $ is a valid character for JS variables.

上下文属性很容易用于小型应用程序。它们不需要任何努力,只要用某种全局对象公开系统API即可。确保没有命名冲突是很有帮助的(例如,为此($)使用特殊字符,例如$.currentTime)。$是JS变量的有效字符。

Registering QML types allows the user to control the lifecycle of a C++ object from QML. This is not possible with the context properties. Also, it does not pollute the global namespace. Still all types need to be registered first and by this, all libraries need to be linked on application start, which in most cases is not really a problem.

注册QML类型允许用户从QML控制C++对象的生命周期。对于上下文属性,这是不可能的。而且,它不会污染全局名称空间。不过,所有类型都需要先注册,这样一来,所有库都需要在应用程序启动时链接,在大多数情况下,这并不是一个真正的问题。

The most flexible system is provided by the QML extension plugins. They allow you to register types in a plugin which is loaded when the first QML file calls the import identifier. Also by using a QML singleton, there is no need to pollute the global namespace anymore. Plugins allow you to reuse modules across projects, which comes quite handy when you do more than one project with Qt.

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

Going back to our simple example main.qml file:

回到我们的简单示例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!"
    }
}

When we import the QtQuick and QtQuick.Window, what we do is that we tell the QML run-time to find the corresponding QML extension plugins and load them. This is done by the QML engine by looking for these modules in the QML import paths. The newly loaded types will then be made available to the QML environment.

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

For the remainder of this chapter will focus on the QML extension plugins. As they provide the greatest flexibility and reuse.

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

上一篇:【编测编学】零基础学python_08_列表(排序+反转+长度)


下一篇:2020-01-23