使用生成器

本主题描述了如何使用 QtIF 生成器。

简介

生成器是一个 Python 脚本,可以手动运行或使用 构建系统集成。此脚本使用 QFace 作为自动生成框架,解析接口定义语言 (IDL) 文件,生成域模型(类似于抽象语法树 (AST))并将其提供给生成器。根据要生成的项目类型,指定了不同的 格式

命令行参数

要运行生成,请使用以下命令

$$[QT_HOST_LIBEXECS]/ifcodegen/generate.py -T $$[QT_INSTALL_DATA]/ifcodegen-templates
--format=backend_simulator interface.qface out_dir

选项和参数如下

选项/参数描述
--reload / --no-reload [可选]指定生成器是否应跟踪 IDL 文件中的更改并在运行时更新输出;默认为 --no-reload
-T, --template-search-path <search-path> 将给定的路径添加到搜索路径列表。将扫描此列表中的所有目录以查找生成模板(由 生成 YAML 识别)。可以使用 –template 选项 选择识别的模板。
-t, --template 选择用于生成的模板。在 模板搜索路径 中搜索模板。可以提供绝对路径而不是模板名称。此模板不需要是模板搜索路径的一部分。以下模板通常默认安装:
  • 前端
  • qml插件
  • 后端模拟器
  • 后端 Qtro
  • 服务器 Qtro
  • 服务器 Qtro 模拟器
-A, –annotations <annotation-file> 将给定的注释文件与 QFace 文件中已有的注释和隐含注释文件合并。这些文件根据传递给生成器的顺序合并。在 YAML 文件中提供重复的键将覆盖先前设置的值。此选项可以多次使用。有关更多信息,请参阅 合并注释
-I, –import <import-path> 将给定的路径添加到导入路径列表。递归扫描此列表中的所有目录以查找 QFace 文件。然后使用找到的 QFace 文件来解析导入模块时所需的信息;这类似于 C++ 包含路径的工作方式。
sourceIDL源文件路径或路径列表。如果有多个条目,每个条目都将被处理。如果提供目录路径,则扫描该目录下的IDL文件。
输出目录生成的文件的目标文件夹。
–help显示选项并退出。

目前,根据–template值,生成器可以使用给定的IDL文件生成多种类型的项目

项目类型描述
前端 使用qtinterfaceframework基础类和动态后端系统来生成API。
qml插件生成一个注册所有前端类型的C++ QML插件。
后端模拟器 为首先由前端选项生成的API生成一个模拟后端。此后端作为模拟实现。
QtRemoteObjects后端 为首先由前端选项生成的API生成一个基于QtRemoteObjects的后端客户端。此后端连接到后端服务器。
服务器_qtro 为首先由前端选项生成的API生成一个基于QtRemoteObjects的后端服务器桩。
服务器_qtro模拟器 为首先由前端选项生成的API生成一个基于QtRemoteObjects的模拟服务器。
文件夹路径使用文件夹内的模板。一个与文件夹同名的YAML文件应该提供文件夹中模板文件的列表。这对于想编写自己的模板很有用。有关更多详细信息,请参阅生成YAML

配置生成器

生成器的Python脚本解析输入文件并创建领域模型。然后将此领域模型作为上下文传递给Jinja模板引擎。使用生成YAML文件指定要生成的文件。之后,可以使用注释YAML文件向IDL文件添加更多生成器特有的信息。

生成器YAML

创建领域模型树后,遍历此树,并将领域模型对象树的每个叶(模块、接口、结构等)传递给配置文件定义的特定Jinja模板。

生成器YAML文件定义了用来生成哪些文件的模板。假设你想要为你的领域模型中的每个模块生成一个头文件。但,一个模块可以有多个接口,因此你想为每个接口和结构使用不同的头文件。在这种情况下,生成器YAML文件定义了一组规则来指定使用哪个模板文件以及如何命名它们。

此YAML文件必须具有以下结构

frontend:
    module:
        documents:
            - "{{module.module_name|lower}}plugin.h": "plugin.h.tpl"
    interface:
        documents:
            - '{{interface|lower}}backend.h': 'backend.h.tpl'

