Use the Generator | Qt Interface Framework (original) (raw)
This topic describes how to use the QtIF generator.
Introduction
The Generator is a Python script that can be run manually or using the Build System Integration. This script uses QFace as the autogenerator framework which parses the Interface Definition Language (IDL) file, generates the domain model (similar to an Abstract Syntax Tree (AST)) and then feeds it to the generator. Depending on the type of the project to generate, different formats
are specified.
Command Line Parameters
To run the generation, use the following command: [QT_HOST_LIBEXECS]/ifcodegen/generate.py -T [QT_INSTALL_DATA]/ifcodegen-templates --format=backend_simulator interface.qface out_dir
The options and parameters are:
Option/Parameter | Description |
---|---|
--reload / --no-reload [optional] | Specifies whether the generator should keep track of the changes in the IDL file and update the output on the fly; the default is --no-reload |
-T, --template-search-path | Adds the given path to the list of search paths. All directories in this list are scanned for generation templates (identified by the Generation YAML). The identified templates can be selected with the –template option. |
-t, --template | Selects the template which should be used for the generation. Templates are searched within the template search path. Instead of the template name, an absolute path to a template can be provided. This template doesn't need to be part of the template search path. The following templates are usually installed by default: frontend qmlplugin backend_simulator backend_qtro server_qtro server_qtro_simulator |
-A, –annotations | Merges the given annotation file with annotations already in the QFace file and the implicit annotation file. These files are merged according to the order in which they are passed to the generator. Providing a duplicate key in the YAML file overrides the previously set value. This option can be used multiple times. For more information, see Merge Annotations. |
-I, –import | Adds the given path to the list of import paths. All directories in this list are scanned recursively for QFace files. The QFace files found are then used to resolve the information required when importing a module; this is similar to how C++ include paths work. |
-P, –target-platform | Specifies the target system architecture for generating files, using values compatible with CMAKE_SYSTEM_NAME. Possible values include Windows, Linux, Android, iOS, and others as defined by CMake. |
source | Path or paths to the IDL source files. If there are multiple entries, each one is handled. If a directory path is provided, it's scanned for IDL files. |
outputdir | The destination folder for the generated files. |
–help | Display options and exit. |
Currently, based on the --template value, the generator can generate multiple types of projects with a given IDL file:
Project Type | Description |
---|---|
frontend | Generates an API using base classes from qtinterfaceframework and the Dynamic Backend System |
qmlplugin | Generates a C++ QML Plugin which registers all frontend types in QML. |
backend_simulator | Generates a simulation backend for the API first generated by the frontend option. This backend serves as a mock implementation. |
backend_qtro | Generates a QtRemoteObjects based backend client for the API first generated by the frontend option. This backend connects to a backend server. |
server_qtro | Generates a QtRemoteObjects based backend server stub for the API first generated by the frontend option. |
server_qtro_simulator | Generates a QtRemoteObjects based simulation server for the API first generated by the frontend option. |
folder path | Uses templates inside the folder. A YAML file with the same name as the folder should provide a list of template files in the folder. This is useful if you want to write your own templates. For more details, see Generation YAML. |
Configure the Generator
The generator's Python script parses the input files and creates a domain model. This domain model is then passed as a context to the Jinja template engine. Use the Generation YAML file to specify which files to generate. Afterwards, you can use an Annotation YAML file to add more information to the IDL file, which is generator specific.
Generation YAML
After the domain model tree is created, this tree is traversed and each leaf of the domain model object tree (module, interface, structure, and so on) is passed to a specific Jinja template defined by the configuration file.
The Generation YAML file defines which template to use to generate which file. Suppose you want to generate a header file for each module in your domain model. But, a module can have multiple interfaces, so, you want to use a different header file for each interface and structure. In this case, the Generation YAML file defines a set of rules to specify which template file to use and how to name them.
This YAML file must have the following structure:
frontend: module: documents: - "{{module.module_name|lower}}plugin.h": "plugin.h.tpl" interface: documents: - '{{interface|lower}}backend.h': 'backend.h.tpl'
For every entity, there's a list of templates that must be called, when traversing this entity in the domain model tree. Here, the YAML file defines a list of documents, which need to be generated for all modules and a list for all interfaces. Every list entry consists of two parts; the first part is the name of the file that needs to be created, as specified in the Jinja template language format. The value of the object property used in the template's name is processed and substituted into the template, thus forming the final name of the file to create. The second part is the name of the template to use. For the Interface Framework generator, you must specify rules for three kinds of entities: modules, interfaces and structures. See the QFace Rule Base Generation Documentation for more information.
Extra Jinja Filters
Sometimes the Jinja filters provided by ifcodegen are not enough, but it is possible to write your own filters using Python. If you add a python script called filters.py
within your template it will be loaded automatically. You can use the following boiler-plate code as a template:
Copyright (C) 2021 The Qt Company Ltd.
SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
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
To share filters also between templates, you can also specify other python scripts which should be loaded in addition. This needs to be done in the Generation YAML file.
The following snippet generates a plugin.h and loads additional filters from a folder called extra-filter:
frontend: extra_filters: [ "extra-filter/filters.py" ] module: documents: - "{{module.module_name|lower}}plugin.h": "plugin.h.tpl"
Annotations YAML
Currently, not all aspects of the interface description can be expressed using the IDL itself. For instance, there is no language construct to define a default value for a property or a range of valid values a property can take. Still, this can be achieved via a mechanism called Annotations. Annotations provide freedom and flexibility to express any concepts and constructs.
The code snippet below shows an example of using annotations in the IDL. Here, we define an interface that is zoned, and specify its ID.
@config: {zoned: true, id: "org.qt-project.interfaceframework.ClimateControl/1.2"}
It does not make sense to place all of the annotations in the main IDL file. For instance, you may need to define some aspects of the auto-test code generation. Such annotations can be put in the YAML file that accompanies the main IDL file, with the same name. During the parse phase QFace automatically picks this file up and merges the annotation specified in this YAML file with those defined in the IDL file.
Since the accompanying YAML file is always picked up automatically, it won't work for annotations that you need for some specific projects, such as when generating a backend plugin. For this use case, you can pass multiple additional annotation YAML files to the generator.
In QtInterfaceFramework, the following annotations are used to define IDLs:
Tag | Where | Object type | Purpose |
---|---|---|---|
@config: {namespace: "module"} | Main IDL file | Module | Defines the C++ namespace the generated code should use. Available options are: Use no namespace (default) qt Use the qt namespace module Use the full module name as namespace Use the provided value as namespace |
@config: {interfaceBuilder: "FunctionName"} | Main IDL file | Module | Declares a function that is called in the plugin to generate the instances for every interface. The function takes a pointer to the plugin instance and returns a QVector<QIfFeatureInterface *>. Interfaces should be generated in the same order as defined by Plugin::interfaces(). Use this tag to instantiate classes derived from the generated plugin interfaces' classes. |
@config: {zoned: true} | Main IDL file | Interface | Tells the generator whether the interface is zoned or not. Use this tag to define whether the backend feature interface is derived from QIfZonedFeatureInterface or QIfFeatureInterface. |
@config: {id: "org.qt.project.interfaceframework.ClimateControl/1.0"} | Main IDL file | Interface | Defines the interface ID, which is a string used by the QtInterfaceFramework service manager to glue a frontend interface with its backend implementation. For more information, see Dynamic Backend System. |
@config: {getter_name: "isHeaterEnabled"} | Main IDL file | Property | Overrides the default getter method's name. Useful for boolean properties, such as the getter for a property: 'enabled', should be 'isEnabled' instead of the default. |
@config: {setter_name: "setHeaterEnabled"} | Main IDL file | Property | Overrides the default setter method's name. |
@config: {qml_name: "ClimateControl"} or @config: {qml_type: "ClimateControl"} | Main IDL file | Module, Interface | Defines the name this interface or module should use in QML. For interfaces, it is the name which is used to export the interface to QML. For modules, it defines the URI of the complete module. The last part of the URI is also used for the singleton that exports all enums to QML. |
@designer: {categoryName: "Smart Home Components"} | Main IDL file | Module, Interface | Defines the category name this interface should be listed under in the Qt Design Studio Library. When defined for a module it sets the category name for all interfaces inside that module, but it can be overridden per interface by defining it there as well. |
@designer: {name: "Climate Control"} | Main IDL file | Interface | Defines the name this interface should be listed under in the Qt Design Studio Library. |
@designer: {typeIcon: "images/climate.png"} | Main IDL file | Interface | The typeIcon is a 16x16 icon used in the Navigator Pane within Qt Design Studio.Note: The icon needs to be copied to the correct folder by a custom build rule. |
@designer: {libraryIcon: "images/climate.png"} | Main IDL file | Interface | The libraryIcon is shown in the Library within Qt Design Studio.Note: The icon needs to be copied to the correct folder by a custom build rule. |
@config: { configurationId: "smarthome"} | Main IDL file | Module, Interface | Defines the configurationId of this interface. The configurationId can be used to specify a configuration for the interface from a central location using QIfConfiguration. When defined for a module, it sets the configurationId for all interfaces inside that module, but it can be overridden per interface by defining it there as well. Defaults to module.name |
@config: { defaultServerMode : "gui" } | Main IDL file | Module | Defines the default mode used by the server generated from the server_qtro_simulator and {QtRemoteObjects Server} {server_qtro} templates. Valid options are "gui", "headless" (default) or "android" (for the Android target platform). |
Annotations that are not logically part of the interface description, but rather the ones used to specify additional information, are put in the accompanying YAML file.
Here is a list of annotations used to define the various aspects of the generated backends and servers:
backend_simulator
Tag | Where | Object type | Purpose |
---|---|---|---|
config_simulator: serviceObjectId: "smarthome_simulation" | Accompanying YAML file | Module | Defines the id of the generated plugin. See QIfServiceObject::id for more information. Defaults to module.name + "_simulation" |
config_simulator: configurationId: "smarthome" | Accompanying YAML file | Module | Defines the configurationId of the generated plugin, which can be used with QIfConfiguration to provide settings to the backend. Defaults to module.name |
config_simulator: simulationFile: ":/qrc/simulation.qml" | Accompanying YAML file | Module | Defines which simulation QML file the simulation backend should load. The snippet provided loads the QML file from the resource system, which the developer needs to embed. |
config_simulator: zones: [ Left, Right ] | Accompanying YAML file | Interface | Defines a list of zones that the simulation code should support, for the backend simulator. |
config_simulator: default: MyFlag.Value1 | MyFlag.Value2 | Accompanying YAML file | Property | Defines the initial values for the property returned by the simulator backend.For zoned properties, you can map a zone to a default value. The default key of the map is "=". config_simulator: default: { Left: 21.0, Right: 22.5, =: 0.0 } |
config_simulator: minimum: 10 | Accompanying YAML file | Property | Defines the minimum value for integer and real properties; the generated code in the simulator backend validates the value. |
config_simulator: maximum: 10 | Accompanying YAML file | Property | Defines the maximum value for integer and real properties; the generated code in the simulator backend validates the value. |
config_simulator: range: [10, 20] | Accompanying YAML file | Property | Defines the range value for integer and real properties; the generated code in the simulator backend validates the value. |
config_simulator: domain: {10, 20, 30} | Accompanying YAML file | Property | Defines the possible values for the property; the generated code in the simulator backend validates the value. |
config_simulator: unsupported: yes | Accompanying YAML file | Property | Defines whether the property is supported; the generated code in the simulator backend validates the value and reports a warning when you try to change an unsupported property. |
backend_qtro
Tag | Where | Object type | Purpose |
---|---|---|---|
config_qtro: serviceObjectId: "smarthome_qtro" | Accompanying YAML file | Module | Defines the id of the generated plugin. See QIfServiceObject::id for more information. Defaults to module.name + "_qtro" |
config_qtro: configurationId: "smarthome" | Accompanying YAML file | Module | Defines the configurationId of the generated plugin, which can be used with QIfConfiguration to provide settings to the backend. Defaults to module.name |
config_server_qtro
Tag | Where | Object type | Purpose |
---|---|---|---|
config_server_qtro: useGeneratedMain: true | Accompanying YAML file | Module | Generates a main.cpp with common command-line options and a QIfRemoteObjectsConfig instance which is passed to a user-provided serverMain function. |
Structure for Generated Projects
In the generator output directory, first, a new subfolder is created and named after the module ID. All the generated files are placed in this folder. The tables below describe the files that are generated for the frontend and backend.
Frontend
Generates a QML-friendly C++ API based on the Dynamic Backend System.
Filename | Purpose |
---|---|
global.h | Standard file with global EXPORT defines. |
module.h/cpp | Files defining a module class used for module global variables and types. |
module_enum.qdocinc | Documentation for all values of all enums which can be included by qdoc. |
modulefactory.h/cpp | Files defining a module factory class used for factory methods for all structs. |
.pri | A standard Qt .pri file that contains all the generated files. Use this .pri file to include the generated files into a qmake project. |
CMakeLists.txt | File to integrate with the CMake build system. This file defines the rules how to build the generated files with CMake. In addition extra variables can be exposed by using qt6_set_ifcodegen_variable. |
qml/{{module|qml_type | replace('.', '/')}}/plugins.qmltypes |
backendinterface.h/cpp | Files defining the interface need to be implemented by the backend implementation of the feature. |
.h/cpp | Frontend implementation of the feature, ready to be used from QML. |
_p.h | Private part of the frontend implementation. |
.h/cpp | Frontend implementation for the struct, implemented as Q_GADGET. |
QML Plugin
Generates a C++ QML Plugin which registers all types from the frontend in QML.
Note: For CMake this template has been superseded by the new QML type registration system. Please see QML Type Registration for more information.
Filename | Purpose |
---|---|
plugin.cpp | The C++ QML Plugin class. |
.pri | A standard Qt .pri file that contains all the generated files. Use this .pri file to include the generated files into a qmake project. |
CMakeLists.txt | File to integrate with the CMake build system. This file defines the rules how to build the generated files with CMake. In addition extra variables can be exposed by using qt6_set_ifcodegen_variable. |
plugins.qmltypes | QML code-completion file for use in QtCreator. |
qmldir | QML config file to register the plugin with the QML plugin system. |
Backend Simulator
Provides a simulator backend using the QIfSimulationEngine to implement the simulation behavior in QML files.
Filename | Purpose |
---|---|
plugin.h/cpp | Files defining implementation of QtInterfaceFramework backend plugin implementing QIfServiceInterface. |
.json | File containing identifiers of the exposed feature interfaces needed by the Qt plugin system. |
.pri | A standard Qt .pri file that contains all the generated files. Use this .pri file to include the generated files into a qmake project. |
CMakeLists.txt | File to integrate with the CMake build system. This file defines the rules how to build the generated files with CMake. In addition extra variables can be exposed by using qt6_set_ifcodegen_variable. |
_simulation.qml | QML simulation file that loads the interface specific QML simulation files. |
_simulation_data.json | Simulation data exported from the config_simulator annotations. |
.qrc | Qt Resource file that contains the QML and JSON files. |
qml/{{module|qml_type | replace('.', '/')}}/plugins.qmltypes |
qml/{{module|qml_type | replace('.', '/')}}/simulation/plugins.qmltypes" |
backend.h/cpp | Files containing the implementation of the simulation backend. |
Simulation.qml | Interface-specific QML simulation files. |
QtRemoteObjects Backend
The backend_qtro template is only available if the QtRemoteObjects module was detected when building the qtinterfaceframework repository. This backend is a client for connecting to the remote backend server; not the location to implement the actual backend logic.
Filename | Purpose |
---|---|
plugin.h/cpp | Files that define the implementation of the QtInterfaceFramework backend plugin, which implements QIfServiceInterface. |
.json | File containing identifiers of the exposed feature interfaces needed by the Qt plugin system. |
.pri | A standard Qt .pri file that contains all the generated files. Use this .pri file to include the generated files into a qmake project. Also includes the .rep file to the project and calls the remote object compiler. |
CMakeLists.txt | File to integrate with the CMake build system. This file defines the rules how to build the generated files with CMake. In addition extra variables can be exposed by using qt6_set_ifcodegen_variable. |
backend.h/cpp | Files containing the implementation of the remote object backend. Establishes the connection and initializes the remote object replica. |
.rep | The input file for Qt’s replica compiler to produce the replica class code. |
pagingmodel.rep | The input file for Qt’s replica compiler to produce the replica class code for all models. |
QtRemoteObjects Server
The server_qtro template is only available if the QtRemoteObjects module was detected when building the qtinterfaceframework repository. The code produced only contains the source classes to inherit and the code for establishing the connection. The developer must implement the actual backend logic.
Filename | Purpose |
---|---|
core.h/cpp | Code for establishing the connection and starting the remoting for the source objects. |
.pri | A standard Qt .pri file that contains all the generated files. Use this .pri file to include the generated files into a qmake project. Also includes the .rep file to the project and calls the remote object compiler. |
CMakeLists.txt | File to integrate with the CMake build system. This file defines the rules how to build the generated files with CMake. In addition extra variables can be exposed by using qt6_set_ifcodegen_variable. |
.rep | The input file for the Qt’s replica compiler to produce the source class code. |
pagingmodel.rep | The input file for Qt’s replica compiler to produce the replica class code for all models. |
QtRemoteObjects Simulation Server
The server_qtro template is only available if the QtRemoteObjects module was detected when building the qtinterfaceframework repository. The code produced contains a fully-implemented server that may use the same implementation as the backend_simulator template, which uses the QIfSimulationEngine to implement the simulation behavior in QML.
By default a QCoreApplication is used in the generated server and enables the server to be run headless. To also allow instantiating UI controls inside the simulation QML code, the server can be started in GUI mode (–gui option). The default mode can be changed using the config_defaultServerMode annotation.
Filename | Purpose |
---|---|
_simulation.qml | QML simulation file which loads the interface specific QML simulation files. |
_simulation_data.json | Simulation data exported from the config_simulator annotations. |
.qrc | Qt Resource file which contains the QML and JSON files. |
qml/{{module|qml_type | replace('.', '/')}}/plugins.qmltypes |
qml/{{module|qml_type | replace('.', '/')}}/simulation/plugins.qmltypes |
core.h/cpp | Code for establishing the connection and starting the remoting for the source objects. |
main.cpp | The main file. |
.pri | A standard Qt .pri file that contains all the generated files. Use this .pri file to include the generated files into a qmake project. Also includes the .rep file to the project and calls the remote object compiler. |
CMakeLists.txt | File to integrate with the CMake build system. This file defines the rules how to build the generated files with CMake. In addition extra variables can be exposed by using qt6_set_ifcodegen_variable. |
.rep | The input file for the Qt’s replica compiler to produce the source class code. |
adapter.h/cpp | QtRemoteObjects Adapter classes for the backend implementations. |
pagingmodel.rep | The input file for Qt’s replica compiler to produce the replica class code for all models. |
backend.h/cpp | Files that contain the simulation backend implementation. |
Simulation.qml | Interface-specific QML simulation files. |
Backend specific configuration option
Some of the generated backends can be provided with service settings using the QIfConfiguration class. The following table describes the available settings per template
Backend Simulator
The generated code doesn't have any service setting itself, but it forwards the serviceSettings to the QML simulation. A handwritten QML simulation can use the serviceSettings property of the BackendInterface object to change the behavior when such a setting is set.
QtRemoteObjects Backend
Name | Description |
---|---|
connectionUrl | The url the interface tries to connect to using Qt Remote Objects. Defaults to local + {{module.module_name|lower}}. Changing this value at runtime will reconnect the backend to the new URL. |
connectionTimeout | Defines when a timeout warning should be printed (in milliseconds). To disable the warning set the timeout to -1. |
In addition to those global settings, the values can also be provided per backend interface and this allows the backend to connect to multiple remote object instances. Interface specific settings need to be prefixed with the interface name. The following example creates a Configuration, which is applied to all Items that are part of the cluster
group. The service settings applied to the currently connected backend use a global connectionTimeout
and a specific connectionUrl
for the InstrumentCluster interface in the cluster module, while all other interfaces use the default 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.