/**************************************************************************** ** ** Copyright (C) 2021 The Qt Company Ltd. ** Copyright (C) 2019 Luxoft Sweden AB ** Copyright (C) 2018 Pelagicore AG ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the documentation of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: https://www.gnu.org/licenses/fdl-1.3.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ /*! \example interfaceframework/qface-climate \brief This Example shows how to use the Qt Interface Framework Generator. \ingroup qtinterfaceframework-examples \title Qt Interface Framework Generator Climate Example \image qface-climate.png \section1 Introduction This example shows you how you can use the Qt Interface Framework Generator to build a new component. Based on a single QFace Interface Definition Language (IDL) file, the example generates: \list \li a shared library with the front-end code \li a back-end simulator plugin \li a demo application that shows the values in the current module \endlist \section1 The IDL File The IDL file used in this example represents a simplified climate control interface that contains a single interface and some enumerated types. Let's take a look at a minimal version of the same QFace IDL file: \code 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 } \endcode \section2 Walkthrough First, we need to define which \c module we want to describe. The \c module acts as a namespace, because the IDL file can contain multiple interfaces. \code module Example.If.Climate 1.0; \endcode The most important part of the \c module is its \c interface definition. \code interface ClimateControl { bool airConditioning; int fanSpeedLevel; RecirculationMode recirculationMode; AirflowDirection airflowDirections; } \endcode In this case, we define an \c interface named \b ClimateControl consisting of a few properties it should offer. Each property definition must contain at least a type and a name. Most of the basic types are built-in and can be found in the \l {QFace IDL syntax}. The last two properties are special as they use custom types, that are defined after the \c interface definition. \code enum RecirculationMode { RecirculationOff = 0x0, RecirculationOn = 0x1, AutoRecirculation = 0x2 } flag AirflowDirection { Windshield = 1, Dashboard = 2, Floor = 4 } \endcode The first definition is an \c enum with all the values it supports, including the numeric value of each individual item. The second definition is similar, but using the \c flag type. \section2 Comments and Annotations Compared to the minimal IDL we saw in the previous section, the full \fileLink {examples/interfaceframework/qface-climate/example-climate.qface}{IDL file} contains a lot of comments and annotations. Comments starting with \c /** define documentation statements and can be converted into documentation markup like QDoc or Doxygen, by the generation template. \section3 Annotations Annotations are used to add additional information to the IDL statements. They are YAML fragments that provide a key-value store. The generation template defines the supported annotations. Here's an overview of all the annotations used in this example and what they do: \table \header \li Annotation \li Description \row \li \code @config: {zoned: true} \endcode \li Specifies that the interface supports different zones. \row \li \code @config: {qml_type: "UiClimateControl"} \endcode \li Specifies the component name when used from QML. \row \li \code @config: {id: "example.interfaceframework.ClimateControl/1.0"} \endcode \li Specifies the ID used to match backend plugins. \row \li \code @config_simulator: { range:[0, 50] } \endcode \li Specifies a range of valid values for numerical properties. \note The \c {range} annotation used here is a shortcut to specify both minimum and maximum values. \row \li \code @config_simulator: { minimum: 0; maximum: 50 } \endcode \li Specifies the minimum and maximum values for numerical properties. \row \li \code @config_simulator: { domain: ["cold", "mild", "warm" ] } \endcode \li Specifies a list of valid values for properties. \row \li \code @config: {interfaceBuilder: "echoInterfaceBuilder"} \endcode \li Specifies that the plugin should use a custom function to generate the backend instances. \endtable In addition to the IDL file, a YAML file with the same base name is used to add extra configurations. These configurations may also be added directly into the IDL file, but we choose to keep them separate for readability. Some of these extra configurations are highlighted below: \table \row \li \code Example.If.Climate.ClimateControl: config_simulator: zones: { left : FrontLeft, right : FrontRight, rear: Rear } \endcode \li Defines the names for the supported zones. \row \li \code Example.If.Climate.ClimateControl#recirculationMode: config_simulator: default: RecirculationMode.RecirculationOff \endcode \li Specifies the default value assigned to a property in the simulator back-end plugin. \endtable \section1 Front End Library Now we want to use the Interface Framework Generator to generate a shared library that contains a C++ implementation of our module and its interface. In this case, we use the \c frontend template, that generates a class derived from \c {QIfAbstractZonedFeature} including all the specified properties. The generated library uses the \l {Dynamic Backend System} from QtInterfaceFramework, providing an easy way to change the behavior implementations. For more details, see \l {Back End Simulator Plugin}. To call the autogenerator for our shared library, it needs to be integrated into the build system. \e CMake: First the \c InterfaceFramework package needs to be found using \c find_package: \snippet interfaceframework/qface-climate/frontend/CMakeLists.txt 0 Afterwards we proceed to build a library and let the autogenerator extend this target with the generated source code by invoking \l {qt6_ifcodegen_extend_target}. \snippet interfaceframework/qface-climate/frontend/CMakeLists.txt 1 \e qmake: The qmake project file needs to use the \c ifcodegen qmake feature. The snippet below shows how to do this: \snippet interfaceframework/qface-climate/frontend/frontend.pro 1 By adding \c ifcodegen to the \c CONFIG variable, the \c ifcodegen feature file is loaded and interprets the \c IFCODEGEN_SOURCES variable just like the \c SOURCES variable in normal qmake projects. However, activating the qmake feature using the \c CONFIG variable has one disadvantage: it doesn't report any errors if this feature is not available. But, you can use the following additional code to report errors: \snippet interfaceframework/qface-climate/frontend/frontend.pro 0 The other part of the project file is a normal library setup which should work on Linux, macOS, and Windows. \section1 Back End Simulator Plugin Since the \c{front-end} library uses the \l {Dynamic Backend System}, we need a corresponding \c{back-end} plugin, for the library to provide some functionality. To generate a mock version of the back-end plugin called "Simulator Backend", you can use the \c backend_simulator template from the same IDL file as the \c{front-end} library uses the \l {Dynamic Backend System}, we need a corresponding \c{back-end} plugin, fo library. The build system integration works in the same way, but it uses a different generation template. \e CMake: A plugin is defined and extended by calling the codegenerator, this time with the \c backend_simulator template: \snippet interfaceframework/qface-climate/backend_simulator/CMakeLists.txt 2 \e qmake: As we want to generate a plugin instead of a plain library, we need to instruct qmake to do so by adding \c plugin to the \c CONFIG variable. \snippet interfaceframework/qface-climate/backend_simulator/backend_simulator.pro 2 For the plugin to compile correctly it needs to get the backend interface header from the previously created library. However, this header is not part of our source tree but the build tree, because it is also generated. We provide this header by adding it to the include path using the following code: \e qmake: \snippet interfaceframework/qface-climate/backend_simulator/backend_simulator.pro 1 \e CMake: \snippet interfaceframework/qface-climate/backend_simulator/CMakeLists.txt 1 The \c backend_simulator template makes use of the \b @config_simulator annotations explained \l{Annotations}{above}. This means that the generated back end provides the default values defined in the annotations and checks the boundaries of new values using the \c minimum/maximum or \c range annotations. Using the \c zones annotations, the generated back end provides individual values for every zone and communicates the available zones to the front-end library. For more information, see the \l {Climate Control QML Example}. \section1 Demo Application The demo application presents a simple QML interface with all the properties of the generated interface. Since we do not provide a QML plugin, the application needs to link to the generated front-end library and call the \c {ClimateModule::registerTypes} and \c {ClimateModule::registerQmlTypes} methods that are generated in the module singleton to register all auto-generated interfaces and types with the QML engine. In our QML application, we still need to import the module using the same module URI used in the IDL file. Afterwards, the interface can be instantiated like a regular QML item. \snippet interfaceframework/qface-climate/demo/main.qml 0 \dots 0 Our application doesn't know about our back-end plugin, so, we need to put this plugin in the folder where our application looks for plugins. By default, Qt looks in the \b plugins folder within its installation directory or in the application's current working directory. For QtInterfaceFramework plugins to be found, they need to be placed within a \b interfaceframework sub-folder. To make sure this is done automatically, we add the following line to our back-end build system file: \e CMake: \snippet interfaceframework/qface-climate/backend_simulator/CMakeLists.txt 0 \e qmake: \snippet interfaceframework/qface-climate/backend_simulator/backend_simulator.pro 0 */