对于每个实体,在遍历领域模型树中的该实体时,都需要调用一系列模板。在这里,YAML文件定义了一个文档列表,该列表需要为所有模块和所有接口生成。每个列表条目包含两部分;第一部分是需要创建的文件名称,按照Jinja模板语言的格式指定。模板名称中使用的对象属性值将被处理并替换到模板中,从而形成创建文件的最终名称。第二部分是使用模板的名称。对于接口框架生成器,您必须为三种类型的实体指定规则:模块、接口和结构。请参阅QFace规则基生成文档获取更多信息。

额外的Jinja过滤器

有时由ifcodegen提供的Jinja过滤器不足以满足需要,但您可以编写自己的过滤器使用Python。如果您在模板中添加一个名为filters.py的Python脚本,它将被自动加载。您可以使用以下模板代码

# Copyright (C) 2021 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0

import json
import inspect

from qface.idl.domain import Module, Interface, Property, Parameter, Field, Struct
from qface.helper.generic import lower_first, upper_first
from qface.helper.qtcpp import Filters

from generator.global_functions import jinja_error, jinja_warning
from generator.filters import deprecated_filter

def custom_filter(s):
    jinja_warning("Test calling a function from the ifcodegen within our own filters")
    return

filters['custom_filter'] = custom_filter

为了在模板之间共享过滤器,还可以指定其他应加载的Python脚本。这需要在生成YAML文件中完成。

以下示例生成了插件.h并从名为extra-filter的文件夹中加载了额外的过滤器

frontend:
    extra_filters: [ "extra-filter/filters.py" ]
    module:
        documents:
            - "{{module.module_name|lower}}plugin.h": "plugin.h.tpl"

注释YAML

目前,使用IDL本身无法表达接口描述的所有方面。例如,没有语言构造来定义属性的默认值或属性可以接受的值范围。然而,这可以通过称为注释的机制来实现。注释提供了自由和灵活性,可以表达任何概念和构造。

下面的一段代码展示了在IDL中使用注释的示例。这里,我们定义一个有区域界定(zoned)的接口,并指定其ID。

@config: {zoned: true, id: "org.qt-project.interfaceframework.ClimateControl/1.2"}

将所有注释放置在主IDL文件中并不合理。例如,您可能需要定义一些自动测试代码生成的方面。这些注释可以放入与主IDL文件同名的YAML文件中。在解析阶段,QFace会自动拾取此文件,并将YAML文件中指定的注释与在IDL文件中定义的注释合并。

由于伴随的YAML文件总是自动拾取,因此它不适用于需要用于某些特定项目的注释,例如生成后端插件时的注释。对于此用例,您可以将多个额外的注释YAML文件传递给生成器。

QtInterfaceFramework中,以下注释用于定义IDL

