diff options
author | Dominik Holland <dominik.holland@qt.io> | 2022-09-06 17:02:14 +0200 |
---|---|---|
committer | Nicholas Bennett <nicholas.bennett@qt.io> | 2022-09-19 10:50:28 +0000 |
commit | 3bdac53057ae99b86c17010002059a60d94424eb (patch) | |
tree | 50da96673873f9c014021b6db7506d2fc8c23e46 | |
parent | 0f16a2fc9ce3bbd1f0fbd37ddd1b7945d003cdf9 (diff) |
Extend the tutorial by a chapter for own templates
Fixes: QTBUG-98998
Change-Id: Iec90f15b2d2854dbd3c7d0be3fea0c078c64f440
Reviewed-by: Robert Griebl <robert.griebl@qt.io>
(cherry picked from commit e6e69d27c763cdfb8105341be16df2c6175ae751)
Reviewed-by: Nicholas Bennett <nicholas.bennett@qt.io>
44 files changed, 2063 insertions, 1 deletions
diff --git a/examples/interfaceframework/qface-tutorial/CMakeLists.txt b/examples/interfaceframework/qface-tutorial/CMakeLists.txt index a91d5980..959ca6c8 100644 --- a/examples/interfaceframework/qface-tutorial/CMakeLists.txt +++ b/examples/interfaceframework/qface-tutorial/CMakeLists.txt @@ -9,4 +9,5 @@ add_subdirectory(chapter4-simulation-behavior) add_subdirectory(chapter5-ipc) if(TARGET Qt::DBus) add_subdirectory(chapter6-own-backend) + add_subdirectory(ch7-own-template) endif() diff --git a/examples/interfaceframework/qface-tutorial/ch6-own-backend/demo_server/instrumentcluster.cpp b/examples/interfaceframework/qface-tutorial/ch6-own-backend/demo_server/instrumentcluster.cpp new file mode 100644 index 00000000..0f0c513c --- /dev/null +++ b/examples/interfaceframework/qface-tutorial/ch6-own-backend/demo_server/instrumentcluster.cpp @@ -0,0 +1,117 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2019 Luxoft Sweden AB +// Copyright (C) 2018 Pelagicore AG +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "instrumentcluster.h" +#include <QThread> + +InstrumentCluster::InstrumentCluster(QObject *parent) + : QObject(parent) + , m_speed(0) + , m_rpm(0) + , m_fuel(0) + , m_temperature(15) + , m_systemType(InstrumentClusterModule::Metric) +{ + startTimer(100); +} + +int InstrumentCluster::speed() const +{ + return m_speed; +} + +int InstrumentCluster::rpm() const +{ + return m_rpm; +} + +qreal InstrumentCluster::fuel() const +{ + return m_fuel; +} + +qreal InstrumentCluster::temperature() const +{ + return m_temperature; +} + +InstrumentClusterModule::SystemType InstrumentCluster::systemType() const +{ + return m_systemType; +} + +Warning InstrumentCluster::currentWarning() const +{ + return m_currentWarning; +} + +void InstrumentCluster::setSpeed(int speed) +{ + if (m_speed == speed) + return; + + m_speed = speed; + emit speedChanged(m_speed); +} + +void InstrumentCluster::setRpm(int rpm) +{ + if (m_rpm == rpm) + return; + + m_rpm = rpm; + emit rpmChanged(m_rpm); +} + +void InstrumentCluster::setFuel(qreal fuel) +{ + if (qFuzzyCompare(m_fuel, fuel)) + return; + + m_fuel = fuel; + emit fuelChanged(m_fuel); +} + +void InstrumentCluster::setTemperature(qreal temperature) +{ + if (qFuzzyCompare(m_temperature, temperature)) + return; + + m_temperature = temperature; + emit temperatureChanged(m_temperature); +} + +void InstrumentCluster::setSystemType(InstrumentClusterModule::SystemType systemType) +{ + if (m_systemType == systemType) + return; + + m_systemType = systemType; + emit systemTypeChanged(m_systemType); +} + +void InstrumentCluster::setCurrentWarning(const Warning ¤tWarning) +{ + if (m_currentWarning == currentWarning) + return; + + m_currentWarning = currentWarning; + emit currentWarningChanged(m_currentWarning); +} + +void InstrumentCluster::timerEvent(QTimerEvent *event) +{ + Q_UNUSED(event); + + if (speed() >= 250) + setSpeed(0); + else + setSpeed(speed() + 1); + + if (rpm() >= 5000) + setRpm(0); + else + setRpm(rpm() + 100); +} diff --git a/examples/interfaceframework/qface-tutorial/ch7-own-template/CMakeLists.txt b/examples/interfaceframework/qface-tutorial/ch7-own-template/CMakeLists.txt new file mode 100644 index 00000000..4123a5a0 --- /dev/null +++ b/examples/interfaceframework/qface-tutorial/ch7-own-template/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.16) +project(qface-chapter7 LANGUAGES CXX) + +add_subdirectory(instrument-cluster) +add_subdirectory(frontend) +add_subdirectory(backend_simulator) +add_subdirectory(backend_dbus) +add_subdirectory(imports) +add_subdirectory(demo_server) diff --git a/examples/interfaceframework/qface-tutorial/ch7-own-template/backend_dbus/CMakeLists.txt b/examples/interfaceframework/qface-tutorial/ch7-own-template/backend_dbus/CMakeLists.txt new file mode 100644 index 00000000..f295e642 --- /dev/null +++ b/examples/interfaceframework/qface-tutorial/ch7-own-template/backend_dbus/CMakeLists.txt @@ -0,0 +1,38 @@ +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +set(CMAKE_AUTOMOC ON) + +if(NOT DEFINED INSTALL_EXAMPLESDIR) + set(INSTALL_EXAMPLESDIR "examples") +endif() + +set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/interfaceframework/qface-tutorial/chapter7-own-template") + +find_package(Qt6 REQUIRED COMPONENTS Core DBus Gui InterfaceFramework) + +qt_add_plugin(ic_chapter7_dbus) +set_target_properties(ic_chapter7_dbus PROPERTIES LIBRARY_OUTPUT_DIRECTORY ../interfaceframework) + +# Interface Framework Generator: +qt_ifcodegen_extend_target(ic_chapter7_dbus + IDL_FILES ../instrument-cluster.qface + TEMPLATE ../templates/backend_dbus +) + +target_include_directories(ic_chapter7_dbus PRIVATE + ../demo_server +) + +target_link_libraries(ic_chapter7_dbus PUBLIC + libIc_chapter7 + Qt::Core + Qt::DBus + Qt::Gui + Qt::InterfaceFramework +) + +install(TARGETS ic_chapter7_dbus + RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" + BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" + LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" +) diff --git a/examples/interfaceframework/qface-tutorial/ch7-own-template/backend_dbus/backend_dbus.pro b/examples/interfaceframework/qface-tutorial/ch7-own-template/backend_dbus/backend_dbus.pro new file mode 100644 index 00000000..8b468c21 --- /dev/null +++ b/examples/interfaceframework/qface-tutorial/ch7-own-template/backend_dbus/backend_dbus.pro @@ -0,0 +1,22 @@ +TEMPLATE = lib +TARGET = $$qtLibraryTarget(instrumentcluster_dbus) +DESTDIR = ../interfaceframework + +QT += interfaceframework dbus +CONFIG += plugin + +LIBS += -L$$OUT_PWD/../ -l$$qtLibraryTarget(QtIfInstrumentCluster) +INCLUDEPATH += $$OUT_PWD/../frontend $$PWD/../demo_server +QMAKE_RPATHDIR += $$QMAKE_REL_RPATH_BASE/../ + +PLUGIN_TYPE = interfaceframework + +IFCODEGEN_TEMPLATE = $$PWD/../templates/backend_dbus +IFCODEGEN_SOURCES = ../instrument-cluster.qface + +HEADERS += \ + ../demo_server/dbus_conversion.h \ + +CONFIG += install_ok # Do not cargo-cult this! +target.path = $$[QT_INSTALL_EXAMPLES]/interfaceframework/qface-tutorial/chapter7-own-template +INSTALLS += target diff --git a/examples/interfaceframework/qface-tutorial/ch7-own-template/backend_simulator/CMakeLists.txt b/examples/interfaceframework/qface-tutorial/ch7-own-template/backend_simulator/CMakeLists.txt new file mode 100644 index 00000000..f2f327e4 --- /dev/null +++ b/examples/interfaceframework/qface-tutorial/ch7-own-template/backend_simulator/CMakeLists.txt @@ -0,0 +1,46 @@ +set(CMAKE_AUTOMOC ON) + +if(NOT DEFINED INSTALL_EXAMPLESDIR) + set(INSTALL_EXAMPLESDIR "examples") +endif() + +set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/interfaceframework/qface-tutorial/chapter7-own-template") + +find_package(Qt6 REQUIRED COMPONENTS Core Gui InterfaceFramework) + +qt_add_plugin(ic_chapter7_simulation) +set_target_properties(ic_chapter7_simulation PROPERTIES LIBRARY_OUTPUT_DIRECTORY ../interfaceframework) + +# Interface Framework Generator: +qt_ifcodegen_extend_target(ic_chapter7_simulation + IDL_FILES ../instrument-cluster.qface + TEMPLATE backend_simulator +) + +target_link_libraries(ic_chapter7_simulation PUBLIC + libIc_chapter7 +) + +set(import_path "${CMAKE_CURRENT_BINARY_DIR}/backend_simulator/qml") +if (NOT ${import_path} IN_LIST QML_IMPORT_PATH) + list (APPEND QML_IMPORT_PATH "${import_path}") + set(QML_IMPORT_PATH ${QML_IMPORT_PATH} CACHE INTERNAL "" FORCE) +endif() + +# Resources: +set(simulation_resource_files + "simulation.qml" +) + +qt_add_resources(ic_chapter7_simulation "simulation" + PREFIX + "/" + FILES + ${simulation_resource_files} +) + +install(TARGETS ic_chapter7_simulation + RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" + BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" + LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" +) diff --git a/examples/interfaceframework/qface-tutorial/ch7-own-template/backend_simulator/backend_simulator.pro b/examples/interfaceframework/qface-tutorial/ch7-own-template/backend_simulator/backend_simulator.pro new file mode 100644 index 00000000..84a0ae83 --- /dev/null +++ b/examples/interfaceframework/qface-tutorial/ch7-own-template/backend_simulator/backend_simulator.pro @@ -0,0 +1,24 @@ +TEMPLATE = lib +TARGET = $$qtLibraryTarget(instrumentcluster_simulation) +DESTDIR = ../interfaceframework + +QT += core interfaceframework +CONFIG += ifcodegen plugin + +LIBS += -L$$OUT_PWD/../ -l$$qtLibraryTarget(QtIfInstrumentCluster) +INCLUDEPATH += $$OUT_PWD/../frontend +QMAKE_RPATHDIR += $$QMAKE_REL_RPATH_BASE/../ + +IFCODEGEN_TEMPLATE = backend_simulator +IFCODEGEN_SOURCES = ../instrument-cluster.qface +PLUGIN_TYPE = interfaceframework + +RESOURCES += \ + simulation.qrc + +# Additional import path used to resolve QML modules in Qt Creator's code model +QML_IMPORT_PATH = $$OUT_PWD/../frontend/qml + +CONFIG += install_ok # Do not cargo-cult this! +target.path = $$[QT_INSTALL_EXAMPLES]/interfaceframework/qface-tutorial/chapter7-own-template +INSTALLS += target diff --git a/examples/interfaceframework/qface-tutorial/ch7-own-template/backend_simulator/simulation.qml b/examples/interfaceframework/qface-tutorial/ch7-own-template/backend_simulator/simulation.qml new file mode 100644 index 00000000..db44cfb5 --- /dev/null +++ b/examples/interfaceframework/qface-tutorial/ch7-own-template/backend_simulator/simulation.qml @@ -0,0 +1,98 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2019 Luxoft Sweden AB +// Copyright (C) 2018 Pelagicore AG +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import Example.If.InstrumentClusterModule.simulation + +QtObject { + property var settings : IfSimulator.findData(IfSimulator.simulationData, "InstrumentCluster") + property bool defaultInitialized: false + property LoggingCategory qLcInstrumentCluster: LoggingCategory { + name: "example.if.instrumentclustermodule.simulation.instrumentclusterbackend" + } + property var backend : InstrumentClusterBackend { + + function initialize() { + console.log(qLcInstrumentCluster, "INITIALIZE") + if (!defaultInitialized) { + IfSimulator.initializeDefault(settings, backend) + defaultInitialized = true + } + Base.initialize() + } + + property int gearSpeed: 260 / 6 + property int currentGear: speed / gearSpeed + rpm: currentGear >= 1 ? 3000 + (speed % gearSpeed) / gearSpeed * 2000 + : (speed % gearSpeed) / gearSpeed * 5000 + + property var animation: SequentialAnimation { + loops: Animation.Infinite + running: true + + ParallelAnimation { + SequentialAnimation { + + NumberAnimation { + target: backend + property: "speed" + from: 0 + to: 80 + duration: 4000 + } + + NumberAnimation { + target: backend + property: "speed" + to: 50 + duration: 2000 + } + + NumberAnimation { + target: backend + property: "speed" + to: 200 + duration: 7000 + } + + ScriptAction { + script: { + backend.currentWarning = InstrumentClusterModule.warning("red","LOW FUEL", "images/fuelsymbol_orange.png") + } + } + + NumberAnimation { + target: backend + property: "speed" + to: 0 + duration: 5000 + } + + ScriptAction { + script: { + backend.currentWarning = InstrumentClusterModule.warning() + } + } + } + + NumberAnimation { + target: backend + property: "fuel" + from: 1 + to: 0 + } + } + + NumberAnimation { + target: backend + property: "fuel" + from: 0 + to: 1 + duration: 4000 + } + } + } +} + diff --git a/examples/interfaceframework/qface-tutorial/ch7-own-template/backend_simulator/simulation.qrc b/examples/interfaceframework/qface-tutorial/ch7-own-template/backend_simulator/simulation.qrc new file mode 100644 index 00000000..64b8d048 --- /dev/null +++ b/examples/interfaceframework/qface-tutorial/ch7-own-template/backend_simulator/simulation.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/"> + <file>simulation.qml</file> + </qresource> +</RCC> diff --git a/examples/interfaceframework/qface-tutorial/ch7-own-template/chapter6-own-backend.pro b/examples/interfaceframework/qface-tutorial/ch7-own-template/chapter6-own-backend.pro new file mode 100644 index 00000000..86589117 --- /dev/null +++ b/examples/interfaceframework/qface-tutorial/ch7-own-template/chapter6-own-backend.pro @@ -0,0 +1,16 @@ +TEMPLATE = subdirs +SUBDIRS += \ + instrument-cluster \ + frontend \ + backend_simulator \ + backend_dbus \ + imports \ + demo_server \ + +instrument-cluster.depends = frontend +backend_simulator.depends = frontend +backend_dbus.depends = frontend +imports.depends = frontend +demo_server.depends = frontend + +OTHER_FILES += instrument-cluster.qface diff --git a/examples/interfaceframework/qface-tutorial/ch7-own-template/demo_server/CMakeLists.txt b/examples/interfaceframework/qface-tutorial/ch7-own-template/demo_server/CMakeLists.txt new file mode 100644 index 00000000..f621a508 --- /dev/null +++ b/examples/interfaceframework/qface-tutorial/ch7-own-template/demo_server/CMakeLists.txt @@ -0,0 +1,44 @@ +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +set(CMAKE_AUTOMOC ON) + +if(NOT DEFINED INSTALL_EXAMPLESDIR) + set(INSTALL_EXAMPLESDIR "examples") +endif() + +set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/interfaceframework/qface-tutorial/chapter7-own-template") + +find_package(Qt6 REQUIRED COMPONENTS Core DBus InterfaceFramework) + +set_source_files_properties(instrumentcluster.xml PROPERTIES INCLUDE "") +set(cluster_SRCS) +qt_add_dbus_adaptor(cluster_SRCS + instrumentcluster.xml + dbus_conversion.h + "" # empty parent_class value on purpose to not pass -l flag + instrumentcluster_adaptor +) + +qt_add_executable(chapter7-demo-server + dbus_conversion.h + instrumentcluster.cpp instrumentcluster.h + main.cpp + ${cluster_SRCS} +) + +set_target_properties(chapter7-demo-server PROPERTIES RUNTIME_OUTPUT_DIRECTORY ../) +set_target_properties(chapter7-demo-server PROPERTIES + WIN32_EXECUTABLE TRUE + MACOSX_BUNDLE FALSE +) + +target_link_libraries(chapter7-demo-server PUBLIC + libIc_chapter7 + Qt::DBus +) + +install(TARGETS chapter7-demo-server + RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" + BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" + LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" +) diff --git a/examples/interfaceframework/qface-tutorial/ch7-own-template/demo_server/dbus_conversion.h b/examples/interfaceframework/qface-tutorial/ch7-own-template/demo_server/dbus_conversion.h new file mode 100644 index 00000000..541be767 --- /dev/null +++ b/examples/interfaceframework/qface-tutorial/ch7-own-template/demo_server/dbus_conversion.h @@ -0,0 +1,65 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2019 Luxoft Sweden AB +// Copyright (C) 2018 Pelagicore AG +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef DBUS_CONVERSION_H +#define DBUS_CONVERSION_H + +#include <QDBusArgument> +#include "warning.h" +#include "instrumentclustermodule.h" + +QT_BEGIN_NAMESPACE + +Q_ALWAYS_INLINE QDBusArgument &operator<<(QDBusArgument &argument, const Warning &data) +{ + argument.beginStructure(); + argument << data.color(); + argument << data.text(); + argument << data.icon(); + argument.endStructure(); + + return argument; +} + +Q_ALWAYS_INLINE const QDBusArgument &operator>>(const QDBusArgument &argument, Warning &data) +{ + argument.beginStructure(); + QString color; + QString text; + QString icon; + argument >> color; + argument >> text; + argument >> icon; + data.setColor(color); + data.setText(text); + data.setIcon(icon); + argument.endStructure(); + + return argument; +} + +Q_ALWAYS_INLINE QDBusArgument &operator<<(QDBusArgument &argument, InstrumentClusterModule::SystemType data) +{ + argument.beginStructure(); + argument << quint32(data); + argument.endStructure(); + + return argument; +} + +Q_ALWAYS_INLINE const QDBusArgument &operator>>(const QDBusArgument &argument, InstrumentClusterModule::SystemType &data) +{ + argument.beginStructure(); + quint32 systemType; + argument >> systemType; + data = InstrumentClusterModule::toSystemType(systemType, nullptr); + argument.endStructure(); + + return argument; +} + +QT_END_NAMESPACE + +#endif // DBUS_CONVERSION_H diff --git a/examples/interfaceframework/qface-tutorial/ch7-own-template/demo_server/demo_server.pro b/examples/interfaceframework/qface-tutorial/ch7-own-template/demo_server/demo_server.pro new file mode 100644 index 00000000..47c41984 --- /dev/null +++ b/examples/interfaceframework/qface-tutorial/ch7-own-template/demo_server/demo_server.pro @@ -0,0 +1,26 @@ +TARGET = chapter7-demo-server +DESTDIR = .. + +QT += interfaceframework dbus +QT -= gui +CONFIG -= app_bundle + +LIBS += -L$$OUT_PWD/../ -l$$qtLibraryTarget(QtIfInstrumentCluster) +INCLUDEPATH += $$OUT_PWD/../frontend + +cluster.files = instrumentcluster.xml +cluster.header_flags += -i dbus_conversion.h + +DBUS_ADAPTORS += cluster + +CONFIG += install_ok # Do not cargo-cult this! +target.path = $$[QT_INSTALL_EXAMPLES]/interfaceframework/qface-tutorial/chapter7-own-template +INSTALLS += target + +SOURCES += \ + instrumentcluster.cpp \ + main.cpp + +HEADERS += \ + instrumentcluster.h \ + dbus_conversion.h \ diff --git a/examples/interfaceframework/qface-tutorial/ch7-own-template/demo_server/instrumentcluster.cpp b/examples/interfaceframework/qface-tutorial/ch7-own-template/demo_server/instrumentcluster.cpp new file mode 100644 index 00000000..0f0c513c --- /dev/null +++ b/examples/interfaceframework/qface-tutorial/ch7-own-template/demo_server/instrumentcluster.cpp @@ -0,0 +1,117 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2019 Luxoft Sweden AB +// Copyright (C) 2018 Pelagicore AG +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "instrumentcluster.h" +#include <QThread> + +InstrumentCluster::InstrumentCluster(QObject *parent) + : QObject(parent) + , m_speed(0) + , m_rpm(0) + , m_fuel(0) + , m_temperature(15) + , m_systemType(InstrumentClusterModule::Metric) +{ + startTimer(100); +} + +int InstrumentCluster::speed() const +{ + return m_speed; +} + +int InstrumentCluster::rpm() const +{ + return m_rpm; +} + +qreal InstrumentCluster::fuel() const +{ + return m_fuel; +} + +qreal InstrumentCluster::temperature() const +{ + return m_temperature; +} + +InstrumentClusterModule::SystemType InstrumentCluster::systemType() const +{ + return m_systemType; +} + +Warning InstrumentCluster::currentWarning() const +{ + return m_currentWarning; +} + +void InstrumentCluster::setSpeed(int speed) +{ + if (m_speed == speed) + return; + + m_speed = speed; + emit speedChanged(m_speed); +} + +void InstrumentCluster::setRpm(int rpm) +{ + if (m_rpm == rpm) + return; + + m_rpm = rpm; + emit rpmChanged(m_rpm); +} + +void InstrumentCluster::setFuel(qreal fuel) +{ + if (qFuzzyCompare(m_fuel, fuel)) + return; + + m_fuel = fuel; + emit fuelChanged(m_fuel); +} + +void InstrumentCluster::setTemperature(qreal temperature) +{ + if (qFuzzyCompare(m_temperature, temperature)) + return; + + m_temperature = temperature; + emit temperatureChanged(m_temperature); +} + +void InstrumentCluster::setSystemType(InstrumentClusterModule::SystemType systemType) +{ + if (m_systemType == systemType) + return; + + m_systemType = systemType; + emit systemTypeChanged(m_systemType); +} + +void InstrumentCluster::setCurrentWarning(const Warning ¤tWarning) +{ + if (m_currentWarning == currentWarning) + return; + + m_currentWarning = currentWarning; + emit currentWarningChanged(m_currentWarning); +} + +void InstrumentCluster::timerEvent(QTimerEvent *event) +{ + Q_UNUSED(event); + + if (speed() >= 250) + setSpeed(0); + else + setSpeed(speed() + 1); + + if (rpm() >= 5000) + setRpm(0); + else + setRpm(rpm() + 100); +} diff --git a/examples/interfaceframework/qface-tutorial/ch7-own-template/demo_server/instrumentcluster.h b/examples/interfaceframework/qface-tutorial/ch7-own-template/demo_server/instrumentcluster.h new file mode 100644 index 00000000..43a6922b --- /dev/null +++ b/examples/interfaceframework/qface-tutorial/ch7-own-template/demo_server/instrumentcluster.h @@ -0,0 +1,62 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2019 Luxoft Sweden AB +// Copyright (C) 2018 Pelagicore AG +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef INSTRUMENTCLUSTER_H +#define INSTRUMENTCLUSTER_H + +#include <QObject> +#include "warning.h" +#include "instrumentclustermodule.h" + +class InstrumentCluster : public QObject +{ + Q_OBJECT + + Q_PROPERTY(int speed READ speed WRITE setSpeed NOTIFY speedChanged) + Q_PROPERTY(int rpm READ rpm WRITE setRpm NOTIFY rpmChanged) + Q_PROPERTY(qreal fuel READ fuel WRITE setFuel NOTIFY fuelChanged) + Q_PROPERTY(qreal temperature READ temperature WRITE setTemperature NOTIFY temperatureChanged) + Q_PROPERTY(InstrumentClusterModule::SystemType systemType READ systemType WRITE setSystemType NOTIFY systemTypeChanged) + Q_PROPERTY(Warning currentWarning READ currentWarning WRITE setCurrentWarning NOTIFY currentWarningChanged) + +public: + explicit InstrumentCluster(QObject *parent = nullptr); + + Q_INVOKABLE int speed() const; + Q_INVOKABLE int rpm() const; + Q_INVOKABLE qreal fuel() const; + Q_INVOKABLE qreal temperature() const; + Q_INVOKABLE InstrumentClusterModule::SystemType systemType() const; + Q_INVOKABLE Warning currentWarning() const; + +signals: + void speedChanged(int speed); + void rpmChanged(int rpm); + void fuelChanged(qreal fuel); + void temperatureChanged(qreal temperature); + void systemTypeChanged(InstrumentClusterModule::SystemType systemType); + void currentWarningChanged(Warning currentWarning); + +public slots: + void setSpeed(int speed); + void setRpm(int rpm); + void setFuel(qreal fuel); + void setTemperature(qreal temperature); + void setSystemType(InstrumentClusterModule::SystemType systemType); + void setCurrentWarning(const Warning ¤tWarning); + +protected: + void timerEvent(QTimerEvent *event) override; + +private: + int m_speed; + int m_rpm; + qreal m_fuel; + qreal m_temperature; + InstrumentClusterModule::SystemType m_systemType; + Warning m_currentWarning; +}; + +#endif // INSTRUMENTCLUSTER_H diff --git a/examples/interfaceframework/qface-tutorial/ch7-own-template/demo_server/instrumentcluster.xml b/examples/interfaceframework/qface-tutorial/ch7-own-template/demo_server/instrumentcluster.xml new file mode 100644 index 00000000..e5e20d09 --- /dev/null +++ b/examples/interfaceframework/qface-tutorial/ch7-own-template/demo_server/instrumentcluster.xml @@ -0,0 +1,55 @@ +<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> +<node> + <interface name="Example.If.InstrumentCluster"> + <property name="speed" type="i" access="read"/> + <property name="rpm" type="i" access="read"/> + <property name="fuel" type="d" access="read"/> + <property name="temperature" type="d" access="read"/> + <property name="systemType" type="(i)" access="read"> + <annotation name="org.qtproject.QtDBus.QtTypeName" value="InstrumentClusterModule::SystemType"/> + </property> + <property name="currentWarning" type="(sss)" access="read"> + <annotation name="org.qtproject.QtDBus.QtTypeName" value="Warning"/> + </property> + <method name="speed" > + <arg name="speed" type="i" direction="out"/> + </method> + <method name="rpm" > + <arg name="rpm" type="i" direction="out"/> + </method> + <method name="fuel" > + <arg name="fuel" type="d" direction="out"/> + </method> + <method name="temperature" > + <arg name="temperature" type="d" direction="out"/> + </method> + <method name="systemType" > + <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="InstrumentClusterModule::SystemType"/> + <arg name="systemType" type="(i)" direction="out"/> + </method> + <method name="currentWarning" > + <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="Warning"/> + <arg name="currentWarning" type="(sss)" direction="out"/> + </method> + <signal name="speedChanged"> + <arg name="speed" type="i" direction="out"/> + </signal> + <signal name="rpmChanged"> + <arg name="rpm" type="i" direction="out"/> + </signal> + <signal name="fuelChanged"> + <arg name="fuel" type="d" direction="out"/> + </signal> + <signal name="temperatureChanged"> + <arg name="temperature" type="d" direction="out"/> + </signal> + <signal name="systemTypeChanged"> + <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="InstrumentClusterModule::SystemType"/> + <arg name="systemType" type="(i)" direction="out"/> + </signal> + <signal name="currentWarningChanged"> + <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="Warning"/> + <arg name="currentWarning" type="(sss)" direction="out"/> + </signal> + </interface> +</node> diff --git a/examples/interfaceframework/qface-tutorial/ch7-own-template/demo_server/main.cpp b/examples/interfaceframework/qface-tutorial/ch7-own-template/demo_server/main.cpp new file mode 100644 index 00000000..e20c985b --- /dev/null +++ b/examples/interfaceframework/qface-tutorial/ch7-own-template/demo_server/main.cpp @@ -0,0 +1,26 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2019 Luxoft Sweden AB +// Copyright (C) 2018 Pelagicore AG +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include <QCoreApplication> +#include <QDBusConnection> + +#include "instrumentcluster.h" +#include "instrumentcluster_adaptor.h" + +int main(int argc, char *argv[]) +{ + QCoreApplication app(argc, argv); + + InstrumentCluster cluster; + qDBusRegisterMetaType<Warning>(); + qDBusRegisterMetaType<InstrumentClusterModule::SystemType>(); + + InstrumentClusterAdaptor adaptor(&cluster); + QDBusConnection connection = QDBusConnection::sessionBus(); + connection.registerObject("/", &cluster); + connection.registerService("Example.If.InstrumentCluster"); + + return app.exec(); +} diff --git a/examples/interfaceframework/qface-tutorial/ch7-own-template/frontend/CMakeLists.txt b/examples/interfaceframework/qface-tutorial/ch7-own-template/frontend/CMakeLists.txt new file mode 100644 index 00000000..8f0f61b7 --- /dev/null +++ b/examples/interfaceframework/qface-tutorial/ch7-own-template/frontend/CMakeLists.txt @@ -0,0 +1,34 @@ +cmake_minimum_required(VERSION 3.16) +project(QtIfInstrumentCluster LANGUAGES CXX) + +set(CMAKE_AUTOMOC ON) + +if(NOT DEFINED INSTALL_EXAMPLESDIR) + set(INSTALL_EXAMPLESDIR "examples") +endif() + +set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/interfaceframework/qface-tutorial/chapter7-own-template") + +find_package(Qt6 REQUIRED COMPONENTS Core Gui InterfaceFramework Qml Quick) + +qt_add_library(libIc_chapter7) +set_target_properties(libIc_chapter7 PROPERTIES OUTPUT_NAME "InstrumentCluster") +set_target_properties(libIc_chapter7 PROPERTIES RUNTIME_OUTPUT_DIRECTORY ../) + +# Interface Framework Generator: +qt_ifcodegen_extend_target(libIc_chapter7 + IDL_FILES ../instrument-cluster.qface + TEMPLATE frontend +) + +set(import_path "${CMAKE_CURRENT_BINARY_DIR}/frontend/qml") +if (NOT ${import_path} IN_LIST QML_IMPORT_PATH) + list (APPEND QML_IMPORT_PATH "${import_path}") + set(QML_IMPORT_PATH ${QML_IMPORT_PATH} CACHE INTERNAL "" FORCE) +endif() + +install(TARGETS libIc_chapter7 + RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" + BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" + LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" +) diff --git a/examples/interfaceframework/qface-tutorial/ch7-own-template/frontend/frontend.pro b/examples/interfaceframework/qface-tutorial/ch7-own-template/frontend/frontend.pro new file mode 100644 index 00000000..d54111ef --- /dev/null +++ b/examples/interfaceframework/qface-tutorial/ch7-own-template/frontend/frontend.pro @@ -0,0 +1,12 @@ +TARGET = $$qtLibraryTarget(QtIfInstrumentCluster) +TEMPLATE = lib +DESTDIR = .. + +QT += interfaceframework qml quick + +CONFIG += ifcodegen +IFCODEGEN_SOURCES = ../instrument-cluster.qface + +CONFIG += install_ok # Do not cargo-cult this! +target.path = $$[QT_INSTALL_EXAMPLES]/interfaceframework/qface-tutorial/chapter7-own-template +INSTALLS += target diff --git a/examples/interfaceframework/qface-tutorial/ch7-own-template/imports/CMakeLists.txt b/examples/interfaceframework/qface-tutorial/ch7-own-template/imports/CMakeLists.txt new file mode 100644 index 00000000..9f1d217c --- /dev/null +++ b/examples/interfaceframework/qface-tutorial/ch7-own-template/imports/CMakeLists.txt @@ -0,0 +1,36 @@ +set(CMAKE_AUTOMOC ON) + +if(NOT DEFINED INSTALL_EXAMPLESDIR) + set(INSTALL_EXAMPLESDIR "examples") +endif() + +set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/interfaceframework/qface-tutorial/chapter7-own-template/imports") + +find_package(Qt6 REQUIRED COMPONENTS Core Gui InterfaceFramework) + +# Interface Framework Generator: +qt_ifcodegen_import_variables(CLUSTER + IDL_FILES ../instrument-cluster.qface + TEMPLATE qmlplugin +) + +qt_add_qml_module(ic_chapter7_imports + OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${CLUSTER_URI_PATH}" + URI ${CLUSTER_URI} + VERSION ${CLUSTER_VERSION} + PLUGIN_TARGET ic_chapter7_imports + NO_PLUGIN_OPTIONAL + NO_GENERATE_PLUGIN_SOURCE + SOURCES + ${CLUSTER_SOURCES} +) + +target_link_libraries(ic_chapter7_imports PUBLIC + libIc_chapter7 +) + +install(TARGETS ic_chapter7_imports + RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" + BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" + LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" +) diff --git a/examples/interfaceframework/qface-tutorial/ch7-own-template/imports/imports.pro b/examples/interfaceframework/qface-tutorial/ch7-own-template/imports/imports.pro new file mode 100644 index 00000000..cee967c5 --- /dev/null +++ b/examples/interfaceframework/qface-tutorial/ch7-own-template/imports/imports.pro @@ -0,0 +1,25 @@ +TEMPLATE = lib +CONFIG += plugin +QT += interfaceframework + +LIBS += -L$$OUT_PWD/../ -l$$qtLibraryTarget(QtIfInstrumentCluster) +INCLUDEPATH += $$OUT_PWD/../frontend + +IFCODEGEN_TEMPLATE = qmlplugin +IFCODEGEN_SOURCES = ../instrument-cluster.qface + +load(ifcodegen) + +DESTDIR = $$OUT_PWD/$$replace(URI, \\., /) +QMAKE_RPATHDIR += $$QMAKE_REL_RPATH_BASE/../../../../ + +exists($$OUT_PWD/qmldir) { + cpqmldir.files = $$OUT_PWD/qmldir \ + $$OUT_PWD/plugins.qmltypes + cpqmldir.path = $$DESTDIR + cpqmldir.CONFIG = no_check_exist + COPIES += cpqmldir +} + +target.path = $$[QT_INSTALL_EXAMPLES]/interfaceframework/qface-tutorial/chapter7-own-template/imports +INSTALLS += target diff --git a/examples/interfaceframework/qface-tutorial/ch7-own-template/instrument-cluster.qface b/examples/interfaceframework/qface-tutorial/ch7-own-template/instrument-cluster.qface new file mode 100644 index 00000000..f3f93a7e --- /dev/null +++ b/examples/interfaceframework/qface-tutorial/ch7-own-template/instrument-cluster.qface @@ -0,0 +1,24 @@ +@config_simulator: { simulationFile: "qrc:/simulation.qml" } +module Example.If.InstrumentClusterModule 1.0 + +@config_dbus: { xml: "../demo_server/instrumentcluster.xml", interfaceName: "Example.If.InstrumentCluster", className: "ExampleIfInstrumentClusterInterface" } +interface InstrumentCluster { + readonly int speed; + readonly int rpm; + readonly real fuel; + @config_simulator: { default: 15 } + readonly real temperature; + readonly SystemType systemType; + readonly Warning currentWarning; +} + +enum SystemType { + Imperial, + Metric +} + +struct Warning { + string color + string text + string icon +} diff --git a/examples/interfaceframework/qface-tutorial/ch7-own-template/instrument-cluster/CMakeLists.txt b/examples/interfaceframework/qface-tutorial/ch7-own-template/instrument-cluster/CMakeLists.txt new file mode 100644 index 00000000..004a7472 --- /dev/null +++ b/examples/interfaceframework/qface-tutorial/ch7-own-template/instrument-cluster/CMakeLists.txt @@ -0,0 +1,79 @@ +set(CMAKE_AUTOMOC ON) + +if(NOT DEFINED INSTALL_EXAMPLESDIR) + set(INSTALL_EXAMPLESDIR "examples") +endif() + +set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/interfaceframework/qface-tutorial/chapter7-own-template") + +find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick) + +qt_add_executable(ic_chapter7 + main.cpp +) + +set_target_properties(ic_chapter7 PROPERTIES OUTPUT_NAME "chapter7-own-template") +set_target_properties(ic_chapter7 PROPERTIES RUNTIME_OUTPUT_DIRECTORY ../) +set_target_properties(ic_chapter7 PROPERTIES + WIN32_EXECUTABLE TRUE + MACOSX_BUNDLE FALSE +) + +target_link_libraries(ic_chapter7 PUBLIC + Qt::Core + Qt::Gui + Qt::Qml + Qt::Quick +) + +# Resources: +set(app_resource_files + "Cluster.qml" + "Dial.qml" + "Fuel.qml" + "Label.qml" + "LeftDial.qml" + "RightDial.qml" + "Top.qml" +) + +qt_add_resources(ic_chapter7 "app" + PREFIX + "/" + FILES + ${app_resource_files} +) + +set(images_resource_files + "../../images/+--.png" + "../../images/P-R-N-D.png" + "../../images/dial_cursor.png" + "../../images/dial_cursor_right.png" + "../../images/dial_fill_color.png" + "../../images/dial_fill_color_left.png" + "../../images/dial_pattern.png" + "../../images/fuel.png" + "../../images/fuel_level.png" + "../../images/fuelsymbol_orange.png" + "../../images/left_dial.png" + "../../images/mask_overlay.png" + "../../images/middle-bkg.png" + "../../images/middle-circle.png" + "../../images/right_dial.png" + "../../images/top_bar.png" +) + +qt_add_resources(ic_chapter7 "images" + PREFIX + "/images" + BASE + "../../images" + FILES + ${images_resource_files} +) + +install(TARGETS ic_chapter7 + RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" + BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" + LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" +) diff --git a/examples/interfaceframework/qface-tutorial/ch7-own-template/instrument-cluster/Cluster.qml b/examples/interfaceframework/qface-tutorial/ch7-own-template/instrument-cluster/Cluster.qml new file mode 100644 index 00000000..f5494ba3 --- /dev/null +++ b/examples/interfaceframework/qface-tutorial/ch7-own-template/instrument-cluster/Cluster.qml @@ -0,0 +1,56 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2019 Luxoft Sweden AB +// Copyright (C) 2018 Pelagicore AG +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Window +import Example.If.InstrumentClusterModule + +Window { + id: root + + width: 1920 + height: 720 + title: qsTr("QtIF Instrument Cluster Chapter 6") + visible: true + color: "#0c0c0c" + + InstrumentCluster { + id: instrumentCluster + } + + LeftDial { + id: leftDial + anchors.left: parent.left + anchors.leftMargin: 0.1 * width + + value: instrumentCluster.speed + metricSystem: instrumentCluster.systemType === InstrumentClusterModule.Metric + } + + RightDial { + id: rightDial + anchors.right: parent.right + anchors.rightMargin: 0.1 * width + + value: instrumentCluster.rpm + warningColor: instrumentCluster.currentWarning.color + warningText: instrumentCluster.currentWarning.text + warningIcon: instrumentCluster.currentWarning.icon + fuelLevel: 1.0//instrumentCluster.fuel + } + + Top { + id: topbar + y: 7 + anchors.horizontalCenter: parent.horizontalCenter + + temperature: instrumentCluster.temperature + } + + Image { + anchors.fill: parent + source: Qt.resolvedUrl("images/mask_overlay.png") + } +} diff --git a/examples/interfaceframework/qface-tutorial/ch7-own-template/instrument-cluster/Dial.qml b/examples/interfaceframework/qface-tutorial/ch7-own-template/instrument-cluster/Dial.qml new file mode 100644 index 00000000..bdebb5ad --- /dev/null +++ b/examples/interfaceframework/qface-tutorial/ch7-own-template/instrument-cluster/Dial.qml @@ -0,0 +1,30 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2019 Luxoft Sweden AB +// Copyright (C) 2018 Pelagicore AG +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick + +Item { + id: root + + width: 480 + height: 480 + + property real value: 0 + property int upDuration: 2000 + property int downDuration: 1000 + property string fillImage: "images/dial_fill_color_left.png" + property string circleRadius: "0.193" + property string dialCursor: "images/dial_cursor.png" + + Image { + id: meter + property real min: -83.5 + property real max: 157 + width: root.width + height: width - 1 + rotation: min + (max - min) * root.value + source: Qt.resolvedUrl(root.dialCursor) + } +} diff --git a/examples/interfaceframework/qface-tutorial/ch7-own-template/instrument-cluster/Fuel.qml b/examples/interfaceframework/qface-tutorial/ch7-own-template/instrument-cluster/Fuel.qml new file mode 100644 index 00000000..6c8a15b5 --- /dev/null +++ b/examples/interfaceframework/qface-tutorial/ch7-own-template/instrument-cluster/Fuel.qml @@ -0,0 +1,34 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2019 Luxoft Sweden AB +// Copyright (C) 2018 Pelagicore AG +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick + +Item { + id: root + + width: 0.73 * 720 + height: width - 1 + + property real value: 0 + + Item { + width: root.value * parent.width + height: parent.height + clip: true + Image { + width: root.width + height: root.height + source: Qt.resolvedUrl("images/fuel_level.png") + } + } + + Image { + id: fuel + anchors.fill: parent + source: Qt.resolvedUrl("images/fuel.png") + + } +} + diff --git a/examples/interfaceframework/qface-tutorial/ch7-own-template/instrument-cluster/Label.qml b/examples/interfaceframework/qface-tutorial/ch7-own-template/instrument-cluster/Label.qml new file mode 100644 index 00000000..2c597f93 --- /dev/null +++ b/examples/interfaceframework/qface-tutorial/ch7-own-template/instrument-cluster/Label.qml @@ -0,0 +1,36 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2019 Luxoft Sweden AB +// Copyright (C) 2018 Pelagicore AG +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick + +Item { + id: root + + property alias textObject: textElement + property alias text: textElement.text + property alias font: textElement.font + property alias fontSize: textElement.font.pixelSize + property alias color: textElement.color + property alias textFormat: textElement.textFormat + property alias horizontalAlignment: textElement.horizontalAlignment + property alias elide: textElement.elide + property alias wrapMode: textElement.wrapMode + property alias verticalAlignment: textElement.verticalAlignment + + Text { + id: textElement + + anchors.fill: parent + anchors.margins: 8 + + verticalAlignment: Text.AlignVCenter + + font.family: "Source Sans Pro" + font.pixelSize: 28 + font.weight: Font.Light + + color: "white" + } +} diff --git a/examples/interfaceframework/qface-tutorial/ch7-own-template/instrument-cluster/LeftDial.qml b/examples/interfaceframework/qface-tutorial/ch7-own-template/instrument-cluster/LeftDial.qml new file mode 100644 index 00000000..950c1fc2 --- /dev/null +++ b/examples/interfaceframework/qface-tutorial/ch7-own-template/instrument-cluster/LeftDial.qml @@ -0,0 +1,72 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2019 Luxoft Sweden AB +// Copyright (C) 2018 Pelagicore AG +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick + +Item { + id: root + + width: 0.8 * 720 + height: 720 + + property real value: 0.0 + property bool metricSystem: false + + Image { + id: overlay + + width: 0.91 * root.width + height: 0.98 * width + anchors.right: parent.right + anchors.rightMargin: 0 + anchors.verticalCenter: parent.verticalCenter + source: Qt.resolvedUrl("images/left_dial.png") + } + + Text { + id: speedText + + anchors.verticalCenter: overlay.verticalCenter + anchors.horizontalCenter: overlay.horizontalCenter + anchors.verticalCenterOffset: -7 + anchors.horizontalCenterOffset: 5 + font.family: "Source Sans Pro" + font.pixelSize: 60 + font.letterSpacing: 4 + color: "white" + text: root.value + } + + Rectangle { + width: 60 + height: 1 + opacity: 0.4 + anchors.top: speedText.bottom + anchors.topMargin: -8 + anchors.horizontalCenter: overlay.horizontalCenter + anchors.horizontalCenterOffset: 2 + } + + Text { + id: mph + anchors.top: speedText.bottom + anchors.topMargin: -5 + anchors.horizontalCenter: overlay.horizontalCenter + anchors.horizontalCenterOffset: 2 + font.family: "Source Sans Pro" + font.pixelSize: 24 + color: "white" + text: root.metricSystem ? "km/h" : "mph" + } + + Dial { + width: 0.66 * root.height + height: width + anchors.centerIn: overlay + anchors.verticalCenterOffset: 2 + fillImage: "images/dial_fill_color_left.png" + value: root.value / 240 + } +} diff --git a/examples/interfaceframework/qface-tutorial/ch7-own-template/instrument-cluster/RightDial.qml b/examples/interfaceframework/qface-tutorial/ch7-own-template/instrument-cluster/RightDial.qml new file mode 100644 index 00000000..81565001 --- /dev/null +++ b/examples/interfaceframework/qface-tutorial/ch7-own-template/instrument-cluster/RightDial.qml @@ -0,0 +1,180 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2019 Luxoft Sweden AB +// Copyright (C) 2018 Pelagicore AG +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick + +Item { + id: root + + width: 0.8 * 720 + height: 720 + + property color warningColor: "transparent" + property string warningIcon + property string warningText + property int value + property alias fuelLevel: fuelMeter.value + + Item { + width: root.width/3 + height: 0.1 * root.width + anchors.bottom: overlay.top + anchors.left: parent.left + anchors.leftMargin: 0.2 * root.width + + Image { + id: gears + + width: 0.2 * root.width + height: 0.25 * width + anchors.bottom: parent.bottom + anchors.right: parent.right + source: Qt.resolvedUrl("images/P-R-N-D.png") + } + + Image { + id: plusMinus + anchors.bottom: parent.bottom + anchors.left: gears.right + anchors.leftMargin: 10 + source: Qt.resolvedUrl("images/+--.png") + } + } + + Image { + id: overlay + + width: 0.91 * root.width + height: 0.99 * width + + anchors.left: parent.left + anchors.leftMargin: 0 + anchors.verticalCenter: parent.verticalCenter + source: Qt.resolvedUrl("images/right_dial.png") + + Rectangle { + id: rect + width: circle.width + 5 + height: width + radius: width + color: "transparent" + border.color: root.warningColor + border.width: rect.borderWidth + anchors.centerIn: parent + anchors.horizontalCenterOffset: -3 + + property int borderWidth: 3 + + SequentialAnimation { + running: root.warningColor != "transparent" + loops: Animation.Infinite + NumberAnimation { + + target: rect + properties: "borderWidth" + from: 3 + to: 7 + duration: 500 + } + + NumberAnimation { + + target: rect + properties: "borderWidth" + from: 7 + to: 3 + duration: 500 + } + + onStopped: rect.borderWidth = 3 + + } + } + + Image { + id: circle + + width: root.width/2 + height: width + anchors.centerIn: parent + anchors.horizontalCenterOffset: -6 + anchors.verticalCenterOffset: 0 + source: Qt.resolvedUrl("images/middle-bkg.png") + + Image { + id: circle_overlay + + width: parent.width + height: parent.height + anchors.centerIn: parent + source: Qt.resolvedUrl("images/middle-circle.png") + } + + Image { + id: fuelSymbol + width: 70 + height: 70 + anchors.top: parent.top + anchors.topMargin: 55 + anchors.horizontalCenter: parent.horizontalCenter + anchors.horizontalCenterOffset: 0 + source: root.warningIcon + fillMode: Image.PreserveAspectFit + + } + + Item { + width: parent.width + height: parent.height/4 + anchors.centerIn: parent + anchors.verticalCenterOffset: 40 + + Rectangle { + id: speedText + width: parent.width - 80 + height: 40 + radius: 20 + anchors.horizontalCenter: parent.horizontalCenter + gradient: Gradient { + GradientStop { position: 0.0; color: Qt.darker("grey", 1.5) } + GradientStop { position: 0.4; color: "#0c0c0c" } + } + + Label { + width: parent.width + anchors.centerIn: parent + horizontalAlignment: Text.AlignHCenter + font.pixelSize: text.length > 10 ? 18 : 24 + font.bold: true + text: root.warningText + elide: Text.ElideMiddle + } + } + } + } + } + + Fuel { + id: fuelMeter + anchors.bottom: parent.bottom + anchors.bottomMargin: 0.05 * root.height + anchors.horizontalCenter: parent.horizontalCenter + anchors.horizontalCenterOffset: -20 + } + + Dial { + id: dial + + width: 0.69 * root.height + height: width + anchors.centerIn: overlay + anchors.verticalCenterOffset: 0 + anchors.horizontalCenterOffset: -5 + fillImage: "images/dial_fill_color.png" + circleRadius: "0.29" + dialCursor: "images/dial_cursor_right.png" + value: root.value / 7000 + } +} diff --git a/examples/interfaceframework/qface-tutorial/ch7-own-template/instrument-cluster/Top.qml b/examples/interfaceframework/qface-tutorial/ch7-own-template/instrument-cluster/Top.qml new file mode 100644 index 00000000..a1219d3e --- /dev/null +++ b/examples/interfaceframework/qface-tutorial/ch7-own-template/instrument-cluster/Top.qml @@ -0,0 +1,83 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2019 Luxoft Sweden AB +// Copyright (C) 2018 Pelagicore AG +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick + +Item { + id: root + width: 0.37 * 1920 + height: 0.12 * 720 + + property real temperature: 0 + + Image { + id: background + width: parent.width + height: parent.height + source: Qt.resolvedUrl("images/top_bar.png") + } + + Label { + id: timeText + anchors.verticalCenter: background.verticalCenter + anchors.left: background.left + anchors.leftMargin: 0.2 * background.width + + font.pixelSize: 0.42 * background.height + font.bold: true + + text: Qt.formatTime(currentDate, "hh:mm") + + property var currentDate: new Date(); + + Timer { + interval: 1000 + repeat: true + running: true + onTriggered: { + timeText.currentDate = new Date(); + } + } + } + + Item { + id: navigator + + width: 0.25 * background.width + height: background.height + anchors.verticalCenter: background.verticalCenter + anchors.horizontalCenter: background.horizontalCenter + + Row { + id: row + property int radius: 7 + anchors.centerIn: parent + anchors.verticalCenterOffset: -16 + spacing: 8 + + Repeater { + model: 3 + delegate: Rectangle { + height: row.radius * 2 + width: row.radius * 2 + radius: row.radius + color: 1 === index ? "white" : "#4d4d4d" + } + } + } + } + + Label { + id: temperatureText + anchors.verticalCenter: background.verticalCenter + anchors.left: navigator.right + anchors.leftMargin: 15 + + font.pixelSize: 0.42 * background.height + font.bold: true + + text: root.temperature + "°C" + } +} diff --git a/examples/interfaceframework/qface-tutorial/ch7-own-template/instrument-cluster/app.qrc b/examples/interfaceframework/qface-tutorial/ch7-own-template/instrument-cluster/app.qrc new file mode 100644 index 00000000..97948209 --- /dev/null +++ b/examples/interfaceframework/qface-tutorial/ch7-own-template/instrument-cluster/app.qrc @@ -0,0 +1,11 @@ +<RCC> + <qresource prefix="/"> + <file>Cluster.qml</file> + <file>Top.qml</file> + <file>RightDial.qml</file> + <file>LeftDial.qml</file> + <file>Dial.qml</file> + <file>Fuel.qml</file> + <file>Label.qml</file> + </qresource> +</RCC> diff --git a/examples/interfaceframework/qface-tutorial/ch7-own-template/instrument-cluster/instrument-cluster.pro b/examples/interfaceframework/qface-tutorial/ch7-own-template/instrument-cluster/instrument-cluster.pro new file mode 100644 index 00000000..40c3244b --- /dev/null +++ b/examples/interfaceframework/qface-tutorial/ch7-own-template/instrument-cluster/instrument-cluster.pro @@ -0,0 +1,16 @@ +TARGET = chapter7-own-template +DESTDIR = .. + +QT += qml quick +CONFIG -= app_bundle + +SOURCES = main.cpp +RESOURCES += app.qrc \ + ../../images/images.qrc \ + +# Additional import path used to resolve QML modules in Qt Creator's code model +QML_IMPORT_PATH = $$OUT_PWD/../frontend/qml + +CONFIG += install_ok # Do not cargo-cult this! +target.path = $$[QT_INSTALL_EXAMPLES]/interfaceframework/qface-tutorial/chapter7-own-template +INSTALLS += target diff --git a/examples/interfaceframework/qface-tutorial/ch7-own-template/instrument-cluster/main.cpp b/examples/interfaceframework/qface-tutorial/ch7-own-template/instrument-cluster/main.cpp new file mode 100644 index 00000000..0035beea --- /dev/null +++ b/examples/interfaceframework/qface-tutorial/ch7-own-template/instrument-cluster/main.cpp @@ -0,0 +1,18 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2019 Luxoft Sweden AB +// Copyright (C) 2018 Pelagicore AG +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include <QGuiApplication> +#include <QQmlApplicationEngine> + +int main(int argc, char *argv[]) +{ + QGuiApplication app(argc, argv); + + QQmlApplicationEngine engine; + engine.addImportPath(app.applicationDirPath() + "/imports"); + engine.load(QUrl(QStringLiteral("qrc:///Cluster.qml"))); + + return app.exec(); +} diff --git a/examples/interfaceframework/qface-tutorial/ch7-own-template/templates/backend_dbus.yaml b/examples/interfaceframework/qface-tutorial/ch7-own-template/templates/backend_dbus.yaml new file mode 100644 index 00000000..01c63fa7 --- /dev/null +++ b/examples/interfaceframework/qface-tutorial/ch7-own-template/templates/backend_dbus.yaml @@ -0,0 +1,12 @@ +backend_dbus: + module: + documents: + - "{{module.module_name|lower}}dbusplugin.h": "plugin.h.tpl" + - "{{module.module_name|lower}}dbusplugin.cpp": "plugin.cpp.tpl" + - "{{module.module_name|lower}}.json": "plugin.json.tpl" + - "{{srcBase|lower}}.pri": "plugin.pri.tpl" + - '{{srcBase|lower}}.cmake': 'CMakeLists.txt.tpl' + interface: + documents: + - '{{interface|lower}}dbusbackend.h': 'backend.h.tpl' + - '{{interface|lower}}dbusbackend.cpp': 'backend.cpp.tpl' diff --git a/examples/interfaceframework/qface-tutorial/ch7-own-template/templates/backend_dbus/CMakeLists.txt.tpl b/examples/interfaceframework/qface-tutorial/ch7-own-template/templates/backend_dbus/CMakeLists.txt.tpl new file mode 100644 index 00000000..a5a19dc2 --- /dev/null +++ b/examples/interfaceframework/qface-tutorial/ch7-own-template/templates/backend_dbus/CMakeLists.txt.tpl @@ -0,0 +1,40 @@ +{# +# Copyright (C) 2021 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +#} +{% include "common/generated_comment.cmake.tpl" %} + +qt6_set_ifcodegen_variable(${VAR_PREFIX}_SOURCES +{% for interface in module.interfaces %} + ${CMAKE_CURRENT_LIST_DIR}/{{interface|lower}}dbusbackend.cpp +{% endfor %} + ${CMAKE_CURRENT_LIST_DIR}/{{module.module_name|lower}}dbusplugin.cpp +) + +qt6_set_ifcodegen_variable(${VAR_PREFIX}_LIBRARIES + Qt6::DBus + Qt6::InterfaceFramework +) + +if (TARGET ${CURRENT_TARGET}) +{% for interface in module.interfaces %} + set_source_files_properties({{interface.tags.config_dbus.xml}} PROPERTIES INCLUDE dbus_conversion.h) + qt_add_dbus_interface(${VAR_PREFIX}_SOURCES + {{interface.tags.config_dbus.xml}} + {{interface|lower}}_interface + ) +{% endfor %} + + target_sources(${CURRENT_TARGET} + PRIVATE + ${${VAR_PREFIX}_SOURCES} + ) + + target_include_directories(${CURRENT_TARGET} PRIVATE + $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}> + ) + + target_link_libraries(${CURRENT_TARGET} PRIVATE + ${${VAR_PREFIX}_LIBRARIES} + ) +endif() diff --git a/examples/interfaceframework/qface-tutorial/ch7-own-template/templates/backend_dbus/backend.cpp.tpl b/examples/interfaceframework/qface-tutorial/ch7-own-template/templates/backend_dbus/backend.cpp.tpl new file mode 100644 index 00000000..4bd5a49e --- /dev/null +++ b/examples/interfaceframework/qface-tutorial/ch7-own-template/templates/backend_dbus/backend.cpp.tpl @@ -0,0 +1,85 @@ +{# +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +#} +{% include "common/generated_comment.cpp.tpl" %} +{% set class = '{0}DBusBackend'.format(interface) %} + +#include "{{class|lower}}.h" + +#include <QDBusConnection> + +{{class}}::{{class}}(QObject *parent) + : {{interface}}BackendInterface(parent) + , m_client(nullptr) +{ +{% for struct in module.structs %} + qDBusRegisterMetaType<{{struct}}>(); +{% endfor %} +{% for enum in module.enums %} + qDBusRegisterMetaType<{{module.module_name}}::{{enum|flag_type}}>(); +{% endfor %} +} + +void {{class}}::initialize() +{ + if (!m_client) + setupConnection(); + if (m_fetchList.isEmpty()) { +{% for property in interface.properties %} + emit {{property}}Changed(m_{{property}}); +{% endfor %} + emit initializationDone(); + } +} + +void {{class}}::setupConnection() +{ + qInfo() << "Connecting to the Server"; + m_client = new {{interface.tags.config_dbus.className}}("{{interface.tags.config_dbus.interfaceName}}", "/", QDBusConnection::sessionBus()); +{% for property in interface.properties %} + connect(m_client, &{{interface.tags.config_dbus.className}}::{{property}}Changed, + this, &{{class}}::on{{property|upperfirst}}Changed); +{% endfor %} +{% for property in interface.properties %} + void fetch{{property|upperfirst}}(); +{% endfor %} +} + +{% for property in interface.properties %} +void {{class}}::fetch{{property|upperfirst}}() +{ + m_fetchList.append("{{property}}"); + auto reply = m_client->asyncCall("{{property}}"); + auto watcher = new QDBusPendingCallWatcher(reply, this); + connect(watcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *watcher) { + QDBusPendingReply<{{property|return_type}}> reply = *watcher; + if (reply.isError()) { + qCritical() << reply.error(); + } else { + m_fetchList.removeAll("{{property}}"); + this->on{{property|upperfirst}}Changed(reply.value()); + watcher->deleteLater(); + this->checkInitDone(); + } + }); +} +{% endfor %} + +void {{class}}::checkInitDone() +{ + if (m_fetchList.isEmpty()) { + qInfo() << "All properties initialized"; + emit initializationDone(); + } +} + +{% for property in interface.properties %} +void {{class}}::on{{property|upperfirst}}Changed({{property|parameter_type}}) +{ + m_{{property}} = {{property}}; + emit {{property}}Changed({{property}}); +} +{% endfor %} + + diff --git a/examples/interfaceframework/qface-tutorial/ch7-own-template/templates/backend_dbus/backend.h.tpl b/examples/interfaceframework/qface-tutorial/ch7-own-template/templates/backend_dbus/backend.h.tpl new file mode 100644 index 00000000..d760327d --- /dev/null +++ b/examples/interfaceframework/qface-tutorial/ch7-own-template/templates/backend_dbus/backend.h.tpl @@ -0,0 +1,42 @@ +{# +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +#} +{% include "common/generated_comment.cpp.tpl" %} +{% set class = '{0}DBusBackend'.format(interface) %} +{% set oncedefine = '{0}_{1}_H_'.format(module.module_name|upper, class|upper) %} +#ifndef {{oncedefine}} +#define {{oncedefine}} + +#include "{{interface|lower}}backendinterface.h" +#include "{{interface|lower}}_interface.h" + +class {{class}} : public {{interface}}BackendInterface +{ + Q_OBJECT +public: + {{class}}(QObject *parent = nullptr); + +public: + void initialize() override; + + void setupConnection(); +{% for property in interface.properties %} + void fetch{{property|upperfirst}}(); +{% endfor %} + void checkInitDone(); + +public Q_SLOTS: +{% for property in interface.properties %} + void on{{property|upperfirst}}Changed({{property|parameter_type}}); +{% endfor %} + +private: + {{interface.tags.config_dbus.className}} *m_client; + QStringList m_fetchList; +{% for property in interface.properties %} + {{property|return_type}} m_{{property}}; +{% endfor %} +}; + +#endif // {{oncedefine}} diff --git a/examples/interfaceframework/qface-tutorial/ch7-own-template/templates/backend_dbus/plugin.cpp.tpl b/examples/interfaceframework/qface-tutorial/ch7-own-template/templates/backend_dbus/plugin.cpp.tpl new file mode 100644 index 00000000..6478430f --- /dev/null +++ b/examples/interfaceframework/qface-tutorial/ch7-own-template/templates/backend_dbus/plugin.cpp.tpl @@ -0,0 +1,36 @@ +{# +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +#} +{% include "common/generated_comment.cpp.tpl" %} +{% set class = '{0}DBusPlugin'.format(module.module_name) %} + +#include "{{class|lower}}.h" + +{% for interface in module.interfaces %} +#include "{{interface|lower}}dbusbackend.h" +{% endfor %} + +{{class}}::{{class}}(QObject *parent) + : QObject(parent) +{ +{% for interface in module.interfaces %} + m_interfaces << new {{interface}}DBusBackend(this); +{% endfor %} +} + +QStringList {{class}}::interfaces() const +{ + QStringList list; +{% for iface in module.interfaces %} +{% if loop.first %} list{% endif %} << {{module.module_name|upperfirst}}_{{iface}}_iid{% if loop.last %};{% endif %} +{% endfor %} + + return list; +} + +QIfFeatureInterface *{{class}}::interfaceInstance(const QString &interface) const +{ + int index = interfaces().indexOf(interface); + return index < 0 ? nullptr : m_interfaces.at(index); +} diff --git a/examples/interfaceframework/qface-tutorial/ch7-own-template/templates/backend_dbus/plugin.h.tpl b/examples/interfaceframework/qface-tutorial/ch7-own-template/templates/backend_dbus/plugin.h.tpl new file mode 100644 index 00000000..27f4e75b --- /dev/null +++ b/examples/interfaceframework/qface-tutorial/ch7-own-template/templates/backend_dbus/plugin.h.tpl @@ -0,0 +1,33 @@ +{# +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +#} +{% include "common/generated_comment.cpp.tpl" %} +{% set class = '{0}DBusPlugin'.format(module.module_name) %} +{% set oncedefine = '{0}_H_'.format(class|upper) %} + +#ifndef {{oncedefine}} +#define {{oncedefine}} + +#include <QObject> +#include <QVector> +#include <QIfServiceInterface> + +class {{class}} : public QObject, public QIfServiceInterface +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QIfServiceInterface_iid FILE "{{module.module_name|lower}}.json") + Q_INTERFACES(QIfServiceInterface) + +public: + explicit {{class}}(QObject *parent = nullptr); + + QStringList interfaces() const override; + QIfFeatureInterface* interfaceInstance(const QString &interface) const override; + +private: + QVector<QIfFeatureInterface *> m_interfaces; +}; + +#endif // {{oncedefine}} + diff --git a/examples/interfaceframework/qface-tutorial/ch7-own-template/templates/backend_dbus/plugin.json.tpl b/examples/interfaceframework/qface-tutorial/ch7-own-template/templates/backend_dbus/plugin.json.tpl new file mode 100644 index 00000000..a742ef34 --- /dev/null +++ b/examples/interfaceframework/qface-tutorial/ch7-own-template/templates/backend_dbus/plugin.json.tpl @@ -0,0 +1,11 @@ +{# +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +#} +{ + "interfaces" : [ +{% for interface in module.interfaces %} + "{{interface.qualified_name}}"{% if not loop.last %},{%endif%} +{% endfor%} + ] +} diff --git a/examples/interfaceframework/qface-tutorial/ch7-own-template/templates/backend_dbus/plugin.pri.tpl b/examples/interfaceframework/qface-tutorial/ch7-own-template/templates/backend_dbus/plugin.pri.tpl new file mode 100644 index 00000000..1d4b5307 --- /dev/null +++ b/examples/interfaceframework/qface-tutorial/ch7-own-template/templates/backend_dbus/plugin.pri.tpl @@ -0,0 +1,24 @@ +{# +# Copyright (C) 2021 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +#} +{% include "common/generated_comment.qmake.tpl" %} + +HEADERS += \ +{% for interface in module.interfaces %} + $$PWD/{{interface|lower}}dbusbackend.h \ +{% endfor %} + $$PWD/{{module.module_name|lower}}dbusplugin.h + +SOURCES += \ +{% for interface in module.interfaces %} + $$PWD/{{interface|lower}}dbusbackend.cpp \ +{% endfor %} + $$PWD/{{module.module_name|lower}}dbusplugin.cpp + +{% for interface in module.interfaces %} +{{interface}}.files = {{interface.tags.config_dbus.xml}} +{{interface}}.header_flags += -i dbus_conversion.h + +DBUS_INTERFACES += {{interface}} +{% endfor %} diff --git a/examples/interfaceframework/qface-tutorial/doc/src/qface-tutorial.qdoc b/examples/interfaceframework/qface-tutorial/doc/src/qface-tutorial.qdoc index 7f8549ec..d0bda255 100644 --- a/examples/interfaceframework/qface-tutorial/doc/src/qface-tutorial.qdoc +++ b/examples/interfaceframework/qface-tutorial/doc/src/qface-tutorial.qdoc @@ -889,4 +889,262 @@ \image qface-tutorial-dbus.gif + \target chapter7 + \section1 Chapter 7: Write a Template for a Back End with D-Bus + + Now as we have our own production backend ready, it's a good time to move it into an ifcodegen + template. For a simple example like this, keeping the handwritten backend would be totally fine, + but once you have defined multiple Modules and Interfaces it is a lot of boiler-plate code to + maintain. + + \section2 Create a template from scratch + + The best way to start a new template, is to copy the existing code to a new folder for our + template. First we create a \c templates folder and within that a folder called \c backend_dbus. + This follows the template naming convention from ifcodegen, but you can also use any other + name as well. + + The next step is to rename the files in our template folder, to make them more generic and + identify them as templates by adding an additional \c tpl suffix. + + The template folder should now look like this: + + \list + \li backend.cpp.tpl + \li backend.h.tpl + \li plugin.cpp.tpl + \li plugin.h.tpl + \li plugin.json.tpl + \endlist + + \section2 Convert all files to templates + + Let's go over all the files in the template and use the special \l {Jinja Template Syntax} to + extend them for our needs. + + \section3 plugin.h.tpl + + The first thing we usually want to do is to add a special comment for the autogenerated file. + This will mark it as autogenerated and that it shouldn't be edited manually. + + The following line includes a predefined comment file (part of ifcodegen): + + \quotefromfile interfaceframework/qface-tutorial/ch7-own-template/templates/backend_dbus/plugin.h.tpl + \skipto {% include + \printuntil {% include + + \note The \c.cpp suffix of the included file indicates that the comments are intended to be used + in C++ files. + + Next we want to replace the hardcoded \c #ifdef names with something derived from the module name. + This is done by defining some helper variables first. + The first variable is named \c class and uses a format string which consists of the name of the + module and the static text \c DBusPlugin. + + Given that our module is named \c Example.If.InstrumentClusterModule the resulting string will be + \c InstrumentClusterModuleDBusPlugin. + + The second variable also uses a format string but uses the previously defined class variable + followed by a pipe symbol. The pipe indicates that the variable should be given to a function that + proccess the variable as input to create some new output. This is called a \l {Filter + Reference}{filter} in Jinja. The filter is called \c upper and will make the given module name + all upper-case. + + The next step is to use the new defined variables to replace the hardcoded defines. In order + to use a variable and print its content to the autogenerated code, double curly braces have to + be used. All text that does not use the Jinja syntax is printed as is. With that in mind we + can keep the include statements as they are and the template file should look like this: + + \quotefromfile interfaceframework/qface-tutorial/ch7-own-template/templates/backend_dbus/plugin.h.tpl + \skipto #ifndef + \printuntil #define + \dots + \skipto #endif + \printto + + In the same way we continue to replace all occurrences of the hardcoded class name with \c {{class}} + and replace the hardcoded plugin metadata json with \c {{module.module_name|lower}}.json. + + The class declaration will now look like this: + + \quotefromfile interfaceframework/qface-tutorial/ch7-own-template/templates/backend_dbus/plugin.h.tpl + \skipto class {{class}} + \printuntil }; + + You might have noticed that instead of using a hardcoded pointer to the \c InstrumentClusterBackend + we now use \c QVector<QIfFeatureInterface *>. This is needed as a module can have multiple + interfaces and the plugin needs to store instances for all of them. This can be easily achieved + by using a vector instead of hardcoded values. + + \section3 plugin.cpp.tpl + + In a similar fashion the \c plugin.cpp.tpl can be extended with \l {Jinja Template Syntax} + {Jinja syntax}. It also starts by including the generated comment and by defining the \c class + variable. In addition to including the autogenerated plugin header, we also need to include the + header for all backend classes. As mentioned before, a module can have multiple interfaces. To + generate an include statement for every interface within a module, a Jinja for loop is used: + + \quotefromfile interfaceframework/qface-tutorial/ch7-own-template/templates/backend_dbus/plugin.cpp.tpl + \skipto {% for + \printuntil {% endfor + + Afterwards we continue to define the constructor for our plugin and also use a for loop to fill + the \c m_instances vector with backend class instances. + + In addition, for the plugin to work correctly the \c interfaces() and \c interfaceInstance() + methods need to be implemented. The \c interfaces() method needs to return a list of interfaces + it provides. For this also a Jinja for loop is used. Inside the for loop, Jinja if statements + are used to decide whether some extra content needs to be printed at the beginning and the end + of the loop. + + The full plugin definition now looks like this: + + \quotefromfile interfaceframework/qface-tutorial/ch7-own-template/templates/backend_dbus/plugin.cpp.tpl + \skipto {{class}} + \printuntil + + \section3 backend.h.tpl and backend.cpp.tpl + + The backend class files follow the same schema as the plugin. All fetch methods are generated + using a loop like this: + + \quotefromfile interfaceframework/qface-tutorial/ch7-own-template/templates/backend_dbus/backend.h.tpl + \skipto {% for + \printuntil {% endfor + + In a similar fashion the slots for changed values are generated. But as those take the property + as an argument we need to generate this part as well. + This is done by using a filter called \l parameter_type, which takes care of that. + + \quotefromfile interfaceframework/qface-tutorial/ch7-own-template/templates/backend_dbus/backend.h.tpl + \skipuntil public Q_SLOTS: + \printuntil {% endfor + + Lastly, a for loop also needs to be used to generate all the member variables. To convert the + QFace IDL type of the property to the correct C++ type, we use the \l return_type filter. + + In the source file, we register all enums and structs using \l qDBusRegisterMetaType and + replace the hardcoded \c propertyChanged method calls in the \c initialize() function with a Jinja + for loop. + + \quotefromfile interfaceframework/qface-tutorial/ch7-own-template/templates/backend_dbus/backend.cpp.tpl + \skipto {{class}} + \printto void {{class}}::setupConnection() + + The rest of the code is ported accordingly and looks like this: + + \quotefromfile interfaceframework/qface-tutorial/ch7-own-template/templates/backend_dbus/backend.cpp.tpl + \skipto void {{class}}::setupConnection() + \printto + + \section3 plugin.json + + For the plugin to be loaded correctly we also need to generate the \c plugin.json file, which is + done like this: + + \quotefromfile interfaceframework/qface-tutorial/ch7-own-template/templates/backend_dbus/plugin.json.tpl + \skipuntil #} + \printto + + \section2 Add new Annotations + + If you did the excercise to port all the files by yourself, you might have noticed that not + everything in the backend code can be derived from the QFace module or interface names. + + As we still want to use the DBus interface generated by \l qdbusxml2cpp, the class name and dbus + interface name are given by the \c instrumentcluster.xml file and once we add an additional + interface to the IDL file we also need an DBus XML for that. + + That means, we need additional information for every interface in our IDL file. This can be + achieved by adding a new annotation to the interface: + + \quotefromfile interfaceframework/qface-tutorial/ch7-own-template/instrument-cluster.qface + \skipto @config_dbus + \printuntil interface InstrumentCluster + \dots + + Now as the information is part of the IDL file, we can also access it in the template like this: + + \quotefromfile interfaceframework/qface-tutorial/ch7-own-template/templates/backend_dbus/backend.cpp.tpl + \skipto m_client = + \printuntil m_client = + + \section2 Finalize the template + + To finalize the template we need to create some more files. + + \section3 Build-system template files + + Right now ifcodegen supports both QMake and CMake as build systems. For each one we need to + provide additional files to let the build system know how to generate and compile our code. + + For QMake we add a \c plugin.pri.tpl: + + \quotefromfile interfaceframework/qface-tutorial/ch7-own-template/templates/backend_dbus/plugin.pri.tpl + \skipuntil #} + \printto + + There we add all the generated C++ files to the \c HEADERS and \c SOURCES variables + accordingly. Afterwards we add some extra code to generate the DBus interface for every + interface in our module. + + You might wonder, why the actual file names differ from the template names? We will explain + that after we had a look at the CMake integration: + + \quotefromfile interfaceframework/qface-tutorial/ch7-own-template/templates/backend_dbus/CMakeLists.txt.tpl + \skipuntil #} + \printto + + For CMake it works a bit different, as ifcodegen provides two different mechanisms to + integrate. First all the needed information is saved as ifcodegen variables using a call to + \l qt6_set_ifcodegen_variable. All variables have to be prefixed with \c {${VAR_PREFIX}_}. The + \c VAR_PREFIX variable is set by CMake when importing the file and is used to import the + variables using \l qt_ifcodegen_import_variables. + + The second way to integrate with CMake is \l qt_ifcodegen_extend_target. In that case the + \c ${CURRENT_TARGET} variable is set and the previous defined variables are used to call the + needed cmake functions, e.g: + + \quotefromfile interfaceframework/qface-tutorial/ch7-own-template/templates/backend_dbus/CMakeLists.txt.tpl + \skipto target_sources + \printuntil ) + + \section3 Create a Generation YAML file + + One important piece of information is still missing in order for ifcodegen to generate + something useful. It doesn't know how the generated files should be named or whether a file + should only be generated once or per module. + + All this is defined within the \l {Generation YAML} file, which is named after the template and + is located within the same directory: + + \quotefromfile interfaceframework/qface-tutorial/ch7-own-template/templates/backend_dbus.yaml + \printto + + First the YAML defines that for every module in the IDL file some files should be generated. + Every document consists of the output file name (using \l {Jinja Template Syntax} {Jinja + syntax}), followed by the input template file name. + + The same is done for all files which should be generated for every interface in the IDL file: + + \quotefromfile interfaceframework/qface-tutorial/ch7-own-template/templates/backend_dbus.yaml + \skipto interface: + \printto + + \section2 Use the new template in the build system + + In order to use the new template, you need to integrate it into the build system: + + \e CMake: + \quotefromfile interfaceframework/qface-tutorial/ch7-own-template/backend_dbus/CMakeLists.txt + \skipto qt_ifcodegen_extend_target + \printuntil ) + + \e QMake: + \quotefromfile interfaceframework/qface-tutorial/ch7-own-template/backend_dbus/backend_dbus.pro + \skipto IFCODEGEN_TEMPLATE + \printuntil IFCODEGEN_SOURCES + + Instead of just using the template name, you need to provide the full path to the template, or + add the template folder to the \l {cmake-variable-QT_IFCODEGEN_TEMPLATE_SEARCH_PATH}{template search path}. */ diff --git a/examples/interfaceframework/qface-tutorial/qface-tutorial.pro b/examples/interfaceframework/qface-tutorial/qface-tutorial.pro index fb64b783..2f6f3ef2 100644 --- a/examples/interfaceframework/qface-tutorial/qface-tutorial.pro +++ b/examples/interfaceframework/qface-tutorial/qface-tutorial.pro @@ -7,4 +7,7 @@ SUBDIRS += \ chapter4-simulation-behavior \ chapter5-ipc \ -!win32:qtHaveModule(dbus): SUBDIRS += chapter6-own-backend +!win32:qtHaveModule(dbus): { + SUBDIRS += chapter6-own-backend \ + ch7-own-template +} diff --git a/src/interfaceframework/doc/src/ifcodegen/filter-reference.qdoc b/src/interfaceframework/doc/src/ifcodegen/filter-reference.qdoc index 5697d4a0..57d49154 100644 --- a/src/interfaceframework/doc/src/ifcodegen/filter-reference.qdoc +++ b/src/interfaceframework/doc/src/ifcodegen/filter-reference.qdoc @@ -24,6 +24,7 @@ Returns the C++ type for the passed symbol. E.g. QString for a symbol of type "string". \target parameter_type(symbol) +\target parameter_type \section2 parameter_type(symbol) Returns the C++ type for the passed symbol. This returns the same type as return_type(), but already |