Qt 接口框架生成器气候示例

本示例展示了如何使用 Qt 接口框架生成器。

简介

此示例向您展示如何使用 Qt 接口框架生成器构建新组件。基于单个 QFace 接口定义语言 (IDL) 文件,该示例生成

  • 具有前端代码的共享库
  • 后端模拟插件
  • 演示应用程序,展示当前模块中的值

IDL 文件

此示例中使用的 IDL 文件代表了一个简化的气候控制接口,其中包含一个接口和一些枚举类型。

让我们看看这个 QFace IDL 文件的简化版本:

module Example.If.Climate 1.0;

interface ClimateControl {
    bool airConditioning;
    int fanSpeedLevel;
    RecirculationMode recirculationMode;
    AirflowDirection airflowDirections;
}

enum RecirculationMode {
    RecirculationOff = 0x0,
    RecirculationOn = 0x1,
    AutoRecirculation = 0x2
}

flag AirflowDirection {
    Windshield = 1,
    Dashboard = 2,
    Floor = 4
}
概述

首先,我们需要定义我们要描述哪个 模块模块 作为命名空间使用,因为 IDL 文件可以包含多个接口。

module Example.If.Climate 1.0;

模块 最重要的一部分是其 接口 定义。

interface ClimateControl {
    bool airConditioning;
    int fanSpeedLevel;
    RecirculationMode recirculationMode;
    AirflowDirection airflowDirections;
}

在这个例子中,我们定义了一个名为 ClimateControl接口,它包含一些它应该提供的功能。每个属性定义都必须包含至少一个类型和一个名称。大多数基本类型是内置的,可以在 QFace IDL 语法 中找到。最后两个属性是特殊的,因为它们使用自定义类型,这些类型在 接口 定义之后定义。

enum RecirculationMode {
    RecirculationOff = 0x0,
    RecirculationOn = 0x1,
    AutoRecirculation = 0x2
}

flag AirflowDirection {
    Windshield = 1,
    Dashboard = 2,
    Floor = 4
}

第一个定义是一个包含它支持的所有值的 枚举,包括每个单独项的数值。第二个定义与之类似,但使用的是 标志 类型。

注释和注解

与上一节中看到的最小 IDL 相比,完整的 IDL 文件 包含许多注释和注解。