标签位置对象类型目的
@config: {namespace: "module"}
主IDL文件模块定义生成的代码应该使用的C++命名空间。可用的选项包括
  • <novalue>不使用命名空间(默认值
  • qt使用qt命名空间
  • module使用完整的模块名称作为命名空间
  • <value>使用提供值作为命名空间
@config: {interfaceBuilder: "FunctionName"}
主IDL文件模块声明一个在插件中调用的函数,用于为每个接口生成实例。该函数接受一个指向插件实例的指针,并返回一个QVector<QIfFeatureInterface *>。应按照Plugin::interfaces()定义的顺序生成接口。使用此标签实例化由生成的插件接口类派生的类。
@config: {zoned: true}
主IDL文件接口通知生成器是否该接口是分区的。使用此标签定义后端功能接口是否从QIfZonedFeatureInterfaceQIfFeatureInterface派生。
@config: {id: "org.qt.project.interfaceframework.ClimateControl/1.0"}
主IDL文件接口定义接口ID,这是一个字符串,由QtInterfaceFramework服务管理器用于将前端接口与其后端实现连接。有关更多信息,请参阅动态后端系统
@config: {getter_name: "isHeaterEnabled"}
主IDL文件属性覆盖默认获取方法名称。对于布尔属性,如'enabled'属性的获取器,应使用'isEnabled'而不是默认值。
@config: {setter_name: "setHeaterEnabled"}
主IDL文件属性覆盖默认设置方法名称。
@config: {qml_name: "ClimateControl"}

@config: {qml_type: "ClimateControl"}
主IDL文件模块,接口定义此接口或模块应在QML中使用的名称。对于接口,这是用于将接口导出到QML的名称。对于模块,它定义了完整模块的URI。URI的最后一部分也用于导出所有枚举的单一实例。
@designer: {categoryName: "Smart Home Components"}
主IDL文件模块,接口定义此接口应在Qt Design Studio库中列出的类别名称。如果为模块定义,则设置该模块中所有接口的类别名称,但可以由每个接口在这里定义的值覆盖。
@designer: {name: "Climate Control"}
主IDL文件接口定义此接口应在Qt Design Studio库中列出的名称。
@designer: {typeIcon: "images/climate.png"}
主IDL文件接口typeIcon是Qt Design Studio中的导航面板中使用的16x16图标。

注意:图标需要通过自定义构建规则复制到正确的文件夹。

@designer: {libraryIcon: "images/climate.png"}
主IDL文件接口libraryIcon在Qt Design Studio的库中显示。

注意:图标需要通过自定义构建规则复制到正确的文件夹。

@config: { configurationId: "smarthome"}
主IDL文件模块,接口定义此接口的configurationId。configurationId可用于使用QIfConfiguration从中央位置指定接口的配置。如果为模块定义,则设置该模块中所有接口的configurationId,但可以通过在此定义覆盖每个接口。默认为module.name

注释不属于接口描述的逻辑部分,而是用于指定其他信息的注释,放入附带的YAML文件中。

以下是定义生成的后端和服务器的各种方面的注释列表

backend_simulator

标签位置对象类型目的
config_simulator:
    serviceObjectId: "smarthome_simulation"
附带的YAML文件模块定义生成的插件的id。有关更多信息,请参阅QIfServiceObject::id。默认为module.name + "_simulation"。
config_simulator:
    configurationId: "smarthome"
附带的YAML文件模块定义生成的插件的configurationId,可用于使用QIfConfiguration提供后端的设置。默认为module.name
config_simulator:
    simulationFile: ":/qrc/simulation.qml"
附带的YAML文件模块定义模拟后端应该加载的哪个模拟QML文件。提供的代码片段从资源系统加载了QML文件,开发人员需要将其嵌入。
config_simulator:
    defaultApplicationMode: "gui"
附带的YAML文件模块定义从server_qtro_simulator模板生成的服务器使用的默认模式。有效选项是"gui"或"headless"(默认)。
config_simulator:
    zones: [ Left,
             Right
           ]
附带的YAML文件接口定义后端模拟应支持的区域列表。
config_simulator:
default:
    MyFlag.Value1 |
    MyFlag.Value2
附带的YAML文件属性定义模拟后端返回属性的初始值。

对于分区属性,可以将区域映射到默认值。映射的默认键为"="。

config_simulator:
default: {
    Left: 21.0,
    Right: 22.5,
    =: 0.0
}
config_simulator:
minimum: 10
附带的YAML文件属性定义整数和实数属性的最小值;模拟后端生成的代码验证此值。
config_simulator:
maximum: 10
附带的YAML文件属性定义整数和实数属性的最大值;模拟后端生成的代码验证此值。
config_simulator:
range: [10, 20]
附带的YAML文件属性定义整数和实数值的取值范围;在仿真器后端生成的代码将验证该值。
config_simulator:
domain: {10, 20, 30}
附带的YAML文件属性定义属性的取值范围;在仿真器后端生成的代码将验证该值。
config_simulator:
unsupported: yes
附带的YAML文件属性定义属性是否支持;在仿真器后端生成的代码将验证该值并在尝试更改不支持属性时报告警告。

backend_qtro

标签位置对象类型目的
config_qtro:
    serviceObjectId: "smarthome_qtro"
附带的YAML文件模块定义生成的插件ID。有关更多信息,请参阅QIfServiceObject::id。默认为module.name + "_qtro"
config_qtro:
    configurationId: "smarthome"
附带的YAML文件模块定义生成的插件的configurationId,可用于使用QIfConfiguration提供后端的设置。默认为module.name

config_server_qtro

标签位置对象类型目的
config_server_qtro:
    useGeneratedMain: true
附带的YAML文件模块生成一个包含常见命令行选项的main.cpp和QIfRemoteObjectsConfig实例,该实例传递给用户提供的serverMain函数。

生成的项目结构

在生成输出目录中,首先创建一个新的子目录,并以模块ID命名。所有生成的文件都放在这个文件夹中。下表描述了为前端和后端生成的文件。

前端

基于动态后端系统生成一个适合QML的C++ API。

文件名目的
global.h包含全局EXPORT定义的标准化文件。
module.h/cpp定义模块类的文件,用于模块全局变量和类型。
module_enum.qdocinc涵盖所有枚举值的文档,这些枚举值可以被qdoc包含。
modulefactory.h/cpp定义模块工厂类的文件,用于所有结构的工厂方法。
.pri包含所有生成文件的标准化Qt .pri文件。使用此.pri文件将生成文件包含到qmake项目中。
CMakeLists.txt用于与CMake构建系统集成。此文件定义了如何使用CMake构建生成文件的规则。此外,可以使用qt6_set_ifcodegen_variable暴露额外的变量。
qml/{{module|qml_type|replace('.', '/')}}/plugins.qmltypes用于QtCreator的QML代码补全文件。
backendinterface.h/cpp定义需要由后端实现的功能接口的文件。
.h/cpp可供QML使用的功能的前端实现。
_p.h前端实现的部分。
.h/cpp作为Q_GADGET实现的结构的实现。

QML插件

生成一个C++ QML插件,它会在QML中注册前端的所有类型。

注意:对于CMake,此模板已由新的QML类型注册系统取代。有关更多信息,请参阅QML类型注册

文件名目的
plugin.cppC++ QML插件类。
.pri包含所有生成文件的标准化Qt .pri文件。使用此.pri文件将生成文件包含到qmake项目中。
CMakeLists.txt用于与CMake构建系统集成。此文件定义了如何使用CMake构建生成文件的规则。此外,可以使用qt6_set_ifcodegen_variable暴露额外的变量。
plugins.qmltypes用于QtCreator的QML代码补全文件。
qmldirQML配置文件,用于将插件注册到QML插件系统中。

后端仿真

提供一个使用QIfSimulationEngine实现的仿真器后端,以在QML文件中实现仿真行为。

文件名目的
plugin.h/cpp定义实现QtInterfaceFramework后端插件的文件,该插件实现QIfServiceInterface
.json包含由Qt插件系统需要的暴露功能接口标识符的文件。
.pri包含所有生成文件的标准化Qt .pri文件。使用此.pri文件将生成文件包含到qmake项目中。
CMakeLists.txt用于与CMake构建系统集成。此文件定义了如何使用CMake构建生成文件的规则。此外,可以使用qt6_set_ifcodegen_variable暴露额外的变量。
_simulation.qml加载接口特定QML仿真文件的QML仿真文件。
_simulation_data.json从config_simulator批注导出的仿真数据。
.qrc包含QML和JSON文件的Qt资源文件。
qml/{{module|qml_type|replace('.', '/')}}/plugins.qmltypes用于QtCreator的QML代码补全文件。
qml/{{module|qml_type|replace('.', '/')}}/simulation/plugins.qmltypes"用于在QtCreator中使用模拟API的QML代码补全文件。
backend.h/cpp包含模拟后端实现文件的文件。
Simulation.qml特定接口的QML模拟文件。

QtRemoteObjects 后端

QtRemoteObjects 模块在构建qtinterfaceframework仓库时检测到,此后端_qtro模板才可用。此后端是连接到远程后端服务器的客户端;不是实现实际后端逻辑的位置。

文件名目的
plugin.h/cpp定义QtInterfaceFramework后端插件实现,该插件实现QIfServiceInterface的文件。
.json包含由Qt插件系统需要的暴露功能接口标识符的文件。
.pri包含所有生成文件的标准的Qt .pri文件。使用此.pri文件将生成文件包含到qmake项目中。还包括.{re stronghold} 文件到项目,并调用远程对象编译器。
CMakeLists.txt用于与CMake构建系统集成。此文件定义了如何使用CMake构建生成文件的规则。此外,可以使用qt6_set_ifcodegen_variable暴露额外的变量。
backend.h/cpp包含远程对象后端实现文件的文件。建立连接并初始化远程对象副本。
.repQt的副本编译器的输入文件,用于生成副本类代码。
pagingmodel.repQt的副本编译器的输入文件,用于为所有模型生成副本类代码。

QtRemoteObjects 服务器

当构建qtinterfaceframework仓库时检测到QtRemoteObjects模块,此server_qtro模板才可用。生成的代码仅包含继承的源类代码以及建立连接的代码。开发者必须实现实际的后端逻辑。

文件名目的
core.h/cpp用于建立连接和启动源对象的远程功能的代码。
.pri包含所有生成文件的标准的Qt .pri文件。使用此.pri文件将生成文件包含到qmake项目中。还包括.{re stronghold} 文件到项目,并调用远程对象编译器。
CMakeLists.txt用于与CMake构建系统集成。此文件定义了如何使用CMake构建生成文件的规则。此外,可以使用qt6_set_ifcodegen_variable暴露额外的变量。
.repQt的副本编译器的输入文件,用于生成源类代码。
pagingmodel.repQt的副本编译器的输入文件,用于为所有模型生成副本类代码。

QtRemoteObjects 模拟服务器

当构建qtinterfaceframework仓库时检测到QtRemoteObjects模块,此server_qtro模板才可用。生成的代码包含一个完全实施的服务器,该服务器可能与backend_simulator模板使用相同的实现,该实现使用QIfSimulationEngine在QML中实现模拟行为。

默认情况下,在生成的服务器中使用QCoreApplication,使服务器可以作为无头服务器运行。为了允许在模拟QML代码中实例化UI控制,服务器可以以GUI模式(–gui选项)启动。可以通过config_simulator_defaultServerMode批注更改默认模式。

文件名目的
_simulation.qml加载特定接口的QML模拟文件的QML模拟文件。
_simulation_data.json从config_simulator批注导出的仿真数据。
.qrc包含QML和JSON文件的Qt资源文件。
qml/{{module|qml_type|replace('.', '/')}}/plugins.qmltypes用于QtCreator的QML代码补全文件。
qml/{{module|qml_type|replace('.', '/')}}/simulation/plugins.qmltypes用于在QtCreator中使用模拟API的QML代码补全文件。
core.h/cpp用于建立连接和启动源对象的远程功能的代码。
main.cpp主文件。
.pri包含所有生成文件的标准的Qt .pri文件。使用此.pri文件将生成文件包含到qmake项目中。还包括.{re stronghold} 文件到项目,并调用远程对象编译器。
CMakeLists.txt用于与CMake构建系统集成。此文件定义了如何使用CMake构建生成文件的规则。此外,可以使用qt6_set_ifcodegen_variable暴露额外的变量。
.repQt的副本编译器的输入文件,用于生成源类代码。
adapter.h/cppQtRemoteObjects适配器类,用于后端实现。
pagingmodel.repQt的副本编译器的输入文件,用于为所有模型生成副本类代码。
backend.h/cpp包含模拟后端实现的文件。
Simulation.qml特定接口的QML模拟文件。

后端特定配置选项

一些生成的后端可以使用QIfConfiguration类提供的服务设置。以下表格描述了每个模板可用的设置

后端模拟器

生成的代码没有进行任何服务设置,但它将serviceSettings传递给QML模拟。手写的QML模拟可以使用BackendInterface对象的serviceSettings属性来改变设置设置时的行为。

QtRemoteObjects后端

名称描述
connectionUrl接口尝试使用Qt远程对象连接到的URL。默认为 local + {{module.module_name|lower}}。在运行时更改此值将使后端重新连接到新的URL。
connectionTimeout定义何时打印超时警告(以毫秒为单位)。要将警告禁用,请将超时设置为-1。

除了这些全局设置之外,还可以针对后端接口提供值,这允许后端连接到多个远程对象实例。特定于接口的设置需要以前端名称作为前缀。以下示例创建一个配置,它应用于 cluster 群组中的所有Items。应用于当前连接后端的服务设置使用全局的 connectionTimeout 和集群模块中InstrumentCluster接口的特定 connectionUrl,而其他所有接口使用默认的 connectionUrl

    InterfaceFrameworkConfiguration {
        name: "cluster"
        serviceSettings: {
            "connectionTimeout": 1000,
            "cluster.InstrumentCluster": {
                "connectionUrl": "tcp://127.0.0.1:1234"
            }
        }
    }

    Instead of providing settings per interface, it is also possible to provide the settings on a
    per module basis using the module name as a key.

© 2024 Qt公司有限公司。在此处包含的文档贡献是各自所有者的版权。在此提供的文档根据自由软件基金会发布的 GNU自由文档许可证版本1.3 的条款提供。Qt及其相关商标是芬兰的Qt公司及其在全球范围之内的商标。所有其他商标均属于其各自所有者。