diff options
author | Dominik Holland <dominik.holland@qt.io> | 2021-09-16 16:00:07 +0200 |
---|---|---|
committer | Dominik Holland <dominik.holland@qt.io> | 2021-09-22 13:51:56 +0200 |
commit | fe874d5625c324e76f2beccb16b8036498c26165 (patch) | |
tree | 6c832a2f4b42fd0b0bc4f94b53f3f3087f4c90fa /examples | |
parent | 69377143cc5fab6cf68c892305252ef00c0a6d05 (diff) |
Refactor examples and its documentation
* Remove -if- in example names
* Replace _ in example names with -
* Move all related docs and images to the example folder
Pick-to: 6.2
Change-Id: I1fe38c7d4d735c48224c8bdf8622c701ab056070
Reviewed-by: Robert Griebl <robert.griebl@qt.io>
Diffstat (limited to 'examples')
-rw-r--r-- | examples/ifmedia/mediaplayer/doc/images/mediaplayer.png | bin | 0 -> 61704 bytes | |||
-rw-r--r-- | examples/ifmedia/mediaplayer/doc/src/mediaplayer.qdoc | 60 | ||||
-rw-r--r-- | examples/ifmedia/tuner/doc/images/tuner.png | bin | 0 -> 34382 bytes | |||
-rw-r--r-- | examples/ifmedia/tuner/doc/src/tuner.qdoc | 124 | ||||
-rw-r--r-- | examples/ifvehiclefunctions/CMakeLists.txt | 6 | ||||
-rw-r--r-- | examples/ifvehiclefunctions/climate-qml/CMakeLists.txt (renamed from examples/ifvehiclefunctions/climate_qml/CMakeLists.txt) | 0 | ||||
-rw-r--r-- | examples/ifvehiclefunctions/climate-qml/climate-qml.pro (renamed from examples/ifvehiclefunctions/climate_qml/climate_qml.pro) | 0 | ||||
-rw-r--r-- | examples/ifvehiclefunctions/climate-qml/doc/images/climate-qml.png | bin | 0 -> 16135 bytes | |||
-rw-r--r-- | examples/ifvehiclefunctions/climate-qml/doc/src/climate-qml.qdoc | 83 | ||||
-rw-r--r-- | examples/ifvehiclefunctions/climate-qml/main.cpp (renamed from examples/ifvehiclefunctions/climate_qml/main.cpp) | 0 | ||||
-rw-r--r-- | examples/ifvehiclefunctions/climate-qml/main.qml (renamed from examples/ifvehiclefunctions/climate_qml/main.qml) | 0 | ||||
-rw-r--r-- | examples/ifvehiclefunctions/climate-qml/qml.qrc (renamed from examples/ifvehiclefunctions/climate_qml/qml.qrc) | 0 | ||||
-rw-r--r-- | examples/ifvehiclefunctions/climate-widget/CMakeLists.txt (renamed from examples/ifvehiclefunctions/climate_widget/CMakeLists.txt) | 0 | ||||
-rw-r--r-- | examples/ifvehiclefunctions/climate-widget/climate-widget.pro (renamed from examples/ifvehiclefunctions/climate_widget/climate_widget.pro) | 0 | ||||
-rw-r--r-- | examples/ifvehiclefunctions/climate-widget/doc/images/climate-widget.png | bin | 0 -> 10433 bytes | |||
-rw-r--r-- | examples/ifvehiclefunctions/climate-widget/doc/src/climate-widget.qdoc | 81 | ||||
-rw-r--r-- | examples/ifvehiclefunctions/climate-widget/main.cpp (renamed from examples/ifvehiclefunctions/climate_widget/main.cpp) | 0 | ||||
-rw-r--r-- | examples/ifvehiclefunctions/climate-widget/mainwindow.cpp (renamed from examples/ifvehiclefunctions/climate_widget/mainwindow.cpp) | 0 | ||||
-rw-r--r-- | examples/ifvehiclefunctions/climate-widget/mainwindow.h (renamed from examples/ifvehiclefunctions/climate_widget/mainwindow.h) | 0 | ||||
-rw-r--r-- | examples/ifvehiclefunctions/climate-widget/mainwindow.ui (renamed from examples/ifvehiclefunctions/climate_widget/mainwindow.ui) | 0 | ||||
-rw-r--r-- | examples/ifvehiclefunctions/ifvehiclefunctions.pro | 6 | ||||
-rw-r--r-- | examples/ifvehiclefunctions/window-qml/CMakeLists.txt (renamed from examples/ifvehiclefunctions/window_qml/CMakeLists.txt) | 0 | ||||
-rw-r--r-- | examples/ifvehiclefunctions/window-qml/WindowItem.qml (renamed from examples/ifvehiclefunctions/window_qml/WindowItem.qml) | 0 | ||||
-rw-r--r-- | examples/ifvehiclefunctions/window-qml/main.cpp (renamed from examples/ifvehiclefunctions/window_qml/main.cpp) | 0 | ||||
-rw-r--r-- | examples/ifvehiclefunctions/window-qml/main.qml (renamed from examples/ifvehiclefunctions/window_qml/main.qml) | 0 | ||||
-rw-r--r-- | examples/ifvehiclefunctions/window-qml/qml.qrc (renamed from examples/ifvehiclefunctions/window_qml/qml.qrc) | 0 | ||||
-rw-r--r-- | examples/ifvehiclefunctions/window-qml/window-qml.pro (renamed from examples/ifvehiclefunctions/window_qml/window_qml.pro) | 0 | ||||
-rw-r--r-- | examples/interfaceframework/CMakeLists.txt | 6 | ||||
-rw-r--r-- | examples/interfaceframework/interfaceframework.pro | 6 | ||||
-rw-r--r-- | examples/interfaceframework/qface-addressbook/CMakeLists.txt (renamed from examples/interfaceframework/qface-if-addressbook/CMakeLists.txt) | 1 | ||||
-rw-r--r-- | examples/interfaceframework/qface-addressbook/backend_simulator/CMakeLists.txt (renamed from examples/interfaceframework/qface-if-addressbook/backend_simulator/CMakeLists.txt) | 4 | ||||
-rw-r--r-- | examples/interfaceframework/qface-addressbook/backend_simulator/backend_simulator.pro (renamed from examples/interfaceframework/qface-if-addressbook/backend_simulator/backend_simulator.pro) | 4 | ||||
-rw-r--r-- | examples/interfaceframework/qface-addressbook/backend_simulator/plugin_resource.qrc (renamed from examples/interfaceframework/qface-if-addressbook/backend_simulator/plugin_resource.qrc) | 0 | ||||
-rw-r--r-- | examples/interfaceframework/qface-addressbook/backend_simulator/simulation.qml (renamed from examples/interfaceframework/qface-if-addressbook/backend_simulator/simulation.qml) | 0 | ||||
-rw-r--r-- | examples/interfaceframework/qface-addressbook/demo/CMakeLists.txt (renamed from examples/interfaceframework/qface-if-addressbook/demo/CMakeLists.txt) | 16 | ||||
-rw-r--r-- | examples/interfaceframework/qface-addressbook/demo/demo.pro (renamed from examples/interfaceframework/qface-if-addressbook/demo/demo.pro) | 4 | ||||
-rw-r--r-- | examples/interfaceframework/qface-addressbook/demo/main.cpp (renamed from examples/interfaceframework/qface-if-addressbook/demo/main.cpp) | 0 | ||||
-rw-r--r-- | examples/interfaceframework/qface-addressbook/demo/main.qml (renamed from examples/interfaceframework/qface-if-addressbook/demo/main.qml) | 0 | ||||
-rw-r--r-- | examples/interfaceframework/qface-addressbook/demo/qml.qrc (renamed from examples/interfaceframework/qface-if-addressbook/demo/qml.qrc) | 0 | ||||
-rw-r--r-- | examples/interfaceframework/qface-addressbook/doc/images/qface-addressbook.png | bin | 0 -> 14331 bytes | |||
-rw-r--r-- | examples/interfaceframework/qface-addressbook/doc/src/qface-addressbook.qdoc | 152 | ||||
-rw-r--r-- | examples/interfaceframework/qface-addressbook/example-addressbook.qface (renamed from examples/interfaceframework/qface-if-addressbook/example-if-addressbook.qface) | 0 | ||||
-rw-r--r-- | examples/interfaceframework/qface-addressbook/example-addressbook.yaml (renamed from examples/interfaceframework/qface-if-addressbook/example-if-addressbook.yaml) | 0 | ||||
-rw-r--r-- | examples/interfaceframework/qface-addressbook/frontend/CMakeLists.txt (renamed from examples/interfaceframework/qface-if-addressbook/frontend/CMakeLists.txt) | 4 | ||||
-rw-r--r-- | examples/interfaceframework/qface-addressbook/frontend/frontend.pro (renamed from examples/interfaceframework/qface-if-addressbook/frontend/frontend.pro) | 4 | ||||
-rw-r--r-- | examples/interfaceframework/qface-addressbook/qface-addressbook.pro (renamed from examples/interfaceframework/qface-if-addressbook/qface-if-addressbook.pro) | 4 | ||||
-rw-r--r-- | examples/interfaceframework/qface-climate/CMakeLists.txt (renamed from examples/interfaceframework/qface-if-climate/CMakeLists.txt) | 1 | ||||
-rw-r--r-- | examples/interfaceframework/qface-climate/backend_simulator/CMakeLists.txt (renamed from examples/interfaceframework/qface-if-climate/backend_simulator/CMakeLists.txt) | 4 | ||||
-rw-r--r-- | examples/interfaceframework/qface-climate/backend_simulator/backend_simulator.pro (renamed from examples/interfaceframework/qface-if-climate/backend_simulator/backend_simulator.pro) | 4 | ||||
-rw-r--r-- | examples/interfaceframework/qface-climate/demo/CMakeLists.txt (renamed from examples/interfaceframework/qface-if-climate/demo/CMakeLists.txt) | 16 | ||||
-rw-r--r-- | examples/interfaceframework/qface-climate/demo/demo.pro (renamed from examples/interfaceframework/qface-if-climate/demo/demo.pro) | 4 | ||||
-rw-r--r-- | examples/interfaceframework/qface-climate/demo/main.cpp (renamed from examples/interfaceframework/qface-if-climate/demo/main.cpp) | 0 | ||||
-rw-r--r-- | examples/interfaceframework/qface-climate/demo/main.qml (renamed from examples/interfaceframework/qface-if-climate/demo/main.qml) | 0 | ||||
-rw-r--r-- | examples/interfaceframework/qface-climate/demo/qml.qrc (renamed from examples/interfaceframework/qface-if-climate/demo/qml.qrc) | 0 | ||||
-rw-r--r-- | examples/interfaceframework/qface-climate/doc/images/qface-climate.png | bin | 0 -> 39319 bytes | |||
-rw-r--r-- | examples/interfaceframework/qface-climate/doc/src/qface-climate.qdoc | 325 | ||||
-rw-r--r-- | examples/interfaceframework/qface-climate/example-climate.qface (renamed from examples/interfaceframework/qface-if-climate/example-if-climate.qface) | 0 | ||||
-rw-r--r-- | examples/interfaceframework/qface-climate/example-climate.yaml (renamed from examples/interfaceframework/qface-if-climate/example-if-climate.yaml) | 0 | ||||
-rw-r--r-- | examples/interfaceframework/qface-climate/frontend/CMakeLists.txt (renamed from examples/interfaceframework/qface-if-climate/frontend/CMakeLists.txt) | 4 | ||||
-rw-r--r-- | examples/interfaceframework/qface-climate/frontend/frontend.pro (renamed from examples/interfaceframework/qface-if-climate/frontend/frontend.pro) | 4 | ||||
-rw-r--r-- | examples/interfaceframework/qface-climate/qface-climate.pro (renamed from examples/interfaceframework/qface-if-climate/qface-if-climate.pro) | 4 | ||||
-rw-r--r-- | examples/interfaceframework/qface-remote/CMakeLists.txt (renamed from examples/interfaceframework/qface-if-remote/CMakeLists.txt) | 1 | ||||
-rw-r--r-- | examples/interfaceframework/qface-remote/backend_qtro/CMakeLists.txt (renamed from examples/interfaceframework/qface-if-remote/backend_qtro/CMakeLists.txt) | 4 | ||||
-rw-r--r-- | examples/interfaceframework/qface-remote/backend_qtro/backend_qtro.pro (renamed from examples/interfaceframework/qface-if-remote/backend_qtro/backend_qtro.pro) | 4 | ||||
-rw-r--r-- | examples/interfaceframework/qface-remote/demo/CMakeLists.txt (renamed from examples/interfaceframework/qface-if-remote/demo/CMakeLists.txt) | 18 | ||||
-rw-r--r-- | examples/interfaceframework/qface-remote/demo/demo.pro (renamed from examples/interfaceframework/qface-if-remote/demo/demo.pro) | 4 | ||||
-rw-r--r-- | examples/interfaceframework/qface-remote/demo/main.cpp (renamed from examples/interfaceframework/qface-if-remote/demo/main.cpp) | 0 | ||||
-rw-r--r-- | examples/interfaceframework/qface-remote/demo/main.qml (renamed from examples/interfaceframework/qface-if-remote/demo/main.qml) | 0 | ||||
-rw-r--r-- | examples/interfaceframework/qface-remote/demo/qml.qrc (renamed from examples/interfaceframework/qface-if-remote/demo/qml.qrc) | 0 | ||||
-rw-r--r-- | examples/interfaceframework/qface-remote/doc/images/qface-remote.png | bin | 0 -> 11337 bytes | |||
-rw-r--r-- | examples/interfaceframework/qface-remote/doc/src/qface-remote.qdoc | 267 | ||||
-rw-r--r-- | examples/interfaceframework/qface-remote/example-remote.qface (renamed from examples/interfaceframework/qface-if-remote/example-if-remote.qface) | 0 | ||||
-rw-r--r-- | examples/interfaceframework/qface-remote/frontend/CMakeLists.txt (renamed from examples/interfaceframework/qface-if-remote/frontend/CMakeLists.txt) | 4 | ||||
-rw-r--r-- | examples/interfaceframework/qface-remote/frontend/frontend.pro (renamed from examples/interfaceframework/qface-if-remote/frontend/frontend.pro) | 4 | ||||
-rw-r--r-- | examples/interfaceframework/qface-remote/qface-remote.pro (renamed from examples/interfaceframework/qface-if-remote/qface-if-remote.pro) | 2 | ||||
-rw-r--r-- | examples/interfaceframework/qface-remote/server_qtro/CMakeLists.txt (renamed from examples/interfaceframework/qface-if-remote/server_qtro/CMakeLists.txt) | 20 | ||||
-rw-r--r-- | examples/interfaceframework/qface-remote/server_qtro/main.cpp (renamed from examples/interfaceframework/qface-if-remote/server_qtro/main.cpp) | 0 | ||||
-rw-r--r-- | examples/interfaceframework/qface-remote/server_qtro/processingservice.cpp (renamed from examples/interfaceframework/qface-if-remote/server_qtro/processingservice.cpp) | 0 | ||||
-rw-r--r-- | examples/interfaceframework/qface-remote/server_qtro/processingservice.h (renamed from examples/interfaceframework/qface-if-remote/server_qtro/processingservice.h) | 0 | ||||
-rw-r--r-- | examples/interfaceframework/qface-remote/server_qtro/server_qtro.pro (renamed from examples/interfaceframework/qface-if-remote/server_qtro/server_qtro.pro) | 6 | ||||
-rw-r--r-- | examples/interfaceframework/qface-tutorial/doc/images/qface-tutorial-dbus.gif | bin | 0 -> 343966 bytes | |||
-rw-r--r-- | examples/interfaceframework/qface-tutorial/doc/images/qface-tutorial-final.gif | bin | 0 -> 395076 bytes | |||
-rw-r--r-- | examples/interfaceframework/qface-tutorial/doc/images/qface-tutorial-sync.gif | bin | 0 -> 636529 bytes | |||
-rw-r--r-- | examples/interfaceframework/qface-tutorial/doc/images/qface-tutorial-unsync.gif | bin | 0 -> 625820 bytes | |||
-rw-r--r-- | examples/interfaceframework/qface-tutorial/doc/images/qface-tutorial.png | bin | 0 -> 163657 bytes | |||
-rw-r--r-- | examples/interfaceframework/qface-tutorial/doc/src/qface-tutorial.qdoc | 893 |
86 files changed, 2070 insertions, 88 deletions
diff --git a/examples/ifmedia/mediaplayer/doc/images/mediaplayer.png b/examples/ifmedia/mediaplayer/doc/images/mediaplayer.png Binary files differnew file mode 100644 index 00000000..94bb56a9 --- /dev/null +++ b/examples/ifmedia/mediaplayer/doc/images/mediaplayer.png diff --git a/examples/ifmedia/mediaplayer/doc/src/mediaplayer.qdoc b/examples/ifmedia/mediaplayer/doc/src/mediaplayer.qdoc new file mode 100644 index 00000000..81eefa29 --- /dev/null +++ b/examples/ifmedia/mediaplayer/doc/src/mediaplayer.qdoc @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** 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 QtInterfaceFramework module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \example ifmedia/mediaplayer + \title QML Media Player Example + \ingroup qtifmedia-examples + + \brief The Example shows how to use the MediaPlayer API from QML. + \image mediaplayer.png +*/ diff --git a/examples/ifmedia/tuner/doc/images/tuner.png b/examples/ifmedia/tuner/doc/images/tuner.png Binary files differnew file mode 100644 index 00000000..3caf925e --- /dev/null +++ b/examples/ifmedia/tuner/doc/images/tuner.png diff --git a/examples/ifmedia/tuner/doc/src/tuner.qdoc b/examples/ifmedia/tuner/doc/src/tuner.qdoc new file mode 100644 index 00000000..809e26a2 --- /dev/null +++ b/examples/ifmedia/tuner/doc/src/tuner.qdoc @@ -0,0 +1,124 @@ +/**************************************************************************** +** +** 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 QtInterfaceFramework module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \example ifmedia/tuner + \title QML Tuner Example + \ingroup qtifmedia-examples + + \brief This Example shows how to use the Tuner API from QML. + \image tuner.png + + This Example shows how to use the Tuner API from QML. + + First an AmFmTuner object is created. By default, + the auto discovery is used to search for a plugin that implements QIfAmFmTunerBackendInterface. + Depending on the selection of the band radio buttons the tuner band is selected. + \snippet ifmedia/tuner/main.qml 1 + + \section1 Station Information + + In the left third of the UI we want to display information about the current radio station + as well as providing some buttons to change the stations or start a scan through all stations. + \snippet ifmedia/tuner/main.qml 2 + + The station property of AmFmTuner exposes the station you are currently listening to, which can + be empty as well, if the frequency property was manually changed to a frequency no station is + broadcasting on. + + \section1 Station List + + The middle part of the UI shows a list of all the available radio stations. Every item of the + list shows the name and the frequency of a station. By clicking on one of the list items, + the current station will be changed to this station. On the right side of every station is a + \c + button which can be used to save this station into the preset list. + \snippet ifmedia/tuner/main.qml 3 + + To fill the ListView with all available stations, the FilterAndBrowseModel model is used. As + the FilterAndBrowseModel is a generic model, it needs to know where the data should come from. + This is done by passing the service object of the AmFmTuner to the model. The model will then + use the QIfFilterAndBrowseModelInterface exposed by the same backend which is used by AmFmTuner. + Because the tuner backend could expose multiple different lists, the contentType needs to be + selected: in this case the contentType is set to \c station, which provides all available stations. + \snippet ifmedia/tuner/main.qml 4 + + To change the currently playing station the AmFmTuner::tune method is used by calling it + in an onClicked handler + \snippet ifmedia/tuner/main.qml 5 + + \section1 Preset List + + The preset list occupies the right third of the UI and shows all favorite stations. This list + is sorted and maintained by the user. A press on the \c + button of the station list will + add a station to this list, the \c X button will remove the item and the arrow buttons can be + used to change the order of the stations. + \snippet ifmedia/tuner/main.qml 7 + + Similar to the station list, the FilterAndBrowseModel is used as a model, but the contentType + was changed to \c presets. + For maintaining the list, the move and remove functions of FilterAndBrowseModel are used. + + + \section1 Favorite Button + + The \c + button of the station list should be enabled if the station is not already part of + the preset list. This is done by using the FilterAndBrowseModel::indexOf function which will + search for the passed item and call the callback function passed as second argument with the + result. Depending on whether the index is valid, the button will be enabled or disabled. + This asynchronous approach is needed, as the preset list might be pretty big and the data might + come from a different process which maintains the tuner state. + \snippet ifmedia/tuner/main.qml 6 + + If not already part of the preset list, the station is added to the list by using the + FilterAndBrowseModel::insert method, which is passed \c 0 as the first parameter to add it on + top of the list. +*/ diff --git a/examples/ifvehiclefunctions/CMakeLists.txt b/examples/ifvehiclefunctions/CMakeLists.txt index 617352cb..00a8e2e5 100644 --- a/examples/ifvehiclefunctions/CMakeLists.txt +++ b/examples/ifvehiclefunctions/CMakeLists.txt @@ -1,7 +1,7 @@ # Generated from ifvehiclefunctions.pro. -add_subdirectory(climate_qml) -add_subdirectory(window_qml) +add_subdirectory(climate-qml) +add_subdirectory(window-qml) if(TARGET Qt::Widgets) - add_subdirectory(climate_widget) + add_subdirectory(climate-widget) endif() diff --git a/examples/ifvehiclefunctions/climate_qml/CMakeLists.txt b/examples/ifvehiclefunctions/climate-qml/CMakeLists.txt index 4bef4f24..4bef4f24 100644 --- a/examples/ifvehiclefunctions/climate_qml/CMakeLists.txt +++ b/examples/ifvehiclefunctions/climate-qml/CMakeLists.txt diff --git a/examples/ifvehiclefunctions/climate_qml/climate_qml.pro b/examples/ifvehiclefunctions/climate-qml/climate-qml.pro index 8b11c109..8b11c109 100644 --- a/examples/ifvehiclefunctions/climate_qml/climate_qml.pro +++ b/examples/ifvehiclefunctions/climate-qml/climate-qml.pro diff --git a/examples/ifvehiclefunctions/climate-qml/doc/images/climate-qml.png b/examples/ifvehiclefunctions/climate-qml/doc/images/climate-qml.png Binary files differnew file mode 100644 index 00000000..c123ff3c --- /dev/null +++ b/examples/ifvehiclefunctions/climate-qml/doc/images/climate-qml.png diff --git a/examples/ifvehiclefunctions/climate-qml/doc/src/climate-qml.qdoc b/examples/ifvehiclefunctions/climate-qml/doc/src/climate-qml.qdoc new file mode 100644 index 00000000..3fbd2711 --- /dev/null +++ b/examples/ifvehiclefunctions/climate-qml/doc/src/climate-qml.qdoc @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** 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 QtInterfaceFramework module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \example ifvehiclefunctions/climate-qml + \title Climate Control QML Example + \ingroup qtifvehiclefunctions-examples + + \brief The Example shows how to access the climate control from QML. + \image climate-qml.png + + The example shows how to access the climate control from QML. + + In the example, ClimateControl is created and autoDiscovery enabled. + When autoDiscovery is enabled, the module starts searching for the presence + of a plugin that implements QIfClimateControlBackendInterface. + \snippet ifvehiclefunctions/climate-qml/main.qml 1 + + \note To simplify the deployment process, this example loads a simulation backend. + + If discovery fails, a message dialog is shown: + \snippet ifvehiclefunctions/climate-qml/main.qml 4 + + As a Climate System can support multiple climate zones, we need to + differentiate between the zones. A \e {general zone} can be used for settings + that are applied to all zones. + + General ClimateControl attribute values are handled in check boxes as follows: + \snippet ifvehiclefunctions/climate-qml/main.qml 2 + + For the zoned ClimateControl, the \l [QML] {AbstractZonedFeature::}{zoneAt} + property is used for controlling the values from the front left climate zone: + \snippet ifvehiclefunctions/climate-qml/main.qml 3 +*/ diff --git a/examples/ifvehiclefunctions/climate_qml/main.cpp b/examples/ifvehiclefunctions/climate-qml/main.cpp index 0022cd0e..0022cd0e 100644 --- a/examples/ifvehiclefunctions/climate_qml/main.cpp +++ b/examples/ifvehiclefunctions/climate-qml/main.cpp diff --git a/examples/ifvehiclefunctions/climate_qml/main.qml b/examples/ifvehiclefunctions/climate-qml/main.qml index 009794b9..009794b9 100644 --- a/examples/ifvehiclefunctions/climate_qml/main.qml +++ b/examples/ifvehiclefunctions/climate-qml/main.qml diff --git a/examples/ifvehiclefunctions/climate_qml/qml.qrc b/examples/ifvehiclefunctions/climate-qml/qml.qrc index 5f6483ac..5f6483ac 100644 --- a/examples/ifvehiclefunctions/climate_qml/qml.qrc +++ b/examples/ifvehiclefunctions/climate-qml/qml.qrc diff --git a/examples/ifvehiclefunctions/climate_widget/CMakeLists.txt b/examples/ifvehiclefunctions/climate-widget/CMakeLists.txt index fbbac0c6..fbbac0c6 100644 --- a/examples/ifvehiclefunctions/climate_widget/CMakeLists.txt +++ b/examples/ifvehiclefunctions/climate-widget/CMakeLists.txt diff --git a/examples/ifvehiclefunctions/climate_widget/climate_widget.pro b/examples/ifvehiclefunctions/climate-widget/climate-widget.pro index d897dce0..d897dce0 100644 --- a/examples/ifvehiclefunctions/climate_widget/climate_widget.pro +++ b/examples/ifvehiclefunctions/climate-widget/climate-widget.pro diff --git a/examples/ifvehiclefunctions/climate-widget/doc/images/climate-widget.png b/examples/ifvehiclefunctions/climate-widget/doc/images/climate-widget.png Binary files differnew file mode 100644 index 00000000..a0dec9a5 --- /dev/null +++ b/examples/ifvehiclefunctions/climate-widget/doc/images/climate-widget.png diff --git a/examples/ifvehiclefunctions/climate-widget/doc/src/climate-widget.qdoc b/examples/ifvehiclefunctions/climate-widget/doc/src/climate-widget.qdoc new file mode 100644 index 00000000..aa6329a1 --- /dev/null +++ b/examples/ifvehiclefunctions/climate-widget/doc/src/climate-widget.qdoc @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** 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 QtInterfaceFramework module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \example ifvehiclefunctions/climate-widget + \title Climate Control Widget Example + \ingroup qtifvehiclefunctions-examples + + \brief This example shows how to access the climate control from C++. + \image climate-widget.png + + This example shows how to access the climate control from the C++. + + The first thing to do is to create a QIfClimateControl instance in our + MainWindow constructor. As we only have one Climate backend and don't want + to choose which one to use, we call \l {QIfAbstractFeature::}{startAutoDiscovery} + to start searching for a suitable backend right away and pick the first one + that matches. + + \note To simplify the deployment process, this example loads a simulation backend. + + \l {QIfClimateControl::}{isValid()} is used for verifying that the + autoDiscovery found a backend: + \snippet ifvehiclefunctions/climate-widget/mainwindow.cpp 1 + + The constructor then continues to connect the climate control + attribute change signals to the UI components: + \snippet ifvehiclefunctions/climate-widget/mainwindow.cpp 2 + + Airflow direction is controlled using these functions: + \snippet ifvehiclefunctions/climate-widget/mainwindow.cpp 3 +*/ diff --git a/examples/ifvehiclefunctions/climate_widget/main.cpp b/examples/ifvehiclefunctions/climate-widget/main.cpp index 9757cc27..9757cc27 100644 --- a/examples/ifvehiclefunctions/climate_widget/main.cpp +++ b/examples/ifvehiclefunctions/climate-widget/main.cpp diff --git a/examples/ifvehiclefunctions/climate_widget/mainwindow.cpp b/examples/ifvehiclefunctions/climate-widget/mainwindow.cpp index 8f06010a..8f06010a 100644 --- a/examples/ifvehiclefunctions/climate_widget/mainwindow.cpp +++ b/examples/ifvehiclefunctions/climate-widget/mainwindow.cpp diff --git a/examples/ifvehiclefunctions/climate_widget/mainwindow.h b/examples/ifvehiclefunctions/climate-widget/mainwindow.h index 1a43adda..1a43adda 100644 --- a/examples/ifvehiclefunctions/climate_widget/mainwindow.h +++ b/examples/ifvehiclefunctions/climate-widget/mainwindow.h diff --git a/examples/ifvehiclefunctions/climate_widget/mainwindow.ui b/examples/ifvehiclefunctions/climate-widget/mainwindow.ui index 7f53ae27..7f53ae27 100644 --- a/examples/ifvehiclefunctions/climate_widget/mainwindow.ui +++ b/examples/ifvehiclefunctions/climate-widget/mainwindow.ui diff --git a/examples/ifvehiclefunctions/ifvehiclefunctions.pro b/examples/ifvehiclefunctions/ifvehiclefunctions.pro index 19ff424d..d290d4a7 100644 --- a/examples/ifvehiclefunctions/ifvehiclefunctions.pro +++ b/examples/ifvehiclefunctions/ifvehiclefunctions.pro @@ -1,7 +1,7 @@ TEMPLATE = subdirs SUBDIRS += \ - climate_qml \ - window_qml + climate-qml \ + window-qml -qtHaveModule(widgets): SUBDIRS += climate_widget +qtHaveModule(widgets): SUBDIRS += climate-widget diff --git a/examples/ifvehiclefunctions/window_qml/CMakeLists.txt b/examples/ifvehiclefunctions/window-qml/CMakeLists.txt index 41f89141..41f89141 100644 --- a/examples/ifvehiclefunctions/window_qml/CMakeLists.txt +++ b/examples/ifvehiclefunctions/window-qml/CMakeLists.txt diff --git a/examples/ifvehiclefunctions/window_qml/WindowItem.qml b/examples/ifvehiclefunctions/window-qml/WindowItem.qml index 55086efd..55086efd 100644 --- a/examples/ifvehiclefunctions/window_qml/WindowItem.qml +++ b/examples/ifvehiclefunctions/window-qml/WindowItem.qml diff --git a/examples/ifvehiclefunctions/window_qml/main.cpp b/examples/ifvehiclefunctions/window-qml/main.cpp index 0022cd0e..0022cd0e 100644 --- a/examples/ifvehiclefunctions/window_qml/main.cpp +++ b/examples/ifvehiclefunctions/window-qml/main.cpp diff --git a/examples/ifvehiclefunctions/window_qml/main.qml b/examples/ifvehiclefunctions/window-qml/main.qml index b0b234e0..b0b234e0 100644 --- a/examples/ifvehiclefunctions/window_qml/main.qml +++ b/examples/ifvehiclefunctions/window-qml/main.qml diff --git a/examples/ifvehiclefunctions/window_qml/qml.qrc b/examples/ifvehiclefunctions/window-qml/qml.qrc index 4bb0efb9..4bb0efb9 100644 --- a/examples/ifvehiclefunctions/window_qml/qml.qrc +++ b/examples/ifvehiclefunctions/window-qml/qml.qrc diff --git a/examples/ifvehiclefunctions/window_qml/window_qml.pro b/examples/ifvehiclefunctions/window-qml/window-qml.pro index b3f291c9..b3f291c9 100644 --- a/examples/ifvehiclefunctions/window_qml/window_qml.pro +++ b/examples/ifvehiclefunctions/window-qml/window-qml.pro diff --git a/examples/interfaceframework/CMakeLists.txt b/examples/interfaceframework/CMakeLists.txt index 0a80629e..65744ca8 100644 --- a/examples/interfaceframework/CMakeLists.txt +++ b/examples/interfaceframework/CMakeLists.txt @@ -4,10 +4,10 @@ cmake_minimum_required(VERSION 3.14) project(example LANGUAGES CXX) if(QT_FEATURE_ifcodegen) - add_subdirectory(qface-if-climate) - add_subdirectory(qface-if-addressbook) + add_subdirectory(qface-climate) + add_subdirectory(qface-addressbook) endif() if(QT_FEATURE_ifcodegen AND QT_FEATURE_remoteobjects) - add_subdirectory(qface-if-remote) + add_subdirectory(qface-remote) add_subdirectory(qface-tutorial) endif() diff --git a/examples/interfaceframework/interfaceframework.pro b/examples/interfaceframework/interfaceframework.pro index 9c1e40c8..06233a70 100644 --- a/examples/interfaceframework/interfaceframework.pro +++ b/examples/interfaceframework/interfaceframework.pro @@ -2,9 +2,9 @@ TEMPLATE = subdirs QT_FOR_CONFIG += interfaceframework qtConfig(ifcodegen) { - SUBDIRS += qface-if-climate \ - qface-if-addressbook + SUBDIRS += qface-climate \ + qface-addressbook } -qtConfig(ifcodegen): qtConfig(remoteobjects): SUBDIRS += qface-if-remote +qtConfig(ifcodegen): qtConfig(remoteobjects): SUBDIRS += qface-remote qtConfig(ifcodegen): qtConfig(remoteobjects): SUBDIRS += qface-tutorial diff --git a/examples/interfaceframework/qface-if-addressbook/CMakeLists.txt b/examples/interfaceframework/qface-addressbook/CMakeLists.txt index b3394426..4f9f70ef 100644 --- a/examples/interfaceframework/qface-if-addressbook/CMakeLists.txt +++ b/examples/interfaceframework/qface-addressbook/CMakeLists.txt @@ -1,4 +1,3 @@ -# Generated from qface-if-addressbook.pro. cmake_minimum_required(VERSION 3.14) project(example_if_addressbook LANGUAGES CXX) diff --git a/examples/interfaceframework/qface-if-addressbook/backend_simulator/CMakeLists.txt b/examples/interfaceframework/qface-addressbook/backend_simulator/CMakeLists.txt index 27737ebc..a9089b5c 100644 --- a/examples/interfaceframework/qface-if-addressbook/backend_simulator/CMakeLists.txt +++ b/examples/interfaceframework/qface-addressbook/backend_simulator/CMakeLists.txt @@ -9,7 +9,7 @@ if(NOT DEFINED INSTALL_EXAMPLESDIR) set(INSTALL_EXAMPLESDIR "examples") endif() -set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/interfaceframework/qface-if-addressbook/interfaceframework/") +set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/interfaceframework/qface-addressbook/interfaceframework/") find_package(Qt6 COMPONENTS Core) find_package(Qt6 COMPONENTS Gui) @@ -20,7 +20,7 @@ set_target_properties(addressbook_backend_simulator PROPERTIES LIBRARY_OUTPUT_DI # Interface Framework Generator: qt6_ifcodegen_extend_target(addressbook_backend_simulator - IDL_FILES ../example-if-addressbook.qface + IDL_FILES ../example-addressbook.qface TEMPLATE backend_simulator ) diff --git a/examples/interfaceframework/qface-if-addressbook/backend_simulator/backend_simulator.pro b/examples/interfaceframework/qface-addressbook/backend_simulator/backend_simulator.pro index 4dc8239b..72871754 100644 --- a/examples/interfaceframework/qface-if-addressbook/backend_simulator/backend_simulator.pro +++ b/examples/interfaceframework/qface-addressbook/backend_simulator/backend_simulator.pro @@ -12,12 +12,12 @@ QT += core interfaceframework CONFIG += ifcodegen plugin IFCODEGEN_TEMPLATE = backend_simulator -IFCODEGEN_SOURCES = ../example-if-addressbook.qface +IFCODEGEN_SOURCES = ../example-addressbook.qface PLUGIN_TYPE = interfaceframework PLUGIN_CLASS_NAME = AddressBookSimulatorPlugin CONFIG += install_ok # Do not cargo-cult this! -target.path = $$[QT_INSTALL_EXAMPLES]/interfaceframework/qface-if-addressbook/interfaceframework/ +target.path = $$[QT_INSTALL_EXAMPLES]/interfaceframework/qface-addressbook/interfaceframework/ INSTALLS += target #! [0] diff --git a/examples/interfaceframework/qface-if-addressbook/backend_simulator/plugin_resource.qrc b/examples/interfaceframework/qface-addressbook/backend_simulator/plugin_resource.qrc index 7b89debe..7b89debe 100644 --- a/examples/interfaceframework/qface-if-addressbook/backend_simulator/plugin_resource.qrc +++ b/examples/interfaceframework/qface-addressbook/backend_simulator/plugin_resource.qrc diff --git a/examples/interfaceframework/qface-if-addressbook/backend_simulator/simulation.qml b/examples/interfaceframework/qface-addressbook/backend_simulator/simulation.qml index 4f564843..4f564843 100644 --- a/examples/interfaceframework/qface-if-addressbook/backend_simulator/simulation.qml +++ b/examples/interfaceframework/qface-addressbook/backend_simulator/simulation.qml diff --git a/examples/interfaceframework/qface-if-addressbook/demo/CMakeLists.txt b/examples/interfaceframework/qface-addressbook/demo/CMakeLists.txt index 7871e4a6..477c867a 100644 --- a/examples/interfaceframework/qface-if-addressbook/demo/CMakeLists.txt +++ b/examples/interfaceframework/qface-addressbook/demo/CMakeLists.txt @@ -9,7 +9,7 @@ if(NOT DEFINED INSTALL_EXAMPLESDIR) set(INSTALL_EXAMPLESDIR "examples") endif() -set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/interfaceframework/qface-if-addressbook") +set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/interfaceframework/qface-addressbook") find_package(Qt6 COMPONENTS Core) find_package(Qt6 COMPONENTS Gui) @@ -17,20 +17,20 @@ find_package(Qt6 COMPONENTS Qml) find_package(Qt6 COMPONENTS Quick) find_package(Qt6 COMPONENTS InterfaceFramework) -qt_add_executable(qface-if-addressbook +qt_add_executable(qface-addressbook main.cpp ) -set_target_properties(qface-if-addressbook PROPERTIES RUNTIME_OUTPUT_DIRECTORY ../) -set_target_properties(qface-if-addressbook PROPERTIES +set_target_properties(qface-addressbook PROPERTIES RUNTIME_OUTPUT_DIRECTORY ../) +set_target_properties(qface-addressbook PROPERTIES WIN32_EXECUTABLE TRUE MACOSX_BUNDLE FALSE ) -target_compile_definitions(qface-if-addressbook PUBLIC +target_compile_definitions(qface-addressbook PUBLIC QT_DEPRECATED_WARNINGS ) -target_link_libraries(qface-if-addressbook PUBLIC +target_link_libraries(qface-addressbook PUBLIC QtIfAdressBookExample Qt::Core Qt::Gui @@ -44,14 +44,14 @@ set(qml_resource_files "main.qml" ) -qt6_add_resources(qface-if-addressbook "qml" +qt6_add_resources(qface-addressbook "qml" PREFIX "/" FILES ${qml_resource_files} ) -install(TARGETS qface-if-addressbook +install(TARGETS qface-addressbook RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" diff --git a/examples/interfaceframework/qface-if-addressbook/demo/demo.pro b/examples/interfaceframework/qface-addressbook/demo/demo.pro index 9ec597fe..2bba3196 100644 --- a/examples/interfaceframework/qface-if-addressbook/demo/demo.pro +++ b/examples/interfaceframework/qface-addressbook/demo/demo.pro @@ -1,4 +1,4 @@ -TARGET = qface-if-addressbook +TARGET = qface-addressbook QMAKE_PROJECT_NAME = $$TARGET TEMPLATE = app @@ -34,5 +34,5 @@ DEFINES += QT_DEPRECATED_WARNINGS #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 CONFIG += install_ok # Do not cargo-cult this! -target.path = $$[QT_INSTALL_EXAMPLES]/interfaceframework/qface-if-addressbook +target.path = $$[QT_INSTALL_EXAMPLES]/interfaceframework/qface-addressbook INSTALLS += target diff --git a/examples/interfaceframework/qface-if-addressbook/demo/main.cpp b/examples/interfaceframework/qface-addressbook/demo/main.cpp index c0bc5fe3..c0bc5fe3 100644 --- a/examples/interfaceframework/qface-if-addressbook/demo/main.cpp +++ b/examples/interfaceframework/qface-addressbook/demo/main.cpp diff --git a/examples/interfaceframework/qface-if-addressbook/demo/main.qml b/examples/interfaceframework/qface-addressbook/demo/main.qml index f1c6e068..f1c6e068 100644 --- a/examples/interfaceframework/qface-if-addressbook/demo/main.qml +++ b/examples/interfaceframework/qface-addressbook/demo/main.qml diff --git a/examples/interfaceframework/qface-if-addressbook/demo/qml.qrc b/examples/interfaceframework/qface-addressbook/demo/qml.qrc index 5f6483ac..5f6483ac 100644 --- a/examples/interfaceframework/qface-if-addressbook/demo/qml.qrc +++ b/examples/interfaceframework/qface-addressbook/demo/qml.qrc diff --git a/examples/interfaceframework/qface-addressbook/doc/images/qface-addressbook.png b/examples/interfaceframework/qface-addressbook/doc/images/qface-addressbook.png Binary files differnew file mode 100644 index 00000000..7e170f88 --- /dev/null +++ b/examples/interfaceframework/qface-addressbook/doc/images/qface-addressbook.png diff --git a/examples/interfaceframework/qface-addressbook/doc/src/qface-addressbook.qdoc b/examples/interfaceframework/qface-addressbook/doc/src/qface-addressbook.qdoc new file mode 100644 index 00000000..b6958c35 --- /dev/null +++ b/examples/interfaceframework/qface-addressbook/doc/src/qface-addressbook.qdoc @@ -0,0 +1,152 @@ +/**************************************************************************** +** +** 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-addressbook +\brief This example shows how to generate models using the Qt Interface Framework Generator. +\ingroup qtinterfaceframework-examples +\title Qt Interface Framework Generator Addressbook Example +\image qface-addressbook.png + +\section1 Introduction + +This example shows how to generate a model using the \e model type in a qface file with the Qt Interface Framework +Generator. + +It will only explain the details on how to use the \e model type and how it works internally. For a +general introduction to the Qt Interface Framework Generator, please have a look at the +\l {interfaceframework/qface-climate} {Qt Interface Framework Generator Climate Example}. + +\section2 Walkthrough + +The IDL file used in the example represents an addressbook API. It contains a single interface +providing the contacts as a model and a struct definition for the actual contact. + +\snippet interfaceframework/qface-addressbook/example-addressbook.qface 0 + +The \e contact property is defined to be of type \e model<Contact>. The \l frontend template will +create a C++ property of type \l {QIfPagingModel}{QIfPagingModel*}. The getter function of this +property returns a valid instance once a backend 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 \e +Pagination concept. + +For the backend interface the property type is different and will be a \l QIfPagingModelInterface +pointer. This is needed as the QIfPagingModel is also a QtInterfaceFramework feature and, like all features, it +uses a backend interface for the frontend-backend separation. For more information, see +\l{Concepts and Architecture}. + +The backend plugin needs to implement the \l QIfPagingModelInterface class for every exposed +property. +The \l backend_simulator template already takes care of this and generates all the needed code. + +\section1 Configuring the Simulation Backend Plugin + +By default the generated simulation backend does not populate any data for the model, as the +template doesn't know what content it should provide. + +For this use-case the \l {config_simulator_default}{default} annotation can be used to configure +the simulator to provide static simulation data. + +This is done in the example-addressbook.yaml file: + +\quotefile interfaceframework/qface-addressbook/example-addressbook.yaml + +The JSON fragment assigned to the \l {config_simulator_default}{default} variable is parsed by the +Qt Interface Framework Generator and will be used to generate a simulation backend which creates two Contact +instances and returns them as content for the contacts model. + +\section1 Demo Application + +The demo application is not autogenerated, but a standard QQmlEngine setup for an application +similar to other examples. + +\snippet interfaceframework/qface-addressbook/demo/main.qml 0 + +The model is retrieved from the \e addressbook object using the \e contacts property and passed to +the ListView. The delegate can access the actual contact using the \l {QIfPagingModel::}{ItemRole} +of the QIfPagingModel, which is exposed to QML through \e model.item. + +\section1 Extended Simulation Behavior + +Because the \l 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 \l {config_simulator_simulationFile}{simulationFile} +annotation to tell the autogenerator we want to provide our own simulation QML file. + +In the example the \e simulationFile annotation points to a QML file in a resource file. The +resource file is added to the build system like this. + +\e CMake: + +\snippet interfaceframework/qface-addressbook/backend_simulator/CMakeLists.txt 0 + +\e qmake: + +\snippet interfaceframework/qface-addressbook/backend_simulator/backend_simulator.pro 0 + +\section2 Providing the simulation behavior in QML + +The autogenerated simulation backend code loads the simulation behavior from a QML file using a +QIfSimulationEngine. This special engine makes sure the autogenerated backend 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 backend, the simulation interfaces are provided in the \e +example.if.addressbook.simulation uri. The provided types are named after the backend interfaces +implemented by the simulation backend. For our example two types are registered: + +\list + \li AddressBookBackend + \li ContactsModelBackend +\endlist + +Our simulation QML file looks like this: + +\snippet interfaceframework/qface-addressbook/backend_simulator/simulation.qml 0 + +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 frontend 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 \e 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 \c insert() function and let the autogenerated +implementation do the rest. + +*/ diff --git a/examples/interfaceframework/qface-if-addressbook/example-if-addressbook.qface b/examples/interfaceframework/qface-addressbook/example-addressbook.qface index 12eca1ab..12eca1ab 100644 --- a/examples/interfaceframework/qface-if-addressbook/example-if-addressbook.qface +++ b/examples/interfaceframework/qface-addressbook/example-addressbook.qface diff --git a/examples/interfaceframework/qface-if-addressbook/example-if-addressbook.yaml b/examples/interfaceframework/qface-addressbook/example-addressbook.yaml index 2e7a3c1c..2e7a3c1c 100644 --- a/examples/interfaceframework/qface-if-addressbook/example-if-addressbook.yaml +++ b/examples/interfaceframework/qface-addressbook/example-addressbook.yaml diff --git a/examples/interfaceframework/qface-if-addressbook/frontend/CMakeLists.txt b/examples/interfaceframework/qface-addressbook/frontend/CMakeLists.txt index 5f293acc..59c5bb18 100644 --- a/examples/interfaceframework/qface-if-addressbook/frontend/CMakeLists.txt +++ b/examples/interfaceframework/qface-addressbook/frontend/CMakeLists.txt @@ -9,7 +9,7 @@ if(NOT DEFINED INSTALL_EXAMPLESDIR) set(INSTALL_EXAMPLESDIR "examples") endif() -set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/interfaceframework/qface-ifaddressbook") +set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/interfaceframework/qface-addressbook") find_package(Qt6 COMPONENTS Core) find_package(Qt6 COMPONENTS InterfaceFramework) @@ -20,7 +20,7 @@ qt6_add_library(QtIfAdressBookExample) # Interface Framework Generator: qt6_ifcodegen_extend_target(QtIfAdressBookExample - IDL_FILES ../example-if-addressbook.qface + IDL_FILES ../example-addressbook.qface TEMPLATE frontend ) diff --git a/examples/interfaceframework/qface-if-addressbook/frontend/frontend.pro b/examples/interfaceframework/qface-addressbook/frontend/frontend.pro index 93fc30ae..7bfe6a7c 100644 --- a/examples/interfaceframework/qface-if-addressbook/frontend/frontend.pro +++ b/examples/interfaceframework/qface-addressbook/frontend/frontend.pro @@ -7,7 +7,7 @@ QT_FOR_CONFIG += interfaceframework QT += interfaceframework qml quick CONFIG += ifcodegen -IFCODEGEN_SOURCES = ../example-if-addressbook.qface +IFCODEGEN_SOURCES = ../example-addressbook.qface CONFIG += install_ok # Do not cargo-cult this! -target.path = $$[QT_INSTALL_EXAMPLES]/interfaceframework/qface-ifaddressbook +target.path = $$[QT_INSTALL_EXAMPLES]/interfaceframework/qface-addressbook INSTALLS += target diff --git a/examples/interfaceframework/qface-if-addressbook/qface-if-addressbook.pro b/examples/interfaceframework/qface-addressbook/qface-addressbook.pro index 48c88ba8..48028cc6 100644 --- a/examples/interfaceframework/qface-if-addressbook/qface-if-addressbook.pro +++ b/examples/interfaceframework/qface-addressbook/qface-addressbook.pro @@ -9,5 +9,5 @@ CONFIG += ordered OTHER_FILES += DISTFILES += \ - example-if-addressbook.qface \ - example-if-addressbook.yaml + example-addressbook.qface \ + example-addressbook.yaml diff --git a/examples/interfaceframework/qface-if-climate/CMakeLists.txt b/examples/interfaceframework/qface-climate/CMakeLists.txt index 72827137..45fa4804 100644 --- a/examples/interfaceframework/qface-if-climate/CMakeLists.txt +++ b/examples/interfaceframework/qface-climate/CMakeLists.txt @@ -1,4 +1,3 @@ -# Generated from qface-if-climate.pro. cmake_minimum_required(VERSION 3.14) project(example_if_climate LANGUAGES CXX) diff --git a/examples/interfaceframework/qface-if-climate/backend_simulator/CMakeLists.txt b/examples/interfaceframework/qface-climate/backend_simulator/CMakeLists.txt index eacf45a3..5f61f67b 100644 --- a/examples/interfaceframework/qface-if-climate/backend_simulator/CMakeLists.txt +++ b/examples/interfaceframework/qface-climate/backend_simulator/CMakeLists.txt @@ -9,7 +9,7 @@ if(NOT DEFINED INSTALL_EXAMPLESDIR) set(INSTALL_EXAMPLESDIR "examples") endif() -set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/interfaceframework/qface-if-climate/interfaceframework/") +set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/interfaceframework/qface-climate/interfaceframework/") find_package(Qt6 COMPONENTS Core) find_package(Qt6 COMPONENTS Gui) @@ -25,7 +25,7 @@ set_target_properties(climate_backend_simulator PROPERTIES LIBRARY_OUTPUT_DIRECT #! [2] # Interface Framework Generator: qt6_ifcodegen_extend_target(climate_backend_simulator - IDL_FILES ../example-if-climate.qface + IDL_FILES ../example-climate.qface TEMPLATE backend_simulator ) #! [2] diff --git a/examples/interfaceframework/qface-if-climate/backend_simulator/backend_simulator.pro b/examples/interfaceframework/qface-climate/backend_simulator/backend_simulator.pro index 8f92435e..84413d82 100644 --- a/examples/interfaceframework/qface-if-climate/backend_simulator/backend_simulator.pro +++ b/examples/interfaceframework/qface-climate/backend_simulator/backend_simulator.pro @@ -16,10 +16,10 @@ QT += core interfaceframework #! [2] CONFIG += ifcodegen plugin IFCODEGEN_TEMPLATE = backend_simulator -IFCODEGEN_SOURCES = ../example-if-climate.qface +IFCODEGEN_SOURCES = ../example-climate.qface PLUGIN_TYPE = interfaceframework PLUGIN_CLASS_NAME = ClimateSimulatorPlugin #! [2] CONFIG += install_ok # Do not cargo-cult this! -target.path = $$[QT_INSTALL_EXAMPLES]/interfaceframework/qface-if-climate/interfaceframework/ +target.path = $$[QT_INSTALL_EXAMPLES]/interfaceframework/qface-climate/interfaceframework/ INSTALLS += target diff --git a/examples/interfaceframework/qface-if-climate/demo/CMakeLists.txt b/examples/interfaceframework/qface-climate/demo/CMakeLists.txt index 1b0ceb50..9806b26a 100644 --- a/examples/interfaceframework/qface-if-climate/demo/CMakeLists.txt +++ b/examples/interfaceframework/qface-climate/demo/CMakeLists.txt @@ -9,7 +9,7 @@ if(NOT DEFINED INSTALL_EXAMPLESDIR) set(INSTALL_EXAMPLESDIR "examples") endif() -set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/interfaceframework/qface-if-climate") +set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/interfaceframework/qface-climate") find_package(Qt6 COMPONENTS Core) find_package(Qt6 COMPONENTS Gui) @@ -17,20 +17,20 @@ find_package(Qt6 COMPONENTS Qml) find_package(Qt6 COMPONENTS Quick) find_package(Qt6 COMPONENTS InterfaceFramework) -qt_add_executable(qface-if-climate +qt_add_executable(qface-climate main.cpp ) -set_target_properties(qface-if-climate PROPERTIES RUNTIME_OUTPUT_DIRECTORY ..) -set_target_properties(qface-if-climate PROPERTIES +set_target_properties(qface-climate PROPERTIES RUNTIME_OUTPUT_DIRECTORY ..) +set_target_properties(qface-climate PROPERTIES WIN32_EXECUTABLE TRUE MACOSX_BUNDLE FALSE ) -target_compile_definitions(qface-if-climate PUBLIC +target_compile_definitions(qface-climate PUBLIC QT_DEPRECATED_WARNINGS ) -target_link_libraries(qface-if-climate PUBLIC +target_link_libraries(qface-climate PUBLIC QtIfClimateExample Qt::Core Qt::Gui @@ -44,14 +44,14 @@ set(qml_resource_files "main.qml" ) -qt6_add_resources(qface-if-climate "qml" +qt6_add_resources(qface-climate "qml" PREFIX "/" FILES ${qml_resource_files} ) -install(TARGETS qface-if-climate +install(TARGETS qface-climate RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" diff --git a/examples/interfaceframework/qface-if-climate/demo/demo.pro b/examples/interfaceframework/qface-climate/demo/demo.pro index 52790c3e..7a26ef25 100644 --- a/examples/interfaceframework/qface-if-climate/demo/demo.pro +++ b/examples/interfaceframework/qface-climate/demo/demo.pro @@ -1,4 +1,4 @@ -TARGET = qface-if-climate +TARGET = qface-climate QMAKE_PROJECT_NAME = $$TARGET TEMPLATE = app @@ -34,5 +34,5 @@ DEFINES += QT_DEPRECATED_WARNINGS #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 CONFIG += install_ok # Do not cargo-cult this! -target.path = $$[QT_INSTALL_EXAMPLES]/interfaceframework/qface-if-climate +target.path = $$[QT_INSTALL_EXAMPLES]/interfaceframework/qface-climate INSTALLS += target diff --git a/examples/interfaceframework/qface-if-climate/demo/main.cpp b/examples/interfaceframework/qface-climate/demo/main.cpp index 3fc0f846..3fc0f846 100644 --- a/examples/interfaceframework/qface-if-climate/demo/main.cpp +++ b/examples/interfaceframework/qface-climate/demo/main.cpp diff --git a/examples/interfaceframework/qface-if-climate/demo/main.qml b/examples/interfaceframework/qface-climate/demo/main.qml index f6723db6..f6723db6 100644 --- a/examples/interfaceframework/qface-if-climate/demo/main.qml +++ b/examples/interfaceframework/qface-climate/demo/main.qml diff --git a/examples/interfaceframework/qface-if-climate/demo/qml.qrc b/examples/interfaceframework/qface-climate/demo/qml.qrc index 5f6483ac..5f6483ac 100644 --- a/examples/interfaceframework/qface-if-climate/demo/qml.qrc +++ b/examples/interfaceframework/qface-climate/demo/qml.qrc diff --git a/examples/interfaceframework/qface-climate/doc/images/qface-climate.png b/examples/interfaceframework/qface-climate/doc/images/qface-climate.png Binary files differnew file mode 100644 index 00000000..7c4f943c --- /dev/null +++ b/examples/interfaceframework/qface-climate/doc/images/qface-climate.png diff --git a/examples/interfaceframework/qface-climate/doc/src/qface-climate.qdoc b/examples/interfaceframework/qface-climate/doc/src/qface-climate.qdoc new file mode 100644 index 00000000..7cfc0ac1 --- /dev/null +++ b/examples/interfaceframework/qface-climate/doc/src/qface-climate.qdoc @@ -0,0 +1,325 @@ +/**************************************************************************** +** +** 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 IDL file, the example generates: + +\list + \li a shared library with the frontend code + \li a backend 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 basename 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 backend plugin. +\endtable + + +\section1 Frontend 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 {Backend 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 Backend Simulator Plugin + +Since the \c frontend library uses the \l {Dynamic Backend System}, we need a corresponding +\c backend plugin, for the library to provide some functionality. To generate a mock version of +the backend plugin called "Simulator Backend", you can use the \c backend_simulator template from +the same IDL file as the \c frontend 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 CMake: + +\snippet interfaceframework/qface-climate/backend_simulator/CMakeLists.txt 1 + +\e qmake: + +\snippet interfaceframework/qface-climate/backend_simulator/backend_simulator.pro 1 + +The \c backend_simulator template makes use of the \b @config_simulator annotations explained +\l{Annotations}{above}. This means that the generated backend 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 backend provides individual values for every zone +and communicates the available zones to the frontend 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 frontend +library and call the \c {ClimateModule::registerTypes} and \c {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 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 backend 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 backend 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 + +*/ diff --git a/examples/interfaceframework/qface-if-climate/example-if-climate.qface b/examples/interfaceframework/qface-climate/example-climate.qface index 49187e3b..49187e3b 100644 --- a/examples/interfaceframework/qface-if-climate/example-if-climate.qface +++ b/examples/interfaceframework/qface-climate/example-climate.qface diff --git a/examples/interfaceframework/qface-if-climate/example-if-climate.yaml b/examples/interfaceframework/qface-climate/example-climate.yaml index f774fe35..f774fe35 100644 --- a/examples/interfaceframework/qface-if-climate/example-if-climate.yaml +++ b/examples/interfaceframework/qface-climate/example-climate.yaml diff --git a/examples/interfaceframework/qface-if-climate/frontend/CMakeLists.txt b/examples/interfaceframework/qface-climate/frontend/CMakeLists.txt index f2d6cb43..5f4e0df9 100644 --- a/examples/interfaceframework/qface-if-climate/frontend/CMakeLists.txt +++ b/examples/interfaceframework/qface-climate/frontend/CMakeLists.txt @@ -9,7 +9,7 @@ if(NOT DEFINED INSTALL_EXAMPLESDIR) set(INSTALL_EXAMPLESDIR "examples") endif() -set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/interfaceframework/qface-if-climate") +set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/interfaceframework/qface-climate") find_package(Qt6 COMPONENTS Core) find_package(Qt6 COMPONENTS Gui) @@ -24,7 +24,7 @@ qt6_add_library(QtIfClimateExample) # Interface Framework Generator: qt6_ifcodegen_extend_target(QtIfClimateExample - IDL_FILES ../example-if-climate.qface + IDL_FILES ../example-climate.qface TEMPLATE frontend ) #! [1] diff --git a/examples/interfaceframework/qface-if-climate/frontend/frontend.pro b/examples/interfaceframework/qface-climate/frontend/frontend.pro index 2a1f9015..45bd140c 100644 --- a/examples/interfaceframework/qface-if-climate/frontend/frontend.pro +++ b/examples/interfaceframework/qface-climate/frontend/frontend.pro @@ -10,8 +10,8 @@ QT += interfaceframework qml quick #! [1] CONFIG += ifcodegen -IFCODEGEN_SOURCES = ../example-if-climate.qface +IFCODEGEN_SOURCES = ../example-climate.qface #! [1] CONFIG += install_ok # Do not cargo-cult this! -target.path = $$[QT_INSTALL_EXAMPLES]/interfaceframework/qface-if-climate +target.path = $$[QT_INSTALL_EXAMPLES]/interfaceframework/qface-climate INSTALLS += target diff --git a/examples/interfaceframework/qface-if-climate/qface-if-climate.pro b/examples/interfaceframework/qface-climate/qface-climate.pro index 6879e643..769d621d 100644 --- a/examples/interfaceframework/qface-if-climate/qface-if-climate.pro +++ b/examples/interfaceframework/qface-climate/qface-climate.pro @@ -7,5 +7,5 @@ SUBDIRS = frontend \ CONFIG += ordered OTHER_FILES += \ - example-if-climate.qface \ - example-if-climate.yaml + example-climate.qface \ + example-climate.yaml diff --git a/examples/interfaceframework/qface-if-remote/CMakeLists.txt b/examples/interfaceframework/qface-remote/CMakeLists.txt index 9b6d2fc7..60d58632 100644 --- a/examples/interfaceframework/qface-if-remote/CMakeLists.txt +++ b/examples/interfaceframework/qface-remote/CMakeLists.txt @@ -1,4 +1,3 @@ -# Generated from qface-if-remote.pro. cmake_minimum_required(VERSION 3.14) project(example_if_remote LANGUAGES CXX) diff --git a/examples/interfaceframework/qface-if-remote/backend_qtro/CMakeLists.txt b/examples/interfaceframework/qface-remote/backend_qtro/CMakeLists.txt index 5628738f..4ebb32a2 100644 --- a/examples/interfaceframework/qface-if-remote/backend_qtro/CMakeLists.txt +++ b/examples/interfaceframework/qface-remote/backend_qtro/CMakeLists.txt @@ -9,7 +9,7 @@ if(NOT DEFINED INSTALL_EXAMPLESDIR) set(INSTALL_EXAMPLESDIR "examples") endif() -set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/interfaceframework/qface-if-remote/interfaceframework/") +set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/interfaceframework/qface-remote/interfaceframework/") find_package(Qt6 COMPONENTS Core) find_package(Qt6 COMPONENTS Gui) @@ -25,7 +25,7 @@ set_target_properties(remote_backend_qtro PROPERTIES LIBRARY_OUTPUT_DIRECTORY .. #! [2] # Interface Framework Generator: qt6_ifcodegen_extend_target(remote_backend_qtro - IDL_FILES ../example-if-remote.qface + IDL_FILES ../example-remote.qface TEMPLATE backend_qtro ) #! [2] diff --git a/examples/interfaceframework/qface-if-remote/backend_qtro/backend_qtro.pro b/examples/interfaceframework/qface-remote/backend_qtro/backend_qtro.pro index be83f15e..4921ee5f 100644 --- a/examples/interfaceframework/qface-if-remote/backend_qtro/backend_qtro.pro +++ b/examples/interfaceframework/qface-remote/backend_qtro/backend_qtro.pro @@ -16,10 +16,10 @@ QT += core interfaceframework #! [2] CONFIG += ifcodegen plugin IFCODEGEN_TEMPLATE = backend_qtro -IFCODEGEN_SOURCES = ../example-if-remote.qface +IFCODEGEN_SOURCES = ../example-remote.qface PLUGIN_TYPE = interfaceframework PLUGIN_CLASS_NAME = RemoteClientQtROPlugin #! [2] CONFIG += install_ok # Do not cargo-cult this! -target.path = $$[QT_INSTALL_EXAMPLES]/interfaceframework/qface-if-remote/interfaceframework/ +target.path = $$[QT_INSTALL_EXAMPLES]/interfaceframework/qface-remote/interfaceframework/ INSTALLS += target diff --git a/examples/interfaceframework/qface-if-remote/demo/CMakeLists.txt b/examples/interfaceframework/qface-remote/demo/CMakeLists.txt index 01322e8b..8a1b626f 100644 --- a/examples/interfaceframework/qface-if-remote/demo/CMakeLists.txt +++ b/examples/interfaceframework/qface-remote/demo/CMakeLists.txt @@ -1,7 +1,7 @@ # Generated from demo.pro. cmake_minimum_required(VERSION 3.14) -project(qface-if-remote LANGUAGES CXX) +project(qface-remote LANGUAGES CXX) set(CMAKE_INCLUDE_CURRENT_DIR ON) @@ -13,7 +13,7 @@ if(NOT DEFINED INSTALL_EXAMPLESDIR) set(INSTALL_EXAMPLESDIR "examples") endif() -set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/interfaceframework/qface-if-remote") +set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/interfaceframework/qface-remote") find_package(Qt6 COMPONENTS Core) find_package(Qt6 COMPONENTS Gui) @@ -21,20 +21,20 @@ find_package(Qt6 COMPONENTS Qml) find_package(Qt6 COMPONENTS Quick) find_package(Qt6 COMPONENTS InterfaceFramework) -qt_add_executable(qface-if-remote +qt_add_executable(qface-remote main.cpp ) -set_target_properties(qface-if-remote PROPERTIES RUNTIME_OUTPUT_DIRECTORY ..) -set_target_properties(qface-if-remote PROPERTIES +set_target_properties(qface-remote PROPERTIES RUNTIME_OUTPUT_DIRECTORY ..) +set_target_properties(qface-remote PROPERTIES WIN32_EXECUTABLE TRUE MACOSX_BUNDLE FALSE ) -target_compile_definitions(qface-if-remote PUBLIC +target_compile_definitions(qface-remote PUBLIC QT_DEPRECATED_WARNINGS ) -target_link_libraries(qface-if-remote PUBLIC +target_link_libraries(qface-remote PUBLIC QtIfRemoteExample Qt::Core Qt::Gui @@ -48,14 +48,14 @@ set(qml_resource_files "main.qml" ) -qt6_add_resources(qface-if-remote "qml" +qt6_add_resources(qface-remote "qml" PREFIX "/" FILES ${qml_resource_files} ) -install(TARGETS qface-if-remote +install(TARGETS qface-remote RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" diff --git a/examples/interfaceframework/qface-if-remote/demo/demo.pro b/examples/interfaceframework/qface-remote/demo/demo.pro index ed97d881..ca6ee5f0 100644 --- a/examples/interfaceframework/qface-if-remote/demo/demo.pro +++ b/examples/interfaceframework/qface-remote/demo/demo.pro @@ -1,4 +1,4 @@ -TARGET = qface-if-remote +TARGET = qface-remote QMAKE_PROJECT_NAME = $$TARGET TEMPLATE = app @@ -34,5 +34,5 @@ DEFINES += QT_DEPRECATED_WARNINGS #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 CONFIG += install_ok # Do not cargo-cult this! -target.path = $$[QT_INSTALL_EXAMPLES]/interfaceframework/qface-if-remote +target.path = $$[QT_INSTALL_EXAMPLES]/interfaceframework/qface-remote INSTALLS += target diff --git a/examples/interfaceframework/qface-if-remote/demo/main.cpp b/examples/interfaceframework/qface-remote/demo/main.cpp index faecda76..faecda76 100644 --- a/examples/interfaceframework/qface-if-remote/demo/main.cpp +++ b/examples/interfaceframework/qface-remote/demo/main.cpp diff --git a/examples/interfaceframework/qface-if-remote/demo/main.qml b/examples/interfaceframework/qface-remote/demo/main.qml index 61e0d1a9..61e0d1a9 100644 --- a/examples/interfaceframework/qface-if-remote/demo/main.qml +++ b/examples/interfaceframework/qface-remote/demo/main.qml diff --git a/examples/interfaceframework/qface-if-remote/demo/qml.qrc b/examples/interfaceframework/qface-remote/demo/qml.qrc index 5f6483ac..5f6483ac 100644 --- a/examples/interfaceframework/qface-if-remote/demo/qml.qrc +++ b/examples/interfaceframework/qface-remote/demo/qml.qrc diff --git a/examples/interfaceframework/qface-remote/doc/images/qface-remote.png b/examples/interfaceframework/qface-remote/doc/images/qface-remote.png Binary files differnew file mode 100644 index 00000000..fc671c7d --- /dev/null +++ b/examples/interfaceframework/qface-remote/doc/images/qface-remote.png diff --git a/examples/interfaceframework/qface-remote/doc/src/qface-remote.qdoc b/examples/interfaceframework/qface-remote/doc/src/qface-remote.qdoc new file mode 100644 index 00000000..20d801af --- /dev/null +++ b/examples/interfaceframework/qface-remote/doc/src/qface-remote.qdoc @@ -0,0 +1,267 @@ +/**************************************************************************** +** +** 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-remote +\brief Learn how to use the Qt Interface Framework Generator to create QtRemoteObjects based +backends. +\ingroup qtinterfaceframework-examples +\title Generate QtRemoteObjects based backends with the Qt Interface Framework Generator +\image qface-remote.png + +\section1 Introduction + +This example shows how to generate a Middleware API, a Middleware Backend, and the +corresponding Middleware Service using the Qt Interface Framework Generator. The communication +between the backend and the service is done with QtRemoteObjects as the IPC. + +We use a single QFace IDL file to generate: + +\list +\li a shared library with the front-end code +\li a backend plugin that implements a client to connect to the server +\li a server that runs the actual backend logic in a separate server process +\li a demo application that connects to the server and provides a UI to use the service +\endlist + +In addition to the generated C++ code, the backend plugin and the server also contain an +intermediate \c{.rep} file that is further processed by the +\l [QtRemoteObjects] {Qt Remote Objects Compiler} {replica compiler} to produce the source and +replica classes. + +\section2 Walkthrough + +The IDL file used in the example represents an imaginary remote service for processing data. It +contains a single interface with one property and one method. + +First, we need to define which \e module we want to describe. The \e module acts as a namespace, +because the IDL file can contain multiple interfaces. + +\snippet interfaceframework/qface-remote/example-remote.qface 0 + +The most important part is the definition of the \e interface. + +\snippet interfaceframework/qface-remote/example-remote.qface 1 + +In this case, we define an \e interface named \b ProcessingService with one property and one +method. Every property and method definition needs to 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}. + + +\section1 Frontend library + +Next, we use the Interface Framework Generator to generate a shared library containing a C++ +implementation of our module and its interface; particularly the \e frontend template. This template +generates a class derived from \l{QIfAbstractFeature}, that includes all of the specified +properties. The generated library uses the \l{Dynamic Backend System} from QtInterfaceFramework, +consequently providing an easy way to change how the behavior is implemented. + +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-remote/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-remote/frontend/CMakeLists.txt 1 + +\e qmake: + +\snippet interfaceframework/qface-remote/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, similar to the \c SOURCES variable in regular qmake +projects. However, activating the qmake feature via the \c CONFIG variable has one disadvantage: if +the feature is not available, no errors are reported. We recommend using the following additional +code to report errors: + +\snippet interfaceframework/qface-remote/frontend/frontend.pro 0 + +The remaining part of the project file is a normal library setup that works on Linux, macOS, and +Windows. + + +\section1 QtRemoteObjects Backend Plugin + +As mentioned above, the \e frontend library uses the \l{Dynamic Backend System}. This means +that for the library to provide some functionality, we also need a \e backend plugin. The generated +plugin here works as a client that connects to the server using Qt Remote Objects. 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-remote/backend_qtro/CMakeLists.txt 2 + +\e qmake: + +\snippet interfaceframework/qface-remote/backend_qtro/backend_qtro.pro 2 + +The generated backend plugin code is usable as is, and doesn't require any further change. 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. + +For the plugin to compile correctly it needs to get the +backend interface header from the previously created library. But this header is also generated, so +it's not part of our source tree, but part of the build tree. To provide the backend interface +header, we add it to the include path using the following code: + +\e CMake: + +\snippet interfaceframework/qface-remote/backend_qtro/CMakeLists.txt 1 + +\e qmake: + +\snippet interfaceframework/qface-remote/backend_qtro/backend_qtro.pro 1 + +Most of the code in the backend plugin is generated by the Interface Framework Generator, but some +of it is generated by the Qt's Remote Object Compiler, \c repc. To achieve this, the Interface +Framework Generator produces an intermediate \c{.repc} file that's further processed by the \c repc +compiler. This compiler is called via the generated build system file, found in the build directory. + + +Our application doesn't know about the existence of our backend plugin, so we need to put this +plugin in a folder where the application typically looks for plugins. By default, Qt either +searches 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 provided within a +\b interfaceframework sub-folder. Add the following line to the backend build system file, as +follows: + +\e CMake: + +\snippet interfaceframework/qface-remote/backend_qtro/CMakeLists.txt 0 + +\e qmake: + +\snippet interfaceframework/qface-remote/backend_qtro/backend_qtro.pro 0 + + +\section1 RemoteObjects Server + +The server is an independent, GUI-less application that contains the backend's business logic, and +we need to write most of its implementation. Nevertheless, the generator produces some code to +simplify the development. We can generate server side code by using the Interface Framework +Generator with the \e server_qtro template: + +\e CMake: + +\snippet interfaceframework/qface-remote/server_qtro/CMakeLists.txt 0 + +\e qmake: + +\snippet interfaceframework/qface-remote/server_qtro/server_qtro.pro 0 +\dots 0 +\snippet interfaceframework/qface-remote/server_qtro/server_qtro.pro 1 + +To use the generated remote source, we need to inherit from one of the classes defined in the +generated \c rep_processingservice_source.h file. In this example, we implement our server's +logic in the \c ProcessingService class and use the \c ProcessingServiceSimpleSource as the base +class: + +\code +// server_qtro/processingservice.h +\endcode +\snippet interfaceframework/qface-remote/server_qtro/processingservice.h 0 + +Note that the base class already has the definitions for property accessors, but any custom +method or slot needs to be overridden and defined. Our implementation of the process function +merely counts and returns the length of the data passed and updates the \c lastMessage property: + +\code +// server_qtro/processingservice.cpp +\endcode +\snippet interfaceframework/qface-remote/server_qtro/processingservice.cpp 0 + +To make the \c ProcessingService class accessible remotely, we need to share it via the +QRemoteObjectNode::enableRemoting() function. The \c Core class generated provides a preconfigured +instance of a \c remotenode that is used for the remoting. For the plugin to connect to the right +object, use an identifier in the format "ModuleName.InterfaceName", which in our case is +"Example.If.Remote.ProcessingService". All this is done in the \c main() function, along with +the start of the main event loop: + +\code +// server_qtro/main.cpp +\endcode +\snippet interfaceframework/qface-remote/server_qtro/main.cpp 0 + +This is all you need to do to implement a service that is accessible remotely; use the properties +as usual and provide the method implementations. The QtRemoteObjects library takes care of the +communication. + + +\section1 Demo Client Application + +The demo application presents a simple QML GUI to use the remote service over the generated +interface. + +As we do not provide a QML plugin, the application needs to link to the generated frontend library +and call the \c{RemoteModule::registerTypes} and \c{RemoteModule::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 same module URI which is used +in the QFace file. Afterwards the interface can be instantiated like any other QML item. + +\code +// demo/main.qml +\endcode +\snippet interfaceframework/qface-remote/demo/main.qml 0 +\dots 0 + +Every method call that is made through a generated API, is asynchronous. This means that instead +of directly returning a return value, a QIfPendingReply object is returned. Using the +\l QIfPendingReply::then() method on the returned object, we may assign callbacks to it that are +called when the method call has been successfully finished; or if it has failed. + +\code +// demo/main.qml +\endcode +\snippet interfaceframework/qface-remote/demo/main.qml 1 + +In case of properties, we use bindings as usual: + +\code +// demo/main.qml +\endcode +\snippet interfaceframework/qface-remote/demo/main.qml 2 + +\section1 Running the Example + +To see the demo's entire functionality, run both the server and the demo application +simultaneously. You may leave the server running and restart the application, or vice versa, +to see that the reconnection works. Run the demo application alone without the server running, +to test how the remote method call fails when there is no connection. + +*/ diff --git a/examples/interfaceframework/qface-if-remote/example-if-remote.qface b/examples/interfaceframework/qface-remote/example-remote.qface index 4354e9e7..4354e9e7 100644 --- a/examples/interfaceframework/qface-if-remote/example-if-remote.qface +++ b/examples/interfaceframework/qface-remote/example-remote.qface diff --git a/examples/interfaceframework/qface-if-remote/frontend/CMakeLists.txt b/examples/interfaceframework/qface-remote/frontend/CMakeLists.txt index 0e756e4f..0b2342f8 100644 --- a/examples/interfaceframework/qface-if-remote/frontend/CMakeLists.txt +++ b/examples/interfaceframework/qface-remote/frontend/CMakeLists.txt @@ -13,7 +13,7 @@ if(NOT DEFINED INSTALL_EXAMPLESDIR) set(INSTALL_EXAMPLESDIR "examples") endif() -set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/interfaceframework/qface-if-remote") +set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/interfaceframework/qface-remote") find_package(Qt6 COMPONENTS Core) #! [0] @@ -27,7 +27,7 @@ qt6_add_library(QtIfRemoteExample) # Interface Framework Generator: qt6_ifcodegen_extend_target(QtIfRemoteExample - IDL_FILES ../example-if-remote.qface + IDL_FILES ../example-remote.qface TEMPLATE frontend ) #! [1] diff --git a/examples/interfaceframework/qface-if-remote/frontend/frontend.pro b/examples/interfaceframework/qface-remote/frontend/frontend.pro index 1765afb3..bebdf360 100644 --- a/examples/interfaceframework/qface-if-remote/frontend/frontend.pro +++ b/examples/interfaceframework/qface-remote/frontend/frontend.pro @@ -10,8 +10,8 @@ QT += interfaceframework qml quick #! [1] CONFIG += ifcodegen -IFCODEGEN_SOURCES = ../example-if-remote.qface +IFCODEGEN_SOURCES = ../example-remote.qface #! [1] CONFIG += install_ok # Do not cargo-cult this! -target.path = $$[QT_INSTALL_EXAMPLES]/interfaceframework/qface-if-remote +target.path = $$[QT_INSTALL_EXAMPLES]/interfaceframework/qface-remote INSTALLS += target diff --git a/examples/interfaceframework/qface-if-remote/qface-if-remote.pro b/examples/interfaceframework/qface-remote/qface-remote.pro index 085236b2..0dbdb0b6 100644 --- a/examples/interfaceframework/qface-if-remote/qface-if-remote.pro +++ b/examples/interfaceframework/qface-remote/qface-remote.pro @@ -8,4 +8,4 @@ SUBDIRS = frontend \ CONFIG += ordered OTHER_FILES += \ - example-if-remote.qface + example-remote.qface diff --git a/examples/interfaceframework/qface-if-remote/server_qtro/CMakeLists.txt b/examples/interfaceframework/qface-remote/server_qtro/CMakeLists.txt index 17975374..185caee9 100644 --- a/examples/interfaceframework/qface-if-remote/server_qtro/CMakeLists.txt +++ b/examples/interfaceframework/qface-remote/server_qtro/CMakeLists.txt @@ -1,7 +1,7 @@ # Generated from server_qtro.pro. cmake_minimum_required(VERSION 3.14) -project(qface-if-remote-server LANGUAGES CXX) +project(qface-remote-server LANGUAGES CXX) set(CMAKE_INCLUDE_CURRENT_DIR ON) @@ -13,43 +13,43 @@ if(NOT DEFINED INSTALL_EXAMPLESDIR) set(INSTALL_EXAMPLESDIR "examples") endif() -set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/interfaceframework/qface-if-remote-server") +set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/interfaceframework/qface-remote-server") find_package(Qt6 COMPONENTS Core) find_package(Qt6 COMPONENTS Qml) find_package(Qt6 COMPONENTS Quick) find_package(Qt6 COMPONENTS InterfaceFramework) -qt_add_executable(qface-if-remote-server +qt_add_executable(qface-remote-server main.cpp processingservice.cpp processingservice.h ) #! [0] # Interface Framework Generator: -qt6_ifcodegen_extend_target(qface-if-remote-server - IDL_FILES ../example-if-remote.qface +qt6_ifcodegen_extend_target(qface-remote-server + IDL_FILES ../example-remote.qface TEMPLATE server_qtro ) #! [0] -set_target_properties(qface-if-remote-server PROPERTIES RUNTIME_OUTPUT_DIRECTORY ..) +set_target_properties(qface-remote-server PROPERTIES RUNTIME_OUTPUT_DIRECTORY ..) -set_target_properties(qface-if-remote-server PROPERTIES +set_target_properties(qface-remote-server PROPERTIES WIN32_EXECUTABLE TRUE MACOSX_BUNDLE FALSE ) -target_compile_definitions(qface-if-remote-server PUBLIC +target_compile_definitions(qface-remote-server PUBLIC QT_DEPRECATED_WARNINGS ) -target_link_libraries(qface-if-remote-server PUBLIC +target_link_libraries(qface-remote-server PUBLIC QtIfRemoteExample Qt::Core ) -install(TARGETS qface-if-remote-server +install(TARGETS qface-remote-server RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" diff --git a/examples/interfaceframework/qface-if-remote/server_qtro/main.cpp b/examples/interfaceframework/qface-remote/server_qtro/main.cpp index 765ba103..765ba103 100644 --- a/examples/interfaceframework/qface-if-remote/server_qtro/main.cpp +++ b/examples/interfaceframework/qface-remote/server_qtro/main.cpp diff --git a/examples/interfaceframework/qface-if-remote/server_qtro/processingservice.cpp b/examples/interfaceframework/qface-remote/server_qtro/processingservice.cpp index f3cd443f..f3cd443f 100644 --- a/examples/interfaceframework/qface-if-remote/server_qtro/processingservice.cpp +++ b/examples/interfaceframework/qface-remote/server_qtro/processingservice.cpp diff --git a/examples/interfaceframework/qface-if-remote/server_qtro/processingservice.h b/examples/interfaceframework/qface-remote/server_qtro/processingservice.h index 9f68b2eb..9f68b2eb 100644 --- a/examples/interfaceframework/qface-if-remote/server_qtro/processingservice.h +++ b/examples/interfaceframework/qface-remote/server_qtro/processingservice.h diff --git a/examples/interfaceframework/qface-if-remote/server_qtro/server_qtro.pro b/examples/interfaceframework/qface-remote/server_qtro/server_qtro.pro index c89a77ce..a4618e8d 100644 --- a/examples/interfaceframework/qface-if-remote/server_qtro/server_qtro.pro +++ b/examples/interfaceframework/qface-remote/server_qtro/server_qtro.pro @@ -1,4 +1,4 @@ -TARGET = qface-if-remote-server +TARGET = qface-remote-server #! [0] TEMPLATE = app QT -= gui @@ -16,7 +16,7 @@ SOURCES += main.cpp \ QMAKE_RPATHDIR += $ORIGIN #! [1] IFCODEGEN_TEMPLATE = server_qtro -IFCODEGEN_SOURCES = ../example-if-remote.qface +IFCODEGEN_SOURCES = ../example-remote.qface #! [1] # Additional import path used to resolve QML modules in Qt Creator's code model QML_IMPORT_PATH = @@ -36,7 +36,7 @@ DEFINES += QT_DEPRECATED_WARNINGS #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 CONFIG += install_ok # Do not cargo-cult this! -target.path = $$[QT_INSTALL_EXAMPLES]/interfaceframework/qface-if-remote-server +target.path = $$[QT_INSTALL_EXAMPLES]/interfaceframework/qface-remote-server INSTALLS += target HEADERS += \ diff --git a/examples/interfaceframework/qface-tutorial/doc/images/qface-tutorial-dbus.gif b/examples/interfaceframework/qface-tutorial/doc/images/qface-tutorial-dbus.gif Binary files differnew file mode 100644 index 00000000..6b3da504 --- /dev/null +++ b/examples/interfaceframework/qface-tutorial/doc/images/qface-tutorial-dbus.gif diff --git a/examples/interfaceframework/qface-tutorial/doc/images/qface-tutorial-final.gif b/examples/interfaceframework/qface-tutorial/doc/images/qface-tutorial-final.gif Binary files differnew file mode 100644 index 00000000..e8a833bd --- /dev/null +++ b/examples/interfaceframework/qface-tutorial/doc/images/qface-tutorial-final.gif diff --git a/examples/interfaceframework/qface-tutorial/doc/images/qface-tutorial-sync.gif b/examples/interfaceframework/qface-tutorial/doc/images/qface-tutorial-sync.gif Binary files differnew file mode 100644 index 00000000..b1dece69 --- /dev/null +++ b/examples/interfaceframework/qface-tutorial/doc/images/qface-tutorial-sync.gif diff --git a/examples/interfaceframework/qface-tutorial/doc/images/qface-tutorial-unsync.gif b/examples/interfaceframework/qface-tutorial/doc/images/qface-tutorial-unsync.gif Binary files differnew file mode 100644 index 00000000..7157d9e6 --- /dev/null +++ b/examples/interfaceframework/qface-tutorial/doc/images/qface-tutorial-unsync.gif diff --git a/examples/interfaceframework/qface-tutorial/doc/images/qface-tutorial.png b/examples/interfaceframework/qface-tutorial/doc/images/qface-tutorial.png Binary files differnew file mode 100644 index 00000000..318cceae --- /dev/null +++ b/examples/interfaceframework/qface-tutorial/doc/images/qface-tutorial.png diff --git a/examples/interfaceframework/qface-tutorial/doc/src/qface-tutorial.qdoc b/examples/interfaceframework/qface-tutorial/doc/src/qface-tutorial.qdoc new file mode 100644 index 00000000..f5e8fe21 --- /dev/null +++ b/examples/interfaceframework/qface-tutorial/doc/src/qface-tutorial.qdoc @@ -0,0 +1,893 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** 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-tutorial + \brief Demonstrates step-by-step how to generate a Middleware API based on a QML application. + \ingroup qtinterfaceframework-examples + \title Qt Interface Framework Generator Tutorial + \image qface-tutorial.png + + This tutorial demonstrates how you can extend a QML application with your own autogenerated + Middleware API. We use an existing QML Instrument Cluster application and proceed through the + following steps: + + \list 1 + \li \l{chapter1}{Integrate a basic interface without a backend} + \li \l{chapter2}{Extend the interface and add annotations} + \li \l{chapter3}{Add a simulation backend and corresponding simulation annotations; with a QML plugin} + \li \l{chapter4}{Add a custom simulation behavior} + \li \l{chapter5}{Add a simulation server and use it from a Qt Remote Objects Backend} + \li \l{chapter6}{Develop a production backend that connects to a DBus interface} + \endlist + + Before we start the actual Middleware integration, let's take a look at the existing Instrument + Cluster QML code and all the features it supports: + \list + \li \c images -- This folder contains all images used in the QML code. + \li \c Cluster.qml -- The main QML file that assembles all other QML components together. + \li \c Dial.qml -- The base component to show values like speed or Revolutions per Minute + (RPM), using a needle. + \li \c Fuel.qml -- The component to show the actual fuel level. + \li \c Label.qml -- A small helper component which sets all common settings used to display + text. + \li \c LeftDial.qml -- Shows the current speed using the Dial component and as text, as + well as the current metric in miles per hour (mph) or kilometers per hour (km/h). + \li \c RightDial.qml -- Shows the current RPM and offers a way to show warning indicators. + \li \c Top.qml -- The top bar that shows the current date and the current temperature. + \endlist + + Next, we use our Middleware API to add support for the following features: + \list + \li Show the current speed in the left dial. + \li Show the current RPM in the right dial. + \li Change between different metrics. + \li Show the current temperature in the top bar. + \li Show different warnings on the right dial. + \li Indicate whether the instrument cluster is connected and show real data. + \endlist + + The ultimate goal is to connect all of these features together to simulate a real-time driving + experience like this: + + \image qface-tutorial-final.gif + + \target chapter1 + \section1 Chapter 1: Basic Middlware API with the Interface Framework Generator + + In this chapter we integrate a Middleware API into the existing Instrument Cluster QML code. + Instead of manually writing all of these parts ourselves, which is done in most basic + \l{https://doc.qt.io/qt-5/qtquick-codesamples.html}{QML examples}, we'll use the Interface Framework Generator + to autogenerate the required parts. + + \target define-speed-property + \section2 Interface Definition Language + + To be able to autogenerate the Middleware API, the Interface Framework Generator needs some input on what to + generate. This input is given in form of an Interface Definition Language (IDL), QFace, which + describes the API in a very simple way. + + Let's start to define a very simple interface which provides us with a speed property: + + \quotefromfile interfaceframework/qface-tutorial/chapter1-basics/instrument-cluster.qface + \printuntil } + + 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. + + \quotefromfile interfaceframework/qface-tutorial/chapter1-basics/instrument-cluster.qface + \printuntil module + + The most important part of the module is its interface definition. + + \quotefromfile interfaceframework/qface-tutorial/chapter1-basics/instrument-cluster.qface + \skipto interface + \printuntil } + + In this case, we define an interface named \c InstrumentCluster that consists of one property. + 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}. + + \section2 Autogeneration + + Now that our first version of the IDL file is ready, it's time to autogenerate API from it, + using the \l{Qt Interface Framework Generator}{Interface Framework Generator tool}. Similar to + \l{https://doc.qt.io/qt-5/moc.html}{moc}, this autogeneration process is integrated into the + Build System and is done on compile time. + + In the following snippets we build a C++ library based on our IDL file: + + \e CMake: + + \quotefromfile interfaceframework/qface-tutorial/chapter1-basics/frontend/CMakeLists.txt + \skipto find_package + \printto install + + First \e find_package needs to be used to get all needed libraries into the CMake build system. + A new library is defined with \l {qt6_add_library} and, using CMake target_properties, the + output name, as well as the output directory are set. As we need to link to this library in the + future, it is easier to put the file into the upper directory. + + By calling the \l {qt6_ifcodegen_extend_target} function, the autogenerator is called and the + previously defined library is extended with the generated files. The input file is specified + using the \e IDL_FILES argument. See \l{Build System Integration} for more information. + + \e qmake: + + \quotefromfile interfaceframework/qface-tutorial/chapter1-basics/frontend/frontend.pro + \printto CONFIG += install_ok + + Most of the \c{.pro} file is a standard setup to define a C++ library, using "lib" \c TEMPLATE + and defining the required file name in the \c TARGET variable. The \c qtLibraryTarget function + that we use helps to append the "d" postfix on the filename correctly, for a library that + provides debugging information. In the future, we need to link this file, so we set the + \c DESTDIR to the upper directory to simplify this. + + \note Windows searches for libraries in the same directory automatically. + + Activating the Interface Framework Generator integration requires the \c CONFIG variable to specify the + \c ifcodegen option. This makes sure the Interface Framework Generator is called during the build process, + using the QFace file that we specify in \c IFCODEGEN_SOURCES. For more information, see + \l{Build System Integration}. + + \section2 Which Files are Autogenerated + + The Interface Framework Generator works based on generation templates. These templates define what content + should be generated from a QFace file. Using qmake the template needs to be defined by the + \c IFCODEGEN_TEMPLATE variable. If it is not defined, it defaults to the "frontend" template. + In CMake the template neeeds to be specified using the \c TEMPLATE argument of + \l {qt6_ifcodegen_extend_target} and friends. + For more details on these templates, see \l{Use the Generator}. + + In short, the "frontend" template generates: + \list + \li a C++ class derived from QIfAbstractFeature for every interface in the QFace file + \li one module class that helps to register all interfaces to QML and stores global types + and functions. + \endlist + + To inspect the C++ code yourself, you can view these files in the your library's build folder. + + Right now, the most important autogenerated file for us, is the resulting C++ class for our + defined interface. It looks like this: + + \quotefile interfaceframework/qface-tutorial/chapter1-basics/frontend/instrumentcluster.h + + As you can see, the autogenerated C++ class implements a \c speed property, that we previously + defined in the QFace file. By using the \c Q_OBJECT and \c Q_PROPERTY macros, the class is now + ready for use directly in your QML code. + + \section2 Integrate the Frontend Library with the QML Code + + For this integration, we use the autogenerated frontend library from the QML code. For the sake + of simplicity, we follow the standard Qt example pattern and use a small C++ main function + which registers our autogenerated types to QML and loads the Instrument Cluster QML code into + the QQmlApplicationEngine: + + \quotefromfile interfaceframework/qface-tutorial/chapter1-basics/instrument-cluster/main.cpp + \skipto #include "instrumentclustermodule.h" + \printuntil } + + All we need now is the actual integration of the InstrumentCluster QML element and connecting + the \c speed property to the \c leftDial. This is done by instantiating the element first with + the \c instrumentCluster ID. + + \quotefromfile interfaceframework/qface-tutorial/chapter1-basics/instrument-cluster/Cluster.qml + \skipto import + \printuntil InstrumentCluster + \printuntil } + \codeline + + Lastly, we can create a Binding for the \c LeftDial Item's \c value property to our + InstrumentCluster API's \c speed property. + + \printuntil } + + \target chapter2 + \section1 Chapter 2: Extend the Interface and add Annotations + + In this chapter we extend our Middleware API with more properties via enums and by defining our + own structure. + + \section2 Define Speed as a Read-only Property + + \l{define-speed-property}{Previously}, we defined the speed property in our QFace file in the + following way: + + \quotefromfile interfaceframework/qface-tutorial/chapter1-basics/instrument-cluster.qface + \printuntil } + + This property is defined as readable and writable, as we didn't use any extra specifiers. + However, it's not necessary for our Instrument Cluster example to have a writable \c speed + property because it's not used to accelerate the car, but just to visualize the current state. + + To define the property as read-only, use the \c readonly keyword. + + \quotefromfile interfaceframework/qface-tutorial/chapter2-enums-structs/instrument-cluster.qface + \printuntil readonly + \skipto } + \printuntil } + + When we build our app again, the build system recognizes this change and runs the Interface Framework + Generator to generate an updated version of the C++ code. After the Interface Framework Generator is done, + open the \c instrumentcluster.h from the build folder and notice that the generated + \c speed property changed -- it no longer has a setter anymore and is now read-only. + + \quotefromfile interfaceframework/qface-tutorial/chapter2-enums-structs/frontend/instrumentcluster.h + \skipto class Q_EXAMPLE + \printuntil Q_PROPERTY + \dots + \skipto }; + \printuntil }; + + \section2 Extend the Interface + + To reach our goal to provide a full simulation for the Instrument Cluster, we need to add more + properties to our QFace file: \c rpm, \c fuel and \c temperature: + + \quotefromfile interfaceframework/qface-tutorial/chapter2-enums-structs/instrument-cluster.qface + \printuntil readonly real temperature + \skipto } + \printuntil } + + You might have noticed that we use a different type for the \c fuel and \c temperature + properties. We use \c real here, as we would like to show the temperature as a floating point + number, and the current fuel level as a value between 0 and 1. + + \section2 Define a New Enum Type + + One useful feature is to be able to switch between the metric and the imperial system, so we + need to define a property for the system we currently use. Using a boolean property would work, + but doesn't offer a nice API, so we define a new enum type in the QFace file and use it as the + type for our new \c system property: + + \quotefromfile interfaceframework/qface-tutorial/chapter2-enums-structs/instrument-cluster.qface + \printuntil readonly SystemType + \skipto } + \printuntil enum + \printuntil } + + In the autogenerated code, this results in an enum which is part of the module class, making it + possible for the same enum to be used by multiple classes which are part of the same module: + + \quotefile interfaceframework/qface-tutorial/chapter2-enums-structs/frontend/instrumentclustermodule.h + + \section2 Add a New Structure + + To display warnings on the Instrument Cluster's right dial, we'd like to use a structure that + stores color, icon, and text for the warning; instead of using 3 independent properties. + Similar to defining an interface, we can use the \c struct keyword in our QFace file: + + \quotefromfile interfaceframework/qface-tutorial/chapter2-enums-structs/instrument-cluster.qface + \skipto struct + \printuntil } + + Using this new structure as a type for a property, works in the same way as when using an enum. + The QFace file should now look like this: + + \quotefile interfaceframework/qface-tutorial/chapter2-enums-structs/instrument-cluster.qface + + \section2 Integrate the New Properties + + Like in the previous chapter, actually integrating the newly introduced properties involves + creating Bindings. The \c rpm property can be directly connected to the \c rightDial Item's + \c value property; the same is done for the top Item's \c temperature property. To control + which unit is displayed in the left Dial, the \c leftDial Item provides \c metricSystem, a bool + property. As we used an enum in our QFace file, we need to convert the value first by testing + the \c sytemType property for the "Metric" value. + + \quotefromfile interfaceframework/qface-tutorial/chapter2-enums-structs/instrument-cluster/Cluster.qml + \skipto LeftDial + \printuntil } + \codeline + + These enums are part of the module class, which is also exported to QML as + \c InstrumentClusterModule. To trigger a warning in the \c rightDial Item, we use 3 bindings to + connect to the 3 member variables in the structure: + + \printuntil } + + \target chapter3 + \section1 Chapter 3: Add a Simulation Backend and Annotations with a QML plugin + + In the previous two chapters, we wrote a Middleware API using a QFace file and used the Interface Framework + Generator to autogenerate a C++ API in the form of a library. Now, in this chapter, we extend + this further by introducing a simulation backend and using annotations to define default values + for our simulation. + + \section2 Separation between the Frontend and Backend + + Both QtInterfaceFramework and the Interface Framework Generator enable you to write code that separates the frontend from the + backend -- to split an API from its actual implementation. Already, Qt uses this concept in a + lot of areas, most prominently in the underlying window system technology on various Qt + platforms like XCB on Linux and Cocoa on macOS. + + The same separation is done for our Middleware API, where the frontend provides the API as + a library; the backend provides an implementation of this API. This implementation is based on + QtInterfaceFramework's \l{Dynamic Backend System} which enables us to switch between such backends at runtime. + + \image feature-backend.png + + \section2 Add a Simulation Backend + + For our Instrument Cluster, we'd like to add such a backend to provide actual values. For now, + we'd like to just have some simulation behavior as we can't connect it easily to a real car. + This is why such backends are called "simulation backend". To add this type of backend, once + again, we use the Interface Framework Generator to do the heavy lifting for us and generate one. This work + is done in a similar way to when we generated a library with the "frontend" template. But now, + we are using the "backend_simulator" template: + + \e CMake: + + \quotefromfile interfaceframework/qface-tutorial/chapter3-simulation-backend/backend_simulator/CMakeLists.txt + \skipto find_package + \printto target_link_libraries + + Similar to the frontend library, first the used components are imported using \e find_package. + As we want to build a plugin (dynamic library) which is loaded at runtime instead of linking + against it, we use the \l {qt_add_plugin} function instead. One important aspect + here is that the library name ends with "_simulation", which is a way to tell QtInterfaceFramework that this + is a simulation backend. When a "production" backend is available, it is preferred over the + "simulation" one. For more information, see \l{Dynamic Backend System}. + + As before, the Interface Framework Generator is called by using the \l{qt6_ifcodegen_extend_target} + function, this time setting "backend_simulator" as the \c TEMPLATE. + + \e qmake: + + \quotefromfile interfaceframework/qface-tutorial/chapter3-simulation-backend/backend_simulator/backend_simulator.pro + \printto DESTDIR + \skipto QT + \printuntil CONFIG + \skipto IFCODEGEN_TEMPLATE + \printto CONFIG += install_ok + + Just like for the frontend library, the project file builds a \c lib and defines the library + name using \c qtLibraryTarget to also support the Windows debug postfix. One important aspect + here is that the library name ends with "_simulation", which is a way to tell QtInterfaceFramework that this + is a simulation backend. When a "production" backend is available, it is preferred over the + "simulation" one. For more information, see \l{Dynamic Backend System}. + + Enabling the Interface Framework Generator is also done in the same way as we did earlier: by using the same + \c IFCODEGEN_SOURCE variable, but defining \c IFCODEGEN_TEMPLATE to "backend_simulator", to use the + correct generation template. In addition, we need to add 'plugin' to the \c CONFIG variable, + to make this library a Qt plugin which can be easily loaded at runtime. + + \section2 Link Settings and Locating Plugins + + Trying to build the project file just as it is, right now, would result in compilation and + linking errors. This is because: to do the frontend and backend separation, we need to have the + backend implement a defined interface class, that is known to the frontend. This interface is + aptly called "backend interface" and is automatically generated as part of the frontend + library. Because this class provides signals and slots and uses QObject for its base class, you + need to link to the frontend library when you inherit from it. As this is needed for the + backend plugin, we need to add the following lines in addition: + + \e CMake: + + \quotefromfile interfaceframework/qface-tutorial/chapter3-simulation-backend/backend_simulator/CMakeLists.txt + \skipto target_link_libraries + \printto install + + By defining the frontent library named \e libIc_chapter3 as a target link library the include + path gets updated accordingly. + + \e qmake: + + \quotefromfile interfaceframework/qface-tutorial/chapter3-simulation-backend/backend_simulator/backend_simulator.pro + \skipuntil CONFIG + \printuntil INCLUDEPATH + + Now the project should build fine and create the plugin in your build folder; or the plugin + folder if you don't use a shadow build. When you start the Instrument Cluster again, you should + see the following message: + + \badcode + There is no production backend implementing "Example.If.InstrumentCluster.InstrumentCluster" . + There is no simulation backend implementing "Example.If.InstrumentCluster.InstrumentCluster" . + No suitable ServiceObject found. + \endcode + + This message indicates that QtInterfaceFramework is still unable to find the simulation plugin we just created. + Here, you need to know a little bit more about Qt's Plugin System, especially how it it finds + plugins. + + Qt searches for it's plugins in multiple directories, the first one is the plugin folder, + \c plugins, which comes with your Qt installation. Within the plugins folder, every plugin type + has it's own sub-folder, such as \c platforms, for the platform plugins used to talk to the + underlying platform API and the windowing system. + + Similarly, QtInterfaceFramework searches for its backend plugins in the \c interfaceframework folder. + + To make sure our simulation backend ends up in such a folder, we add the following changes in + our build system file: + + \e CMake: + + \quotefromfile interfaceframework/qface-tutorial/chapter3-simulation-backend/backend_simulator/CMakeLists.txt + \skipuntil qt_add_plugin + \printuntil set_target_properties + + \e qmake: + + \quotefromfile interfaceframework/qface-tutorial/chapter3-simulation-backend/backend_simulator/backend_simulator.pro + \skipto DESTDIR + \printuntil DESTDIR + + You might wonder how creating a \c interfaceframework folder in the upper directory solves this problem of + finding the plugin as it's not part of the system plugins folder. But Qt supports searching in + multiple folders for such plugins and one of those folders is the path to where the executable + itself is located. + + Alternatively, we could add an additional plugin path using the QCoreApplication::addLibraryPath() + function or using the \c QT_PLUGIN_PATH environment variable. For more information, see + \l{https://doc.qt.io/qt-5/plugins-howto.html}{How to create Qt Plugins}. + + Now everything is in place, but because our plugin links against the frontend library, we need + to make sure the library can be found by the dynamic linker. This can be achieved by + setting the \c LD_LIBRARY_PATH environment variable to our library folder. But this results + in the problem, that every user would need to set this variable to be able to use our + application. + + \e CMake: + + Using CMake, the location of our frontend library is automatically added as a \e RUNPATH to the + the binary and no further steps are needed. + + \e qmake: + + In qmake we can ease the setup by using a relative \e RPATH instead of the \c LD_LIBRARY_PATH + and annotate our plugin with the information for the linker, where it might find the needed + libraries, relative to the plugin's location: + + \quotefromfile interfaceframework/qface-tutorial/chapter3-simulation-backend/backend_simulator/backend_simulator.pro + \skipto INCLUDEPATH + \printuntil QMAKE_RPATHDIR + + \section2 Export the QML Types in a QML Plugin + + In the first chapter, we extended our \c main.cpp to register all types of our autogenerated + Middleware APIs. Although this works fine, in bigger projects it's common to use a QML Plugin + instead and be able to use qmlscene for development. Although the code for doing this is + not complex, the Interface Framework Generator supports this as well and makes it even easier. + + From the first chapter, we know that the module name is used for the QML import URI. This is + important for a QML plugin as the QmlEngine expects the plugin in a specific folder to + follow the module name, where every section of the module name is a sub-folder. Our build system + file to generate a QML plugin looks like this: + + \e CMake: + + \quotefromfile interfaceframework/qface-tutorial/chapter3-simulation-backend/imports/CMakeLists.txt + \skipto qt6_ifcodegen_import_variables + \printto install + + Unlike all our previous generator calls we don't extend a previously defined target, but + import the generated code into CMake and pass it to the \l {qt_add_qml_module} function. + The \l {qt6_ifcoegen_import_variables} function will call the generator and export variables + starting with \e CLUSTER as prefix to the current CMake scope. + Those variables reference autogenerated code, but also expose other information like the QML + import URI. + In the next call, the variables are used to define a QML Module with the correct URI and version + (as specified in our IDL file). By using the \e OUTPUT_DIRECTORY variable we can make sure that + the correct folder structure is generated and we can import the QML plugin directly from within + the build folder. + + \e qmake: + + \quotefromfile interfaceframework/qface-tutorial/chapter3-simulation-backend/imports/imports.pro + \printto target.path + + All lines until \c IFCODEGEN_SOURCES should be familiar. We use \c CONFIG to build a plugin, then + define the settings for the linker to link against our frontend library. Then, we use + \c IFCODEGEN_TEMPLATE to define "qmlplugin" as the generation template. Instead of adding + \c ifcodegen to \c CONFIG, this time we use + \l{https://doc.qt.io/qt-5/qmake-test-function-reference.html#load-feature} + {qmake's load() function} to explicitly load the feature. This enables us to use the \c URI + variable which is part of the "qmlplugin" generation template. This URI can be used to define + a \c DESTDIR by replacing all dots with slashes. + + In addition to the folder structure, the QmlEngine also needs a \c qmldir file which indicates + what files are part of the plugin, and under which URI. For more information, see + \l{https://doc.qt.io/qt-5/qtqml-modules-qmldir.html}{Module Definition qmldir Files}. Both this + \c qmldir file and a \c plugins.qmltypes file which provides information about code-completion, + are autogenerated by the Interface Framework Generator; but they need to be placed next to the library. To do + so, we add the files to a scope similar to an \c INSTALL target, but add it to the \c COPIES + variable instead. This makes sure that the files are copied when the plugin is built. + + Now the plugin is ready for use, but our Instrument Cluster application doesn't know where to + search for it and is still using the old hardcoded registration. So, we can now remove the + linking step in the \c instrument-cluster build system file and change our main file + accordingly: + + \quotefromfile interfaceframework/qface-tutorial/chapter3-simulation-backend/instrument-cluster/main.cpp + \skipto #include + \printuntil } + + What has changed is that we've now added an additional import path with the \c addImportPath + function, which points to the "imports" folder next to the binary's location. + + \target chapter4 + \section1 Chapter 4: Add a Custom Simulation + + So far, we've created a Middleware API and integrated it into our Instrument Cluster QML code, + extended it with a QML plugin, and generated a simulation backend. In the background, quite a + lot has happened to support us; but on the UI side not much has changed till now. This chapter + is about bringing our simulation backend to life by defining sane default values and starting + to simulate a real car ride. + + \section2 Define Default Values + + We start by defining default values for our properties, using annotations in our QFace file. + An annotation is a special kind of comment which adds extra data to an interface, method, + property, and so on. For this use case we use the \c config_simulator annotation. For more + information, see \l{annotations-yaml}{Annotations}. + + Currently, in our Instrument Cluster, the temperatur defaults to 0. Let's change this to a + temperature in spring, 15 degrees Celsius, with the following YAML fragment: + + \quotefromfile interfaceframework/qface-tutorial/chapter4-simulation-behavior/instrument-cluster.qface + \printuntil } + + Compile the plugin again for this temperature change to be reflected in our Instrument Cluster. + Let's see how this actually works: when starting the Interface Framework Generator, the config_simulator + annotation was transformed into a JSON file that's now part of the "simulation backend" build + folder. This JSON file looks like this: + + \quotefile interfaceframework/qface-tutorial/chapter4-simulation-behavior/backend_simulator/instrumentclustermodule.json + + But how is this JSON file related to the actual simulation backend code? The autogenerated + simulation backend code uses QIfSimulationEngine, that reads the JSON file and provides its + data to a QML simulation file. A default QML file is also autogenerated and loaded from the + QIfSimulationEngine. This default QML file provides the behavior of what should happen in the + the simulation backend. + + Later, in the next section, we take a look at the QML file and how we can change it. But first, + let's see how we can change the default values in a more dynamic way. + + The QIfSimulationEngine allows us to override which JSON file should be loaded into the + engine, when we set the \c QTIF_SIMULATION_DATA_OVERRIDE environment variable. Since there can + be multiple engines run by different backends, we need to define which engine we're referring + to. In the autogenerated code, the module name is always used as the engine specifier. For this + chapter, we already prepared a second JSON file which is part of our source directory. Setting + the environment variable as follows, changes the \c systemType to mph instead of km/h: + + \badcode + QTIF_SIMULATION_DATA_OVERRIDE=instrumentcluster=<path-to-file>/miles.json + \endcode + + \section2 Define a QML Behavior + + Before we define our custom behavior, let's see what's been autogenerated for us. There are two + QML files: The first is \c instrumentcluster_simulation.qml and rather simple. It defines an + entry point that istantiates the second file, an \c InstrumentClusterSimulation.qml file. This + split is done as there can be multiple interfaces defined as part of the same module. + + \note A QML Engine can only have one entry point. While QIfSimulationEngine has this same + limitation, if you have a module with multiple interfaces, you want to have multiple simulation + files -- one per interface. This is why the first QML file merely instantiates the QML files for + all interfaces that it supports. In the case of our example, it's only one interface. + + The InstrumentClusterSimulation.qml file is very interesting: + + \quotefile interfaceframework/qface-tutorial/chapter4-simulation-behavior/backend_simulator/InstrumentClusterSimulation.qml + + First, there's a \c settings property, that's initialized with the return value from the + \l{IfSimulator::findData}{IfSimulator.findData} method, which takes the + \l{IfSimulator::simulationData}{IfSimulator.simulationData} and a string as input. The + \c simulationData is the JSON file represented as a JavaScript object. + + The \c findData method helps us to extract only the data that is of interest for this + interface, \c InstrumentCluster. The properties that follow help the interface to know whether + the default values are set. The \c LoggingCategory is used to identify the log output from this + simulation file. + + Afterwards, the actual behavior is defined by instantiating an \c InstrumentClusterBackend Item + and extending it with more functions. The \c InstrumentClusterBackend is the interface towards + our \c InstrumentCluster QML frontend class. But, apart from the frontend, these properties are + also writable to make it possible to change them to provide a useful simulation. + + Each time a frontend instance connects to a backend, the \c initialize() function is called. + The same applies to the QML simulation: as the \c initialize() C++ function forwards this to + the QML instance. This also applies to all other functions, like setter and getters, for + properties or methods. For more details, see \l{QIfSimulationEngine}. + + Inside the QML \c initialize() function, we call \c{IfSimulator.initializeDefault()}, to read + the default values from the \c simulationData object and initialize all properties. This is + done only \b once, as we don't want the properties be reset to default when the next frontend + instance connects to the backend. Lastly, the base implementation is called to make sure that + the \c initializationDone signal is sent to the frontend. + + Similarly, a setter function is defined for each property; they use the + \c{IfSimulator.checkSettings()} to read specific constraint settings for the property from + the \c simulationData and check whether these constraints are valid for the new value. If + these constraints aren't valid, then \c{IfSimulator.constraint()} is used to provide a + meaningful error message to the user. + + \section2 Define Our Own QML Simulation + + As mentioned above, the \c InstrumentClusterBackend Item does provide all the properties of our + QFace file. This can be used to simulate a behavior by changing the properties to the values + we want. The simplest form for this would be value assignment, but this would be rather static + not exactly what we'd like to achieve. Instead, we use QML Animation objects to change the + values over time: + + \quotefromfile interfaceframework/qface-tutorial/chapter4-simulation-behavior/backend_simulator/simulation.qml + \skipto NumberAnimation + \printuntil } + + The code snippet above changes the speed property to 80 over 4000 seconds and simulates an + accelerating car. Extending this to the other properties, and combining both sequential and + parallel animations, we can create a full simulation: + + \quotefromfile interfaceframework/qface-tutorial/chapter4-simulation-behavior/backend_simulator/simulation.qml + \skipto property var animation + \printuntil property: "fuel" + \printuntil property: "fuel" + \printuntil } + \printuntil } + + Then, to provide a nice simulation for the \c rpm property, we use a binding which does some + calculations based on the current speed. The complete simulation file looks like this: + + \quotefromfile interfaceframework/qface-tutorial/chapter4-simulation-behavior/backend_simulator/simulation.qml + \skipto import + + The next step is to tell the Interface Framework Generator and the QIfSimulationEngine about our new + simulation file. Similar to QML files, the best aproach here is to put the simulation file into + a resource file. In our example, we add a new file called \c simulation.qrc which contains our + \c simulation.qml using the \c{/} prefix. + + In our QFace file, this location now needs to be added in the form of an annotation: + + \quotefromfile interfaceframework/qface-tutorial/chapter4-simulation-behavior/instrument-cluster.qface + \printuntil module + \dots + + Now, rebuilding the simulation backend embeds the simulation file into the plugin and hands the + file over to the QIfSimulationEngine, which starts the simulation when loaded. + + \target chapter5 + \section1 Chapter 5: Add a Simulation Server Combined with QtRemoteObjects + + In this chapter we extend our Instrument Cluster to use an Inter-Process Communication (IPC) + mechanism and use two processes. At the moment, the simulation is loaded as a plugin that + causes it to be part of the same service. Although this is good enough for a small example + application, it's not how it's done in modern multi-process architectures, where multiple + processes need to be able to access the same value and react to changes. We could write a + second Application that uses the same Middleware API. However, we can achieve the same thing + just by starting the Instrument Cluster twice and checking whether the animations are in sync. + Currently, they're not. + + \image qface-tutorial-unsync.gif + + \section2 Add a QtRemoteObjects Integration + + The IPC for this example is QtRemoteObjects, because the Interface Framework Generator already supports it + out of the box. To use QtRemoteObjects we generate a second plugin, a "production" backend, + this time. Production backends are automatically preferred over the simulation backend we + introduced before. + + This is done with the following build system files: + + \e CMake: + + \quotefromfile interfaceframework/qface-tutorial/chapter5-ipc/backend_qtro/CMakeLists.txt + \skipto qt_add_plugin + \printto install + + \e qmake: + + \quotefromfile interfaceframework/qface-tutorial/chapter5-ipc/backend_qtro/backend_qtro.pro + \printto CONFIG += install_ok + + These files are almost identical to the ones we used earlier for our simulation backend. + For now we highlight what's changed. + + The name of the plugin doesn't end with "_simulation" to indicate that this is a "production" + backend. The template is now changed to "backend_qtro" to generate a backend that uses + QtRemoteObjects Replicas to connect to a QtRemoteObjects Source that provides the values. In + addition to a QtRemoteObject-based backend, we also need a QtRemoteObject-based server. This + part can also be autogenerated using the Interface Framework Generator in a similar fashion: + + \e CMake: + + \quotefromfile interfaceframework/qface-tutorial/chapter5-ipc/simulation_server/CMakeLists.txt + \skipto qt_add_executable + \printto # Resources: + + \e qmake: + + \quotefromfile interfaceframework/qface-tutorial/chapter5-ipc/simulation_server/simulation_server.pro + \printto RESOURCES + + Because we'd like to generate a server binary, the qmake \c TEMPLATE needs to be set to "app" + instead of "lib", in CMake we use \l {qt_add_exectuable} instead. Similar to the plugin, the + server also needs to link against our library to give it access to the defined enums, + structures, and other types. The template we use to generate a simulation server is called + "server_qtro_simulator". + + \section2 Reuse the Existing Simulation Behavior + + Now, if you start the server and then the Instrument Cluster, you don't see the simulation + from our previous chapter anymore. The reason for this, is that the simulation code is part of + our simulation backend, but this backend is no longer used as we added the + QtRemoteObjects-based "production" backend. + + Because we used the "server_qtro_simulator" generation template, this can easily be fixed, as + the generated server code is also using the QIfSimulationEngine and supports to use the same + simulation file than our simulation backend. We just need to extend the project file in the + same way as we did before and are also able to use the same resource file for this. + + \e CMake: + + \quotefromfile interfaceframework/qface-tutorial/chapter5-ipc/simulation_server/CMakeLists.txt + \skipto # Resources: + \printto install + + \e qmake: + + \quotefromfile interfaceframework/qface-tutorial/chapter5-ipc/simulation_server/simulation_server.pro + \skipto RESOURCES + \printuntil RESOURCES + + In the same way, we can also use the other simulation data JSON file that we defined in the + previous chapter, by using the same environment variable. We just need to pass it to the + server instead of our Instrument Cluster application. + + Let's do the final test: starting two Instrument Cluster instances should now show the + animations in sync: + + \image qface-tutorial-sync.gif + + \target chapter6 + \section1 Chapter 6: Develop a Production Backend with D-Bus + + Previously, we extended our Instrument Cluster code by using QtRemoteObjects as IPC and + autogenerated a backend for it, as well as a server that provides the simulation. In this + chapter, we'd like to write our own backend \b manually using D-Bus as IPC. + + We've already prepared a working D-Bus server which provides limited simulation. + + First, let's look at the server code and see what's done there; then write the backend that + connects to it. + + \section2 D-Bus Server + + As mentioned above, we use D-Bus for this chapter and we already have an XML file that + describes the D-Bus interface, similar to our QFace file: + + \quotefile interfaceframework/qface-tutorial/chapter6-own-backend/demo_server/instrumentcluster.xml + + This XML file is used to let qmake generate a base class which is extended by the server with + actual functionality. For more information, see \l{QtDBus}. + + Our D-Bus server starts on the session bus, on the \c{/} path, and provides an interface named + "Example.If.InstrumentCluster". To simulate some values, we keep it simple and use a timer + event to change the speed value every 100 milliseconds. Then, we start from 0, once the + maximum of 250 is reached. Similarly, the \c rpm value is increased to 5000. For all other + properties, we provide hardcoded values. + + \quotefromfile interfaceframework/qface-tutorial/chapter6-own-backend/demo_server/instrumentcluster.cpp + \skipto timerEvent + \printuntil } + + \section2 Write Our own D-Bus Backend + + Let's start with a build system file for our backend. This is very similar to previous + files, but it doesn't use the Interface Framework Generator. Instead, it uses \c DBUS_INTERFACES + for qmake to autogenerate some client code which sends and receives messages over D-Bus. + In the CMake case the \l {qt6_add_dbus_interface} function is used to do the same. + + Now, we need to define an entry point for our plugin. This plugin class needs to derive from + QIfServiceInterface and implement two functions: + + \list + \li \c {QStringList interfaces()} -- that returns a list of all interfaces this plugin + supports. + \li \c {QIfFeatureInterface *interfaceInstance(const QString &interface)} -- that returns + an instance of the requested interface. + \endlist + + Additionally, we also need to provide a list of interfaces we support as plugin metadata, in + the form of a JSON file which looks like this: + + \quotefile interfaceframework/qface-tutorial/chapter6-own-backend/backend_dbus/instrumentcluster_dbus.json + + We need this list, as it gives QtInterfaceFramework the chance to know which interfaces a backend supports, + before instantiating it and loading only the plugins which the application code needs. + + Our plugin code looks like this: + + \quotefromfile interfaceframework/qface-tutorial/chapter6-own-backend/backend_dbus/instrumentclusterplugin.cpp + \skipto #include + \printto + + In \c interfaces() we use the IID which is defined in \c{instrumentclusterbackendinterface.h} + from our autogenerated library. In \c insterfaceInstance() we check for the correct string and + return an instance of the instrument cluster backend we implemented. + + This backend is defined in \c instrumentclusterbackend.h and derives from + \c InstrumentClusterBackendInterface. In our \c InstrumentClusterBackend class, we need to + implement all pure virtual functions from InstrumentClusterBackendInterface and derived classes. + + For our example, this isn't complex, as we just need to implement the initialize() function. + If our XML file would use writable properties or methods, then we'd need to implement those as + well. We don't need to implement getters for our properties, because QtInterfaceFramework uses the changed + signals during the initialization phase to get information about the current state. Although + the generated D-Bus interface class would provide getters to retrieve the properties from our + server, it's not recommended to use these when you develop a backend. These getters are + implemented by using synchronous calls, which means they will block the event loop until an + answer is received by the client. Since this can lead to performance issues, we recommend to + use \b asynchronous calls instead. + + In our backend, we define a fetch function for each property that's implemented like this: + + \quotefromfile interfaceframework/qface-tutorial/chapter6-own-backend/backend_dbus/instrumentclusterbackend.cpp + \skipto ::fetchSpeed + \printto ::fetchRpm + + First, we add the property to a list, to know which properties have been fetched successfully. + Next, we use the \c asyncCall() function to call the getter for the \c speed property and use a + \c QDBusPendingCallWatcher to wait for the result. Once the result is ready, the lambda removes + the property again from our \c fetchList, uses the \c onSpeedChanged() function to store the + value and notifies the frontend about it. Since we don't need the watcher anymore, we delete it + in the next event loop run using \c deleteLater(), and call the \c checkInitDone() function. + + The \c checkInitDone() function is defined as follows: + + \quotefromfile interfaceframework/qface-tutorial/chapter6-own-backend/backend_dbus/instrumentclusterbackend.cpp + \skipto ::checkInitDone + \printto onSpeedChanged + + It ensures that the \c initializationDone() signal is sent to the frontend once all our + properties are fetched from the server, and the initialization is complete. + + In addition to retrieving the current state from the server, we also need to inform our frontend + every time a property changes. This is done by emitting the corresponding change signal when the + server changes one of its properties. To handle this, we define a slot for each property. This + slot saves the property in our class an emits the change signal: + + \quotefromfile interfaceframework/qface-tutorial/chapter6-own-backend/backend_dbus/instrumentclusterbackend.cpp + \skipto void InstrumentClusterBackend::onSpeedChanged(int speed) + \printto onRpmChanged + + The same slot is also used during the initialization phase to save and emit the value. + + You might wonder why saving the value is needed at all, if we can just emit the signal. This is + because the backend plugin is used directly by every instance of the \c InstrumentCluster class + and every instance calls the \c initialize() function to retrieve the current state. Instead of + fetching all properties again, the second \c initialize() call just emits values that were + already saved; and the slots keep them up to date. + + Now, when we start the Instrument Cluster, our backend should connect to our D-Bus server and + look like this: + + \image qface-tutorial-dbus.gif + +*/ |