/** 开头的注释定义文档说明,可以通过生成模板转换为文档标记,如 QDoc 或 Doxygen。

注解

注解用于向 IDL 语句添加额外的信息。它们是 YAML 片段,提供键值存储。生成模板定义了支持的注解。

以下是本示例中使用的所有注释的概述以及它们的作用:

注解描述
@config: {zoned: true}
指定接口支持不同的区域。
@config: {qml_type: "UiClimateControl"}
指定从 QML 中使用时的组件名称。
@config: {id: "example.interfaceframework.ClimateControl/1.0"}
指定匹配后端插件的 ID。
@config_simulator: { range:[0, 50] }
指定数值属性的有效值范围。

注意:此处使用的 range 注解是一个快速指定最小和最大值的快捷方式。

@config_simulator: { minimum: 0; maximum: 50 }
指定数值属性的最小和最大值。
@config_simulator: { domain: ["cold", "mild", "warm" ] }
指定属性的合法值列表。
@config: {interfaceBuilder: "echoInterfaceBuilder"}
指定插件应使用自定义函数来生成后端实例。

除了 IDL 文件外,还使用同名的 YAML 文件来添加额外配置。这些配置也可以直接添加到 IDL 文件中,但我们选择将它们保持独立以提高可读性。

以下列出了一些这些额外配置:

Example.If.Climate.ClimateControl:
    config_simulator:
        zones: { left : FrontLeft, right : FrontRight, rear: Rear }
定义支持区域的名字。
Example.If.Climate.ClimateControl#recirculationMode:
    config_simulator:
        default: RecirculationMode.RecirculationOff
指定模拟器后端插件中属性的默认值。

前端库

现在我们想要使用接口框架生成器来生成一个包含我们的模块及其接口的 C++ 实现的共享库。

在这种情况下,我们使用 frontend 模板,该模板生成一个从 QIfAbstractZonedFeature 派生的类,包含所有指定的属性。生成的库使用来自 QtInterfaceFramework动态后端系统,提供了一种方便的方法来更改行为实现。有关更多详情,请参阅后端模拟插件

要调用共享库的自动生成器,它需要集成到构建系统中。

CMake:

首先,使用 find_package 查找 InterfaceFramework 软件包。

之后,我们继续构建一个库,并通过调用 qt6_ifcodegen_extend_target 来让自动生成器通过生成源代码扩展此目标。

qt_add_library(QtIfClimateExample)

# Interface Framework Generator:
qt_ifcodegen_extend_target(QtIfClimateExample
    IDL_FILES ../example-climate.qface
    TEMPLATE frontend
)

qmake:

qmake 项目文件需要使用 ifcodegen qmake 功能。以下代码片段展示了如何做到这一点

CONFIG += ifcodegen
IFCODEGEN_SOURCES = ../example-climate.qface

通过将 ifcodegen 添加到 CONFIG 变量中,载入 ifcodegen 功能文件,并像在普通 qmake 项目中处理 SOURCES 变量一样,解释 IFCODEGEN_SOURCES 变量。

然而,使用 CONFIG 变量激活 qmake 功能有一个缺点:如果此功能不可用,它不会报告任何错误。但是,您可以使用以下额外代码来报告错误

QT_FOR_CONFIG += interfaceframework
!qtConfig(ifcodegen): error("No ifcodegen available")

项目文件的其余部分是正常的库设置,应该在 Linux、macOS 和 Windows 上都能工作。

后端模拟插件

由于 front-end 库使用了 动态后端系统,因此我们需要一个相应的 back-end 插件,以便库提供某些功能。要生成名为 "Simulator Backend" 的后端插件模拟版本,您可以使用与 front-end 库使用相同 IDL 文件的 backend_simulator 模板。构建系统集成工作方式相同,但是使用不同的生成模板。

CMake:

通过调用代码生成器来定义和扩展插件,这次使用的是 backend_simulator 模板。

qt_add_plugin(climate_backend_simulator PLUGIN_TYPE interfaceframework)

qmake:

因为我们想要生成一个插件而不是普通的库,所以需要指示 qmake 通过将 plugin 添加到 CONFIG 变量中来实现它。

CONFIG += ifcodegen plugin
IFCODEGEN_TEMPLATE = backend_simulator
IFCODEGEN_SOURCES = ../example-climate.qface
PLUGIN_TYPE = interfaceframework
PLUGIN_CLASS_NAME = ClimateSimulatorPlugin

为了正确编译插件,它需要从之前创建的库中获取后端接口头文件。然而,这个头文件不是我们的源代码树的一部分,而是构建树的一部分,因为它也是生成的。我们通过以下代码将此头文件添加到包含路径,以提供此头文件:

qmake:

INCLUDEPATH += $$OUT_PWD/../frontend

CMake:

target_link_libraries(climate_backend_simulator PUBLIC
    QtIfClimateExample
)

backend_simulator 模板使用了上面解释的 @config_simulator 注解。这意味着生成的后端提供了注解中定义的默认值,并使用 minimum/maximumrange 注解检查新值的边界。

使用 zones 注解,生成的后端为每个区域提供单独的值,并将可用的区域与前端库通信。

演示应用程序

演示应用程序提供了一个简单的 QML 界面,其中包含所有生成的接口属性。

由于我们不提供 QML 插件,因此应用程序需要链接到生成的前端库,并调用模块单例中生成的 ClimateModule::registerTypesClimateModule::registerQmlTypes 方法来注册 QML 引擎中的所有自动生成的接口和类型。

在我们的 QML 应用程序中,我们仍然需要使用与 IDL 文件中相同的模块 URI 导入模块。之后,接口可以像常规的 QML 项目一样实例化。

import Example.If.ClimateModule

Window {

    visible: true
    width: 640
    height: 480
    title: qsTr("QtIF Climate")

    UiClimateControl {
        id: climateCtrl
    }

    Column {
        anchors.fill: parent
        anchors.margins: 5

        Text {
            text: "Air Conditioning: " + climateCtrl.airConditioning
        }
...

我们的应用程序不了解我们的后端插件,因此,我们需要将此插件放在我们的应用程序查找插件的位置。默认情况下,Qt 在其安装目录内的 plugins 文件夹或应用程序当前工作目录中查找。为了找到 QtInterfaceFramework 插件,它们需要放置在 interfaceframework 子文件夹中。

为了确保自动完成这项操作,我们在后端构建系统文件中添加以下行:

CMake:

set_target_properties(climate_backend_simulator PROPERTIES LIBRARY_OUTPUT_DIRECTORY ../interfaceframework)

qmake:

DESTDIR = ../interfaceframework

示例项目 @ code.qt.io

© 2024 The Qt Company Ltd. 本文档贡献的版权归其各自的所有者。此处提供的文档受 GNU 自自由文档许可证版本 1.3 的条款约束,该许可证由自由软件基金会发布。Qt 和相应的徽标是 The Qt Company Ltd. 在芬兰及/或全球的商标。所有其他商标均为其各自所有者的财产。