Qt Interface Framework Generator Addressbook Example

This example shows how to generate models using the Qt Interface Framework Generator.

Introduction

This example shows how to generate a model using the model type in a qface file with the Qt Interface Framework Generator.

It will only explain the details on how to use the model type and how it works internally. For a general introduction to the Qt Interface Framework Generator, please have a look at the Qt Interface Framework Generator Climate Example.

Walkthrough

The Interface Definition Language (IDL) file used in the example represents an address book API. It contains a single interface providing the contacts as a model and a struct definition for the actual contact.

interface AddressBook {
    model<Contact> contacts;

    void insertContact(int index, Contact contact);
}

struct Contact {
    string forename;
    string name;
    int phone;
}

The contact property is defined to be of type model<Contact>. The frontend template will create a C++ property of type QIfPagingModel*. The getter function of this property returns a valid instance once a back end is connected and the properties are initialized. This QIfPagingModel instance can be used from C++, as well as from QML and already provides the basic functionality for retrieving its data in an optimized fashion using the so called Pagination concept.

For the back-end interface the property type is different and will be a QIfPagingModelInterface pointer. This is needed as the QIfPagingModel is also a QtInterfaceFramework feature and, like all features, it uses a back-end interface for the front-end - back-end separation. For more information, see Concepts and Architecture.

The back-end plugin needs to implement the QIfPagingModelInterface class for every exposed property. The backend_simulator template already takes care of this and generates all the needed code.

Configuring the Simulation Back End Plugin

By default the generated simulation back end does not populate any data for the model, as the template doesn't know what content it should provide.

For this use-case the default annotation can be used to configure the simulator to provide static simulation data.

This is done in the example-addressbook.yaml file:

Example.If.AddressBookModule:
    config_simulator:
        simulationFile: "qrc:/plugin_resource/simulation.qml"

Example.If.AddressBookModule.AddressBook#contacts:
    config_simulator:
        default: [[ "John", "Doe", "12345" ], [ "Jane", "Doe", "67890" ]]

The JSON fragment assigned to the default variable is parsed by the Qt Interface Framework Generator and will be used to generate a simulation back end which creates two Contact instances and returns them as content for the contacts model.

Demo Application

The demo application is not autogenerated, but a standard QQmlEngine setup for an application similar to other examples.

ListView {
    Layout.fillWidth: true
    Layout.fillHeight: true
    model: addressBook.contacts
    clip: true

    delegate: ItemDelegate {
        width: parent.width
        height: 100
        text: model.item.forename + " " + model.item.name
    }
}

The model is retrieved from the addressbook object using the contacts property and passed to the ListView. The delegate can access the actual contact using the ItemRole of the QIfPagingModel, which is exposed to QML through model.item.

Extended Simulation Behavior

Because the backend_simulator template can only generated a stub, it doesn't know what behavior it should implement for the insertContact function of the qface file. The ifcodegen will simply generate a stub implementation printing a message that this function is not implemented.

This limitation is fixed by using the simulationFile annotation to tell the autogenerator we want to provide our own simulation QML file.

In the example the simulationFile annotation points to a QML file in a resource file. The resource file is added to the build system like this.

CMake:

set(plugin_resource_resource_files
    "simulation.qml"
)

qt_add_resources(addressbook_backend_simulator "plugin_resource"
    PREFIX
        "/plugin_resource"
    FILES
        ${plugin_resource_resource_files}
)

qmake:

RESOURCES += plugin_resource.qrc
Providing the simulation behavior in QML

The auto-generated simulation back-end code loads the simulation behavior from a QML file using a QIfSimulationEngine. This special engine makes sure the auto-generated back-end interfaces are provided to the QML file and they can be extended from there. It also makes sure that the interfaces are available only to this engine instance and to no other engine running in the same process (e.g. in the frontend). See the QIfSimulationEngine documentation for more information about how the engine works.

Using the ifcodegen for the simulation back end, the simulation interfaces are provided in the example.if.addressbook.simulation uri. The provided types are named after the back-end interfaces implemented by the simulation back end. For our example two types are registered:

  • AddressBookBackend
  • ContactsModelBackend

Our simulation QML file looks like this:

import QtQuick
import Example.If.AddressBookModule.simulation

Item {
    AddressBookBackend {
        id: backend
        property var settings : IfSimulator.findData(IfSimulator.simulationData, "AddressBook")

        function initialize() {
            print("AddressBookSimulation INITIALIZE")
            IfSimulator.initializeDefault(settings, backend)
            Base.initialize()
        }

        function insertContact(reply, index, contact) {
            print("BACKEND SIMULATION INSERT CONTACT")
            contacts.insert(index, contact);
            reply.setSuccess(true);
        }

        Component.onCompleted: {
            console.log("BACKEND SIMULATION CREATED")
        }
    }
}

It creates an AddressBookBackend instance and prints a message once the QML code is loaded by using the Component.onCompleted handler.

To implement the behavior for the insertContact function, a JS function is added to the AddressBookBackend object in QML. This function takes three arguments, the first one is an PendingReply object used to notify the front end once the request was successful or failed. The other arguments are as defined in the IDL file.

To insert the provided contact to our list we use the contacts property which hold the implementation of the QIfPagingModelInterface for the contacts property. This implementation provides some extra convenience functions which can be used by the simulation to modify the model in an easy way. In our case we just call the insert() function and let the auto-generated implementation do the rest.

Example project @ code.qt.io

© 2023 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.