Qt IVI Generator Example

Introduction

This shows an example of using the Qt IVI Generator to build a new component. Based on a single qface IDL file, it will generate:

  • a shared library with the front-end code
  • a back-end simulator plugin
  • a demo application showing the current model values

The IDL File

The IDL file used in the example represents a simplified climate control interface. It contains a single interface and a couple of enumerated types.

Lets first look at a more minimal version of the same QFace IDL file:

module Example.IVI.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
}

Walkthrough

First we need to define which module we want to describe. The module acts as a namespace, because the IDL file can contain multiple interfaces.

module Example.IVI.Climate 1.0;

The most important part is the definition of the interface.

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

In this case, we define an interface named ClimateControl consisting of a few properties it should offer. Every property definition needs to contain at least a type and a name. Most of the basic types are builtin and can be found in the reference. The last two properties are more special as they use custom types, which are defined after the interface definition.

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

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

The first definition is an enum and all the values it supports, including the numeric value of each individual item. The second definition is similar, but using the type flag.

The complete reference for the IDL syntax is available here.

Documentation and Annotations

In the previous section we walked through a simplified version of the example's IDL file. The full IDL file also contains a lot of documentation comments and annotations.

Comments starting with /** define documentation statements and can be converted into common documentation languages like QDoc or Doxygen by the generation template.

Annotations

Annotations are used to add additional information to the IDL statements. They are YAML fragments providing a key-value store. Which annotations are supported is defined by the generation template.

Here's an overview of all the annotations used in this example and what they do:

{@config: {zoned: true}}
indicates the interface supports different zones
{@config: {qml_type: "UiClimateControl"}}
the component name when used from QML
{@config: {id: "example.qtivi.ClimateControl/1.0"}}
indicates the id used for matching backend plugins
{@config_simulator: { range:[0, 50] }}
range of valid values for number type properties
{@config_simulator: { minimum: 0 }}
{@config_simulator: { maximum: 50 }}
minimum and maximum values for number type properties.

Note: range annotation is a shortcut to specifying both minimum and maximum values.

{@config_simulator: { domain: ["cold", "mild", "warm" ] }}
list of valid values for properties
{@config: {interfaceBuilder: "echoInterfaceBuilder"}}
indicates the plugin should use a custom function to generate the backend instances

In addition to the IDL file, a YAML file (with the same basename) is used to add extra configurations. These configurations may also be added directly in to the IDL file, but keeping them separate can improve readability.

Highlights:

Example.IVI.Climate.ClimateControl:
    config_simulator:
        zones: { left : FrontLeft, right : FrontRight, rear: Rear }
the names of the supported zones
Example.IVI.Climate.ClimateControl#recirculationMode:
    config_simulator:
        default: RecirculationMode.RecirculationOff
the default value assigned to the property in the simulator backend plugin.

Frontend library

What the IDL file is and what it contains, was described in the previous section. Now we want to use the ivigenerator to generate a shared library containing a C++ implementation of our module and its interface.

For this, the frontend template is used. This will generate a class derived from QIviAbstractZonedFeature including all the specified properties. The generated library will use the Dynamic Backend System from QtIviCore and by that provide an easy way to change the behavior implementations. More on that in the Backend Simulator Plugin section.

In order to call the autogenerator for our shared library, the qmake project file needs to use the ivigenerator qmake feature. The following snippet shows how it can be added:

CONFIG += ivigenerator
QFACE_SOURCES = ../example-ivi-climate.qface

By adding ivigenerator to the CONFIG variable, the ivigenerator feature file will be loaded and interpret the QFACE_SOURCES variable similar to SOURCES variable of normal qmake projects. Activating the qmake feature using the CONFIG variable has the disadvantage that it doesn't report any errors if the feature is not available. Because of this, it is encouraged to use the following additional code to report errors:

QT_FOR_CONFIG += ivicore
!qtConfig(ivigenerator): error("No ivigenerator available")

The other part of the project file is a normal library setup which is supposed to work on Linux, macOS and Windows.

Backend Simulator Plugin

As mentioned above, the frontend library will use the Dynamic Backend System. This means that for the library to provide some functionality, we also need a backend plugin. A mockup version of the backend plugin called "Simulator Backend" can be generated using the backend_simulator template from the same IDL file as the frontend library. The qmake integration works in the same way, but it is using the QFACE_FORMAT variable to tell the ivigenerator to use a different generation template.

CONFIG += ivigenerator
QFACE_FORMAT = backend_simulator
QFACE_SOURCES = ../example-ivi-climate.qface

As we want to generate a plugin instead of a plain library, we need to instrument qmake to do so by adding plugin to the CONFIG variable. For the plugin to compile correctly it needs to get the backend interface header from the previously created library. As this header is also generated, it is not part of our source tree, but part of the build tree. We do this by adding it to the include path using the following construct:

INCLUDEPATH += $$OUT_PWD/../frontend

The backend_simulator template will make use of the @config_simulator annotations explained above. This means the generated backend will provide the default values defined in the annotations and check the boundaries of new values using the minimum/maximum or range annotations.

Using the zones annotations the generated backend will provide individual values for every zone and communicate the available zones to the frontend library. More about how to work with zones can be seen in the Climate Control QML Example.

Demo Application

The demo application presents a simple QML interface with all the properties of the generated interface.

As we do not provide a QML plugin, the application needs to link to the generated frontend library and call the ClimateModule::registerTypes and ClimateModule::registerQmlTypes methods that are generated in the module singleton to register all autogenerated interfaces and types with the QML engine.

In our QML application, we still need to import the module using the name we provided to ClimateModule::registerQmlTypes. Afterwards the interface can be instantiated like any other QML item.

import IviClimate 1.0

Item {

...

    UiClimateControl {
        id: climateCtrl
    }

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

As our application doesn't know about the existence of our backend plugin, we need to put this plugin in a folder where our application searches for plugins. By default Qt either search in the plugins folder within Qt's installation directory or in the current working directory of the application. For QtIvi plugins to be found, they need to be provided within a qtivi sub-folder. This is achieved automatically by adding the following line to our backend project file:

DESTDIR = ../qtivi

Files:

© 2018 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.