From d9d2667e365f7e4220afd8098a694800666329af Mon Sep 17 00:00:00 2001 From: Scott Murray Date: Tue, 4 Dec 2018 11:52:01 -0500 Subject: Initial check-in Demo instrument cluster dashboard application to display instrument cluster mock-up with speedometer, tachometer, temperature, and fuel gauges. A space is provided in the center of the dashboard for the overlay of the receiver application's remote display. Note that the gauges themselves and their animation are based on the Qt dashboard example: https://doc.qt.io/qt-5.11/qtquickextras-dashboard-example.html The associated qml source files are under the BSD license, other files are Apache 2.0. Additionally, note that the application uses the windowmanager, but not the homescreen service, as the demo instrument cluster does not use the homescreen. There is also an implicit assumption that the windowmanager configuration allows the application to claim the homescreen role and that the surface for that role will be 1920x1080 portrait mode. Change-Id: I413fa165125813757ab4712993320440ed641f32 Signed-off-by: Scott Murray --- app/CMakeLists.txt | 50 +++++ app/DashboardGaugeStyle.qml | 175 ++++++++++++++++ app/IconGaugeStyle.qml | 135 +++++++++++++ app/TachometerStyle.qml | 127 ++++++++++++ app/TurnIndicator.qml | 111 +++++++++++ app/ValueSource.qml | 385 ++++++++++++++++++++++++++++++++++++ app/cluster-gauges.qml | 364 ++++++++++++++++++++++++++++++++++ app/cluster-gauges.qrc | 10 + app/images/Utility_Logo_Grey-01.svg | 84 ++++++++ app/images/agl_title_793x211.png | Bin 0 -> 55357 bytes app/images/fuel-icon.png | Bin 0 -> 409 bytes app/images/images.qrc | 8 + app/images/temperature-icon.png | Bin 0 -> 3302 bytes app/main.cpp | 91 +++++++++ 14 files changed, 1540 insertions(+) create mode 100644 app/CMakeLists.txt create mode 100644 app/DashboardGaugeStyle.qml create mode 100644 app/IconGaugeStyle.qml create mode 100644 app/TachometerStyle.qml create mode 100644 app/TurnIndicator.qml create mode 100644 app/ValueSource.qml create mode 100644 app/cluster-gauges.qml create mode 100644 app/cluster-gauges.qrc create mode 100644 app/images/Utility_Logo_Grey-01.svg create mode 100644 app/images/agl_title_793x211.png create mode 100644 app/images/fuel-icon.png create mode 100644 app/images/images.qrc create mode 100644 app/images/temperature-icon.png create mode 100644 app/main.cpp (limited to 'app') diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt new file mode 100644 index 0000000..07c60c4 --- /dev/null +++ b/app/CMakeLists.txt @@ -0,0 +1,50 @@ +########################################################################### +# Copyright 2018 Konsulko Group +# +# Author: Scott Murray +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +########################################################################### + +set(CMAKE_INCLUDE_CURRENT_DIR ON) +set(CMAKE_AUTOMOC ON) +set(CMAKE_CXX_STANDARD 14) + +find_package(Qt5 COMPONENTS Core Gui QuickControls2 WebSockets QuickWidgets REQUIRED) +find_package(PkgConfig REQUIRED) + +qt5_add_resources(RESOURCES cluster-gauges.qrc images/images.qrc) + +PROJECT_TARGET_ADD(cluster-gauges) + +add_executable(${TARGET_NAME} + main.cpp + ${RESOURCES} +) + +pkg_check_modules(QLIBWINMGR REQUIRED qlibwindowmanager) + +set_target_properties(${TARGET_NAME} PROPERTIES + LABELS "EXECUTABLE" + PREFIX "" + COMPILE_FLAGS "${QLIBWINMGR_FLAGS} ${EXTRAS_CFLAGS} -DFOR_AFB_BINDING" + LINK_FLAGS "${BINDINGS_LINK_FLAG}" + LINK_LIBRARIES "${EXTRAS_LIBRARIES}" + OUTPUT_NAME "${TARGET_NAME}" +) + +target_link_libraries(${TARGET_NAME} + Qt5::QuickControls2 + Qt5::QuickWidgets + ${QLIBWINMGR_LIBRARIES} +) diff --git a/app/DashboardGaugeStyle.qml b/app/DashboardGaugeStyle.qml new file mode 100644 index 0000000..d765a70 --- /dev/null +++ b/app/DashboardGaugeStyle.qml @@ -0,0 +1,175 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples 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$ +** +****************************************************************************/ + + +import QtQuick 2.2 +import QtQuick.Controls.Styles 1.4 + +CircularGaugeStyle { + tickmarkInset: toPixels(0.04) + minorTickmarkInset: tickmarkInset + labelStepSize: 20 + labelInset: toPixels(0.23) + + property real xCenter: outerRadius + property real yCenter: outerRadius + property real needleLength: outerRadius - tickmarkInset * 1.25 + property real needleTipWidth: toPixels(0.02) + property real needleBaseWidth: toPixels(0.06) + property bool halfGauge: false + + function toPixels(percentage) { + return percentage * outerRadius; + } + + function degToRad(degrees) { + return degrees * (Math.PI / 180); + } + + function radToDeg(radians) { + return radians * (180 / Math.PI); + } + + function paintBackground(ctx) { + if (halfGauge) { + ctx.beginPath(); + ctx.rect(0, 0, ctx.canvas.width, ctx.canvas.height / 2); + ctx.clip(); + } + + ctx.beginPath(); + ctx.fillStyle = "black"; + ctx.ellipse(0, 0, ctx.canvas.width, ctx.canvas.height); + ctx.fill(); + + ctx.beginPath(); + ctx.lineWidth = tickmarkInset; + ctx.strokeStyle = "black"; + ctx.arc(xCenter, yCenter, outerRadius - ctx.lineWidth / 2, outerRadius - ctx.lineWidth / 2, 0, Math.PI * 2); + ctx.stroke(); + + ctx.beginPath(); + ctx.lineWidth = tickmarkInset / 2; + ctx.strokeStyle = "#222"; + ctx.arc(xCenter, yCenter, outerRadius - ctx.lineWidth / 2, outerRadius - ctx.lineWidth / 2, 0, Math.PI * 2); + ctx.stroke(); + + ctx.beginPath(); + var gradient = ctx.createRadialGradient(xCenter, yCenter, 0, xCenter, yCenter, outerRadius * 1.5); + gradient.addColorStop(0, Qt.rgba(1, 1, 1, 0)); + gradient.addColorStop(0.7, Qt.rgba(1, 1, 1, 0.13)); + gradient.addColorStop(1, Qt.rgba(1, 1, 1, 1)); + ctx.fillStyle = gradient; + ctx.arc(xCenter, yCenter, outerRadius - tickmarkInset, outerRadius - tickmarkInset, 0, Math.PI * 2); + ctx.fill(); + } + + background: Canvas { + onPaint: { + var ctx = getContext("2d"); + ctx.reset(); + paintBackground(ctx); + } + + Text { + id: speedText + font.pixelSize: toPixels(0.3) + text: kphInt + color: "white" + horizontalAlignment: Text.AlignRight + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.verticalCenter + anchors.topMargin: toPixels(0.1) + + readonly property int kphInt: control.value + } + Text { + text: "km/h" + color: "white" + font.pixelSize: toPixels(0.09) + anchors.top: speedText.bottom + anchors.horizontalCenter: parent.horizontalCenter + } + } + + needle: Canvas { + implicitWidth: needleBaseWidth + implicitHeight: needleLength + + property real xCenter: width / 2 + property real yCenter: height / 2 + + onPaint: { + var ctx = getContext("2d"); + ctx.reset(); + + ctx.beginPath(); + ctx.moveTo(xCenter, height); + ctx.lineTo(xCenter - needleBaseWidth / 2, height - needleBaseWidth / 2); + ctx.lineTo(xCenter - needleTipWidth / 2, 0); + ctx.lineTo(xCenter, yCenter - needleLength); + ctx.lineTo(xCenter, 0); + ctx.closePath(); + ctx.fillStyle = Qt.rgba(0.66, 0, 0, 0.66); + ctx.fill(); + + ctx.beginPath(); + ctx.moveTo(xCenter, height) + ctx.lineTo(width, height - needleBaseWidth / 2); + ctx.lineTo(xCenter + needleTipWidth / 2, 0); + ctx.lineTo(xCenter, 0); + ctx.closePath(); + ctx.fillStyle = Qt.lighter(Qt.rgba(0.66, 0, 0, 0.66)); + ctx.fill(); + } + } + + foreground: null +} diff --git a/app/IconGaugeStyle.qml b/app/IconGaugeStyle.qml new file mode 100644 index 0000000..6247076 --- /dev/null +++ b/app/IconGaugeStyle.qml @@ -0,0 +1,135 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples 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$ +** +****************************************************************************/ + + +import QtQuick 2.2 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Extras 1.4 + +DashboardGaugeStyle { + id: fuelGaugeStyle + minimumValueAngle: -60 + maximumValueAngle: 60 + tickmarkStepSize: 1 + labelStepSize: 1 + labelInset: toPixels(-0.25) + minorTickmarkCount: 3 + + needleLength: toPixels(0.85) + needleBaseWidth: toPixels(0.08) + needleTipWidth: toPixels(0.03) + + halfGauge: true + + property string icon: "" + property color minWarningColor: "transparent" + property color maxWarningColor: "transparent" + readonly property real minWarningStartAngle: minimumValueAngle - 90 + readonly property real maxWarningStartAngle: maximumValueAngle - 90 + + tickmark: Rectangle { + implicitWidth: toPixels(0.06) + antialiasing: true + implicitHeight: toPixels(0.2) + color: "#c8c8c8" + } + + minorTickmark: Rectangle { + implicitWidth: toPixels(0.03) + antialiasing: true + implicitHeight: toPixels(0.15) + color: "#c8c8c8" + } + + background: Item { + Canvas { + anchors.fill: parent + onPaint: { + var ctx = getContext("2d"); + ctx.reset(); + + paintBackground(ctx); + + if (minWarningColor != "transparent") { + ctx.beginPath(); + ctx.lineWidth = fuelGaugeStyle.toPixels(0.08); + ctx.strokeStyle = minWarningColor; + ctx.arc(outerRadius, outerRadius, + // Start the line in from the decorations, and account for the width of the line itself. + outerRadius - tickmarkInset - ctx.lineWidth / 2, + degToRad(minWarningStartAngle), + degToRad(minWarningStartAngle + angleRange / (minorTickmarkCount + 1)), false); + ctx.stroke(); + } + if (maxWarningColor != "transparent") { + ctx.beginPath(); + ctx.lineWidth = fuelGaugeStyle.toPixels(0.08); + ctx.strokeStyle = maxWarningColor; + ctx.arc(outerRadius, outerRadius, + // Start the line in from the decorations, and account for the width of the line itself. + outerRadius - tickmarkInset - ctx.lineWidth / 2, + degToRad(maxWarningStartAngle - angleRange / (minorTickmarkCount + 1)), + degToRad(maxWarningStartAngle), false); + ctx.stroke(); + } + } + } + + Image { + source: icon + anchors.bottom: parent.verticalCenter + anchors.bottomMargin: toPixels(0.3) + anchors.horizontalCenter: parent.horizontalCenter + width: toPixels(0.3) + height: width + fillMode: Image.PreserveAspectFit + } + } +} diff --git a/app/TachometerStyle.qml b/app/TachometerStyle.qml new file mode 100644 index 0000000..a632eab --- /dev/null +++ b/app/TachometerStyle.qml @@ -0,0 +1,127 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples 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$ +** +****************************************************************************/ + + +import QtQuick 2.2 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Extras 1.4 + +DashboardGaugeStyle { + id: tachometerStyle + tickmarkStepSize: 1 + labelStepSize: 1 + needleLength: toPixels(0.85) + needleBaseWidth: toPixels(0.08) + needleTipWidth: toPixels(0.03) + + tickmark: Rectangle { + implicitWidth: toPixels(0.03) + antialiasing: true + implicitHeight: toPixels(0.08) + color: styleData.index === 7 || styleData.index === 8 ? Qt.rgba(0.5, 0, 0, 1) : "#c8c8c8" + } + + minorTickmark: null + + tickmarkLabel: Text { + font.pixelSize: Math.max(6, toPixels(0.12)) + text: styleData.value + color: styleData.index === 7 || styleData.index === 8 ? Qt.rgba(0.5, 0, 0, 1) : "#c8c8c8" + antialiasing: true + } + + background: Canvas { + onPaint: { + var ctx = getContext("2d"); + ctx.reset(); + paintBackground(ctx); + + ctx.beginPath(); + ctx.lineWidth = tachometerStyle.toPixels(0.08); + ctx.strokeStyle = Qt.rgba(0.5, 0, 0, 1); + var warningCircumference = maximumValueAngle - minimumValueAngle * 0.1; + var startAngle = maximumValueAngle - 90; + ctx.arc(outerRadius, outerRadius, + // Start the line in from the decorations, and account for the width of the line itself. + outerRadius - tickmarkInset - ctx.lineWidth / 2, + degToRad(startAngle - angleRange / 8 + angleRange * 0.015), + degToRad(startAngle - angleRange * 0.015), false); + ctx.stroke(); + } + + Text { + id: rpmText + font.pixelSize: tachometerStyle.toPixels(0.3) + text: rpmInt + color: "white" + horizontalAlignment: Text.AlignRight + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.verticalCenter + anchors.topMargin: 20 + + readonly property int rpmInt: valueSource.rpm + } + Text { + text: "x1000" + color: "white" + font.pixelSize: tachometerStyle.toPixels(0.1) + anchors.top: parent.top + anchors.topMargin: parent.height / 4 + anchors.horizontalCenter: parent.horizontalCenter + } + Text { + text: "RPM" + color: "white" + font.pixelSize: tachometerStyle.toPixels(0.1) + anchors.top: rpmText.bottom + anchors.horizontalCenter: parent.horizontalCenter + } + } +} diff --git a/app/TurnIndicator.qml b/app/TurnIndicator.qml new file mode 100644 index 0000000..3bc4c0e --- /dev/null +++ b/app/TurnIndicator.qml @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples 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$ +** +****************************************************************************/ + + +import QtQuick 2.2 + +Item { + // This enum is actually keyboard-related, but it serves its purpose + // as an indication of direction for us. + property int direction: Qt.LeftArrow + property bool on: false + + property bool flashing: false + + scale: direction === Qt.LeftArrow ? 1 : -1 + Timer { + id: flashTimer + interval: 500 + running: on + repeat: true + onTriggered: flashing = !flashing + } + function paintOutlinePath(ctx) { + ctx.beginPath(); + ctx.moveTo(0, height * 0.5); + ctx.lineTo(0.6 * width, 0); + ctx.lineTo(0.6 * width, height * 0.28); + ctx.lineTo(width, height * 0.28); + ctx.lineTo(width, height * 0.72); + ctx.lineTo(0.6 * width, height * 0.72); + ctx.lineTo(0.6 * width, height); + ctx.lineTo(0, height * 0.5); + } + Canvas { + id: backgroundCanvas + anchors.fill: parent + + onPaint: { + var ctx = getContext("2d"); + ctx.reset(); + + paintOutlinePath(ctx); + + ctx.lineWidth = 1; + ctx.strokeStyle = "lightgrey"; + ctx.stroke(); + } + } + Canvas { + id: foregroundCanvas + anchors.fill: parent + visible: on && flashing + + onPaint: { + var ctx = getContext("2d"); + ctx.reset(); + + paintOutlinePath(ctx); + + ctx.fillStyle = "green"; + ctx.fill(); + } + } +} diff --git a/app/ValueSource.qml b/app/ValueSource.qml new file mode 100644 index 0000000..41d0f96 --- /dev/null +++ b/app/ValueSource.qml @@ -0,0 +1,385 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2018 Konsulko Group +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples 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$ +** +****************************************************************************/ + +import QtQuick 2.2 +Item { + id: valueSource + property real kph: 0 + property real rpm: 1 + property real fuel: 0.85 + property string gear: { + var g; + if (kph < 30) { + return "1"; + } + if (kph < 50) { + return "2"; + } + if (kph < 80) { + return "3"; + } + if (kph < 120) { + return "4"; + } + if (kph < 160) { + return "5"; + } + } + property string prindle: { + var g; + if (kph > 0) { + return "D"; + } + return "P"; + } + property int turnSignal: prindle == "P" && !start ? randomDirection() : -1 + property real temperature: 0.6 + property bool start: true + + function randomDirection() { + return Math.random() > 0.5 ? Qt.LeftArrow : Qt.RightArrow; + } + + SequentialAnimation { + running: true + loops: 1 + + // We want a small pause at the beginning, but we only want it to happen once. + PauseAnimation { + duration: 1000 + } + + PropertyAction { + target: valueSource + property: "start" + value: false + } + + SequentialAnimation { + loops: Animation.Infinite + ParallelAnimation { + NumberAnimation { + target: valueSource + property: "kph" + easing.type: Easing.InOutSine + from: 0 + to: 30 + duration: 3000 + } + NumberAnimation { + target: valueSource + property: "rpm" + easing.type: Easing.InOutSine + from: 1 + to: 6.1 + duration: 3000 + } + } + ParallelAnimation { + // We changed gears so we lost a bit of speed. + NumberAnimation { + target: valueSource + property: "kph" + easing.type: Easing.InOutSine + from: 30 + to: 26 + duration: 600 + } + NumberAnimation { + target: valueSource + property: "rpm" + easing.type: Easing.InOutSine + from: 6 + to: 2.4 + duration: 600 + } + } + ParallelAnimation { + NumberAnimation { + target: valueSource + property: "kph" + easing.type: Easing.InOutSine + to: 60 + duration: 3000 + } + NumberAnimation { + target: valueSource + property: "rpm" + easing.type: Easing.InOutSine + to: 5.6 + duration: 3000 + } + } + ParallelAnimation { + // We changed gears so we lost a bit of speed. + NumberAnimation { + target: valueSource + property: "kph" + easing.type: Easing.InOutSine + to: 56 + duration: 600 + } + NumberAnimation { + target: valueSource + property: "rpm" + easing.type: Easing.InOutSine + to: 2.3 + duration: 600 + } + } + ParallelAnimation { + NumberAnimation { + target: valueSource + property: "kph" + easing.type: Easing.InOutSine + to: 100 + duration: 3000 + } + NumberAnimation { + target: valueSource + property: "rpm" + easing.type: Easing.InOutSine + to: 5.1 + duration: 3000 + } + } + ParallelAnimation { + // We changed gears so we lost a bit of speed. + NumberAnimation { + target: valueSource + property: "kph" + easing.type: Easing.InOutSine + to: 96 + duration: 600 + } + NumberAnimation { + target: valueSource + property: "rpm" + easing.type: Easing.InOutSine + to: 2.2 + duration: 600 + } + } + + ParallelAnimation { + NumberAnimation { + target: valueSource + property: "kph" + easing.type: Easing.InOutSine + to: 140 + duration: 3000 + } + NumberAnimation { + target: valueSource + property: "rpm" + easing.type: Easing.InOutSine + to: 6.2 + duration: 3000 + } + } + + // Slow down a bit + ParallelAnimation { + NumberAnimation { + target: valueSource + property: "kph" + easing.type: Easing.InOutSine + to: 115 + duration: 6000 + } + NumberAnimation { + target: valueSource + property: "rpm" + easing.type: Easing.InOutSine + to: 5.5 + duration: 6000 + } + } + + // Cruise for a while + ParallelAnimation { + NumberAnimation { + target: valueSource + property: "kph" + easing.type: Easing.InOutSine + to: 110 + duration: 10000 + } + NumberAnimation { + target: valueSource + property: "rpm" + easing.type: Easing.InOutSine + to: 5.2 + duration: 10000 + } + } + ParallelAnimation { + NumberAnimation { + target: valueSource + property: "kph" + easing.type: Easing.InOutSine + to: 115 + duration: 10000 + } + NumberAnimation { + target: valueSource + property: "rpm" + easing.type: Easing.InOutSine + to: 5.5 + duration: 10000 + } + } + + // Start downshifting. + + // Fifth to fourth gear. + ParallelAnimation { + NumberAnimation { + target: valueSource + property: "kph" + easing.type: Easing.Linear + to: 100 + duration: 5000 + } + + NumberAnimation { + target: valueSource + property: "rpm" + easing.type: Easing.InOutSine + to: 3.1 + duration: 5000 + } + } + + // Fourth to third gear. + NumberAnimation { + target: valueSource + property: "rpm" + easing.type: Easing.InOutSine + to: 5.5 + duration: 600 + } + + ParallelAnimation { + NumberAnimation { + target: valueSource + property: "kph" + easing.type: Easing.InOutSine + to: 60 + duration: 5000 + } + NumberAnimation { + target: valueSource + property: "rpm" + easing.type: Easing.InOutSine + to: 2.6 + duration: 5000 + } + } + + // Third to second gear. + NumberAnimation { + target: valueSource + property: "rpm" + easing.type: Easing.InOutSine + to: 6.3 + duration: 600 + } + + ParallelAnimation { + NumberAnimation { + target: valueSource + property: "kph" + easing.type: Easing.InOutSine + to: 30 + duration: 5000 + } + NumberAnimation { + target: valueSource + property: "rpm" + easing.type: Easing.InOutSine + to: 2.6 + duration: 5000 + } + } + + NumberAnimation { + target: valueSource + property: "rpm" + easing.type: Easing.InOutSine + to: 6.5 + duration: 600 + } + + // Second to first gear. + ParallelAnimation { + NumberAnimation { + target: valueSource + property: "kph" + easing.type: Easing.InOutSine + to: 0 + duration: 5000 + } + NumberAnimation { + target: valueSource + property: "rpm" + easing.type: Easing.InOutSine + to: 1 + duration: 4500 + } + } + + PauseAnimation { + duration: 5000 + } + } + } +} diff --git a/app/cluster-gauges.qml b/app/cluster-gauges.qml new file mode 100644 index 0000000..013a0c1 --- /dev/null +++ b/app/cluster-gauges.qml @@ -0,0 +1,364 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2018 Konsulko Group +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples 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$ +** +****************************************************************************/ + +/* + * NOTE: Originally written from scratch, but enough code was eventually + * pasted in from the Qt dashboard.qml example that its license text + * has been adopted. + */ + +import QtQuick 2.2 +import QtQuick.Window 2.1 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Extras 1.4 + +ApplicationWindow { + id: root + width: 1920 + height: 1080 + visible: true + flags: Qt.FramelessWindowHint + style: ApplicationWindowStyle { + background: Rectangle { + color: "black" + } + } + + ValueSource { + id: valueSource + } + + Rectangle { + id: statusFrame + x: (parent.width - width) / 2 + y: 80 + width: 960 + height: 96 + radius: height / 5 + + color: "black" + border.width: 2 + border.color: "grey" + + Row { + width: parent.width + height: parent.height * 0.75 + spacing: (parent.width - (10 * parent.height * 0.75)) / 11 + + anchors.fill: parent + anchors.topMargin: (parent.height - height) /2 + anchors.bottomMargin: (parent.height - height) /2 + anchors.leftMargin: (parent.width - (10 * parent.height * 0.75)) / 11 + anchors.rightMargin: (parent.width - (10 * parent.height * 0.75)) / 11 + + Rectangle { + width: height + height: parent.height + radius: height / 5 + + color: "black" + border.width: 2 + border.color: "grey" + + TurnIndicator { + id: leftIndicator + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + width: height + height: parent.height * 0.75 + + direction: Qt.LeftArrow + on: valueSource.turnSignal == Qt.LeftArrow + } + } + + Rectangle { + width: height + height: parent.height + radius: height / 5 + + color: "black" + border.width: 2 + border.color: "grey" + } + + Rectangle { + width: height + height: parent.height + radius: height / 5 + + color: "black" + border.width: 2 + border.color: "grey" + } + + Rectangle { + width: height + height: parent.height + radius: height / 5 + + color: "black" + border.width: 2 + border.color: "grey" + } + + Rectangle { + width: height + height: parent.height + radius: height / 5 + + color: "black" + border.width: 2 + border.color: "grey" + + Text { + id: prindle + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + horizontalAlignment: Text.AlignCenter + + text: valueSource.prindle + color: "white" + font.pixelSize: parent.height * 0.85 + } + } + + Rectangle { + id: gearIndicatior + width: height + height: parent.height + radius: height / 5 + + color: "black" + border.width: 2 + border.color: "grey" + + Text { + id: gear + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + horizontalAlignment: Text.AlignCenter + + text: valueSource.gear + color: "white" + font.pixelSize: parent.height * 0.85 + } + } + + Rectangle { + width: height + height: parent.height + radius: height / 5 + + color: "black" + border.width: 2 + border.color: "grey" + } + + Rectangle { + width: height + height: parent.height + radius: height / 5 + + color: "black" + border.width: 2 + border.color: "grey" + } + + Rectangle { + width: height + height: parent.height + radius: height / 5 + + color: "black" + border.width: 2 + border.color: "grey" + } + + Rectangle { + width: height + height: parent.height + radius: height / 5 + + color: "black" + border.width: 2 + border.color: "grey" + + TurnIndicator { + id: rightIndicator + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + width: height + height: parent.height * 0.75 + + direction: Qt.RightArrow + on: valueSource.turnSignal == Qt.RightArrow + } + } + } + } + + Item { + x: 36 + y: 240 + width: 600 + height: width + + CircularGauge { + id: accelerometer + x: (parent.width - width) / 2 + //y: (parent.height - height) / 2 + width: parent.width * 0.9 + height: width + + maximumValue: 220 + value: valueSource.kph + + style: DashboardGaugeStyle {} + } + } + + Item { + x: 1284 + y: 240 + width: 600 + height: width + + CircularGauge { + id: tachometer + x: (parent.width - width) / 2 + width: parent.width * 0.9 + height: width + + maximumValue: 8 + value: valueSource.rpm + + style: TachometerStyle {} + } + + CircularGauge { + id: fuelGauge + value: valueSource.fuel + maximumValue: 1 + y: parent.width * 0.85 + width: parent.width * 0.45 + height: parent.height * 0.25 + + style: IconGaugeStyle { + id: fuelGaugeStyle + + icon: "./images/fuel-icon.png" + minWarningColor: Qt.rgba(0.5, 0, 0, 1) + + tickmarkLabel: Text { + color: "white" + visible: styleData.value === 0 || styleData.value === 1 + font.pixelSize: fuelGaugeStyle.toPixels(0.225) + text: styleData.value === 0 ? "E" : (styleData.value === 1 ? "F" : "") + } + } + } + + CircularGauge { + id: tempGauge + value: valueSource.temperature + maximumValue: 1 + x: parent.width * 0.55 + y: parent.width * 0.85 + width: parent.width * 0.45 + height: parent.height * 0.25 + + style: IconGaugeStyle { + id: tempGaugeStyle + + icon: "./images/temperature-icon.png" + maxWarningColor: Qt.rgba(0.5, 0, 0, 1) + + tickmarkLabel: Text { + color: "white" + visible: styleData.value === 0 || styleData.value === 1 + font.pixelSize: tempGaugeStyle.toPixels(0.225) + text: styleData.value === 0 ? "C" : (styleData.value === 1 ? "H" : "") + } + } + } + } + + Rectangle { + id: frame + x: 672 + y: 264 + width: 576 + height: 552 + + color: "black" + border.width: 4 + border.color: "grey" + + Image { + source: './images/Utility_Logo_Grey-01.svg' + x: (parent.width - width) / 2 + y: (parent.height - height) / 2 + width: parent.width / 2 + height: width + } + } + + Image { + source: './images/agl_title_793x211.png' + //x: 772 + x: (parent.width - width) / 2 + y: 898 + width: 376 + height: 100 + } +} diff --git a/app/cluster-gauges.qrc b/app/cluster-gauges.qrc new file mode 100644 index 0000000..22f7528 --- /dev/null +++ b/app/cluster-gauges.qrc @@ -0,0 +1,10 @@ + + + cluster-gauges.qml + DashboardGaugeStyle.qml + IconGaugeStyle.qml + TachometerStyle.qml + TurnIndicator.qml + ValueSource.qml + + diff --git a/app/images/Utility_Logo_Grey-01.svg b/app/images/Utility_Logo_Grey-01.svg new file mode 100644 index 0000000..16ac88c --- /dev/null +++ b/app/images/Utility_Logo_Grey-01.svg @@ -0,0 +1,84 @@ + + + + + + + + + + +]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/images/agl_title_793x211.png b/app/images/agl_title_793x211.png new file mode 100644 index 0000000..5a183d2 Binary files /dev/null and b/app/images/agl_title_793x211.png differ diff --git a/app/images/fuel-icon.png b/app/images/fuel-icon.png new file mode 100644 index 0000000..70da758 Binary files /dev/null and b/app/images/fuel-icon.png differ diff --git a/app/images/images.qrc b/app/images/images.qrc new file mode 100644 index 0000000..8767add --- /dev/null +++ b/app/images/images.qrc @@ -0,0 +1,8 @@ + + + agl_title_793x211.png + Utility_Logo_Grey-01.svg + fuel-icon.png + temperature-icon.png + + diff --git a/app/images/temperature-icon.png b/app/images/temperature-icon.png new file mode 100644 index 0000000..5a4334e Binary files /dev/null and b/app/images/temperature-icon.png differ diff --git a/app/main.cpp b/app/main.cpp new file mode 100644 index 0000000..4f36ef7 --- /dev/null +++ b/app/main.cpp @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2016 The Qt Company Ltd. + * Copyright (C) 2018 Konsulko Group + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +int main(int argc, char *argv[]) +{ + // Slight hack, using the homescreen role greatly simplifies things wrt + // the windowmanager + QString myname = QString("homescreen"); + + QGuiApplication app(argc, argv); + + QCommandLineParser parser; + parser.addPositionalArgument("port", app.translate("main", "port for binding")); + parser.addPositionalArgument("secret", app.translate("main", "secret for binding")); + parser.addHelpOption(); + parser.addVersionOption(); + parser.process(app); + QStringList positionalArguments = parser.positionalArguments(); + + QQmlApplicationEngine engine; + + if (positionalArguments.length() == 2) { + int port = positionalArguments.takeFirst().toInt(); + QString secret = positionalArguments.takeFirst(); + QUrl bindingAddress; + bindingAddress.setScheme(QStringLiteral("ws")); + bindingAddress.setHost(QStringLiteral("localhost")); + bindingAddress.setPort(port); + bindingAddress.setPath(QStringLiteral("/api")); + QUrlQuery query; + query.addQueryItem(QStringLiteral("token"), secret); + bindingAddress.setQuery(query); + QQmlContext *context = engine.rootContext(); + context->setContextProperty(QStringLiteral("bindingAddress"), bindingAddress); + + std::string token = secret.toStdString(); + QLibWindowmanager* qwm = new QLibWindowmanager(); + + // WindowManager + if(qwm->init(port, secret) != 0){ + exit(EXIT_FAILURE); + } + + // Request a surface as described in layers.json windowmanager’s file + if (qwm->requestSurface(myname) != 0) { + exit(EXIT_FAILURE); + } + + // Create an event callback against an event type. Here a lambda is called when SyncDraw event occurs + qwm->set_event_handler(QLibWindowmanager::Event_SyncDraw, [qwm, myname](json_object*) { + fprintf(stderr, "Surface got syncDraw!\n"); + qwm->endDraw(myname); + }); + + engine.load(QUrl(QStringLiteral("qrc:/cluster-gauges.qml"))); + + // Find the instantiated model QObject and connect the signals/slots + QList mobjs = engine.rootObjects(); + + QQuickWindow *window = qobject_cast(mobjs.first()); + QObject::connect(window, SIGNAL(frameSwapped()), qwm, SLOT(slotActivateSurface())); + } + + return app.exec(); +} -- cgit 1.2.3-korg