From 65d4619371979c8921ff155a6fe1d7de0e1d3598 Mon Sep 17 00:00:00 2001 From: Suchinton Chakravarty Date: Sun, 15 Sep 2024 23:31:58 +0530 Subject: Add New Custom Gauges and CARLA playback refactored - This commit adds new custom QML Gauges for Engine RPM, Speed, Fuel level and Coolant temp - Improve Exception handling for CARLA playback - Add the RPM and Speed Gauge elements - Update the functions resp. for updating their values - Fix Alignment of backgrounds, font size and progress bar - Update Half Gauge to have progress ticks - Add gauges to the main IC page Bug-AGL: SPEC-5161 Change-Id: I52274afb7ea95c812c539a0b21305ad078d5dadb Signed-off-by: Suchinton Chakravarty --- Main_Window.ui | 16 +- QMLWidgets/Full_Gauge/RPMGauge.qml | 165 ++++++ QMLWidgets/Full_Gauge/SpeedGauge.qml | 202 +++++++ QMLWidgets/Full_Gauge/assets/Ellipse 1.svg | 11 + QMLWidgets/Full_Gauge/assets/Ellipse 5.svg | 24 + QMLWidgets/Half_Gauge/CoolantGauge.qml | 150 ++++++ QMLWidgets/Half_Gauge/FuelGauge.qml | 158 ++++++ Scripts/record_playback.py | 1 + Widgets/ICPage.py | 91 +++- Widgets/Keypad.py | 53 ++ requirements.txt | 4 +- ui/IC.ui | 822 ++++++++++++----------------- ui/Keypad.ui | 316 +++++++++++ 13 files changed, 1504 insertions(+), 509 deletions(-) create mode 100644 QMLWidgets/Full_Gauge/RPMGauge.qml create mode 100644 QMLWidgets/Full_Gauge/SpeedGauge.qml create mode 100644 QMLWidgets/Full_Gauge/assets/Ellipse 1.svg create mode 100644 QMLWidgets/Full_Gauge/assets/Ellipse 5.svg create mode 100644 QMLWidgets/Half_Gauge/CoolantGauge.qml create mode 100644 QMLWidgets/Half_Gauge/FuelGauge.qml create mode 100644 Widgets/Keypad.py create mode 100644 ui/Keypad.ui diff --git a/Main_Window.ui b/Main_Window.ui index 71fe4a4..41500a8 100644 --- a/Main_Window.ui +++ b/Main_Window.ui @@ -9,10 +9,16 @@ 0 0 - 903 - 705 + 1600 + 900 + + + 1600 + 900 + + @@ -153,7 +159,7 @@ - + 255 255 255 @@ -299,7 +305,7 @@ - + 255 255 255 @@ -445,7 +451,7 @@ - + 255 255 255 diff --git a/QMLWidgets/Full_Gauge/RPMGauge.qml b/QMLWidgets/Full_Gauge/RPMGauge.qml new file mode 100644 index 0000000..382731d --- /dev/null +++ b/QMLWidgets/Full_Gauge/RPMGauge.qml @@ -0,0 +1,165 @@ +import QtQuick 2.9 + +Item { + id: root + width: 400 + height: 400 + + property real value: 0 + property real minValue: 0 + property real maxValue: 8000 + property string unit: "RPM" + + property color primaryColor: "#16CCBA" + property color secondaryColor: "#00000000" + property int animationDuration: 1000 + property real startAngle: 0 + + Rectangle { + anchors.fill: parent + color: "#041F2B" + z: -1 + } + + Image { + id: background1 + source: "./assets/Ellipse 5.svg" + rotation: 180 + sourceSize.width: width + sourceSize.height: width + fillMode: Image.PreserveAspectFit + anchors.fill: parent + anchors.centerIn: parent + scale: 1 + opacity: 0.7 + z: 0 + } + + Image { + id: background2 + source: "./assets/Ellipse 1.svg" + sourceSize.width: width + sourceSize.height: width + anchors.fill: parent + anchors.centerIn: parent + fillMode: Image.PreserveAspectFit + scale: 1 + opacity: 0.7 + z: 1 + } + + Canvas { + id: canvas + anchors.fill: parent + antialiasing: true + property real degree: 0 + + onDegreeChanged: requestPaint() + + onPaint: { + var ctx = getContext("2d") + var center = Qt.point(width / 2, height / 2) + var side = Math.min(width, height) + var radius = (side - side * 0.20) / 2 + var startAngle = Math.PI * 3 / 4 + var fullAngle = Math.PI * 3 / 2 + var progressAngle = startAngle + (degree / 270) * fullAngle + + ctx.reset() + ctx.lineCap = 'round' + + ctx.lineWidth = side * 0.1 + ctx.beginPath() + ctx.arc(center.x, center.y, radius, startAngle, progressAngle) + ctx.strokeStyle = secondaryColor + ctx.stroke() + + ctx.lineWidth = 20 + ctx.beginPath() + ctx.arc(center.x, center.y, radius, startAngle, progressAngle) + ctx.strokeStyle = primaryColor + ctx.stroke() + + // draw tick marks + ctx.lineWidth = 10 + ctx.strokeStyle = '#FFFFFF' + ctx.beginPath() + var tickCount = 2; // Number of tick marks + for (var i = 0; i < tickCount; i++) { + var angle = startAngle + (i / (tickCount - 1)) * (progressAngle - startAngle) + var x1 = center.x + radius * Math.cos(angle) + var y1 = center.y + radius * Math.sin(angle) + var x2 = center.x + (radius - 20) * Math.cos(angle) + var y2 = center.y + (radius - 20) * Math.sin(angle) + ctx.moveTo(x1, y1) + ctx.lineTo(x2, y2) + } + ctx.stroke() + } + } + + Text { + id: gaugeText + anchors.centerIn: parent + text: "0" + font.pixelSize: 0.2 * Math.min(root.width, root.height) + font.bold: true + color: "#FFFFFF" + } + + Text { + id: gaugeUnit + anchors.top: gaugeText.bottom + anchors.horizontalCenter: gaugeText.horizontalCenter + + text: unit + font.pixelSize: 18 + font.bold: true + color: "#FFFFFF" + } + + Behavior on value { + NumberAnimation { duration: animationDuration } + } + + onValueChanged: updateGaugeValue(value) + + function updateGaugeValue(value) { + canvas.degree = value * 270 + gaugeText.text = (value * (maxValue - minValue) + minValue).toFixed(0) + } + + MouseArea { + anchors.fill: parent + hoverEnabled: true + + property bool isDragging: false + property real initialValue: 0 + + onEntered: cursorShape = Qt.PointingHandCursor + onPressed: { + isDragging = true + initialValue = root.value + } + onReleased: isDragging = false + + onMouseXChanged: updateDragValue(mouse.x, mouse.y) + onMouseYChanged: updateDragValue(mouse.x, mouse.y) + + function updateDragValue(x, y) { + if (!isDragging) return + + var centerX = width / 2 + var centerY = height / 2 + var dx = x - centerX + var dy = centerY - y // Note: y-axis is inverted + var angle = Math.atan2(dy, dx) * (180 / Math.PI) + + // Convert angle to gauge value (0 to 1) + var gaugeValue = (angle + 135) / 270 // Adjust based on your gauge's start angle + gaugeValue = Math.max(0, Math.min(gaugeValue, 1)) // Clamp value + + root.value = gaugeValue + } + } +} diff --git a/QMLWidgets/Full_Gauge/SpeedGauge.qml b/QMLWidgets/Full_Gauge/SpeedGauge.qml new file mode 100644 index 0000000..263e7f1 --- /dev/null +++ b/QMLWidgets/Full_Gauge/SpeedGauge.qml @@ -0,0 +1,202 @@ +import QtQuick 2.9 + +Item { + id: root + width: 400 + height: 400 + + property real value: 0 + property real minValue: 0 + property real maxValue: 240 + property string unit: "Km/h" + + property color primaryColor: "#16CCBA" + property color secondaryColor: "#00000000" + property int animationDuration: 500 + property real startAngle: Math.PI * 3 / 4 + property real fullAngle: Math.PI * 3 / 2 + + property real gaugeRadius: 0 + property real tickLength: 0 + + Component.onCompleted: { + calculateGeometry(); + } + + onWidthChanged: calculateGeometry() + onHeightChanged: calculateGeometry() + + function calculateGeometry() { + const side = Math.min(width, height); + gaugeRadius = (side - side * 0.20) / 2; + tickLength = gaugeRadius * 0.05; + } + + Rectangle { + anchors.fill: parent + color: "#041F2B" + z: -1 + } + + Image { + id: background1 + source: "./assets/Ellipse 5.svg" + rotation: 180 + sourceSize.width: width + sourceSize.height: width + fillMode: Image.PreserveAspectFit + anchors.fill: parent + scale: 1 + opacity: 0.7 + z: 0 + } + + Image { + id: background2 + source: "./assets/Ellipse 1.svg" + sourceSize.width: width + sourceSize.height: width + fillMode: Image.PreserveAspectFit + anchors.fill: parent + scale: 1 + opacity: 0.7 + z: 1 + } + + Canvas { + id: canvas + anchors.fill: parent + antialiasing: true + property real degree: 0 + + onDegreeChanged: requestPaint() + + onPaint: { + var ctx = getContext("2d"); + var center = Qt.point(width / 2, height / 2); + + ctx.reset(); + ctx.lineCap = 'round'; + + drawBackground(ctx, center); + drawProgress(ctx, center, degree); + drawTicks(ctx, center); + drawProgressTick(ctx, center, degree); + } + + function drawBackground(ctx, center) { + ctx.lineWidth = gaugeRadius * 0.1; + ctx.beginPath(); + ctx.arc(center.x, center.y, gaugeRadius, startAngle, startAngle + fullAngle); + ctx.strokeStyle = secondaryColor; + ctx.stroke(); + } + + function drawProgress(ctx, center, progress) { + ctx.lineWidth = 20; + ctx.beginPath(); + ctx.arc(center.x, center.y, gaugeRadius, startAngle, startAngle + (progress / 270) * fullAngle); + ctx.strokeStyle = primaryColor; + ctx.stroke(); + } + + function drawTicks(ctx, center) { + ctx.lineWidth = tickLength * 2 + ctx.strokeStyle = '#FFFFFF' + ctx.beginPath() + const tickCount = 2 + for (let i = 0; i < tickCount; i++) { + const angle = startAngle + (i / (tickCount - 1)) * fullAngle + const x1 = center.x + gaugeRadius * Math.cos(angle) + const y1 = center.y + gaugeRadius * Math.sin(angle) + const x2 = center.x + (gaugeRadius - tickLength) * Math.cos(angle) + const y2 = center.y + (gaugeRadius - tickLength) * Math.sin(angle) + ctx.moveTo(x1, y1) + ctx.lineTo(x2, y2) + } + ctx.stroke(); + } + + function drawProgressTick(ctx, center, progress) { + + ctx.lineWidth = tickLength * 3 + ctx.strokeStyle = '#FFFFFF' + ctx.beginPath() + + const progressAngle = startAngle + (progress / 270) * fullAngle + const x1 = center.x + gaugeRadius * Math.cos(progressAngle) + const y1 = center.y + gaugeRadius * Math.sin(progressAngle) + const x2 = center.x + (gaugeRadius - tickLength * 1.5) * Math.cos(progressAngle) + const y2 = center.y + (gaugeRadius - tickLength * 1.5) * Math.sin(progressAngle) + + ctx.moveTo(x1, y1) + ctx.lineTo(x2, y2) + ctx.stroke() + } + } + + Text { + id: gaugeText + anchors.centerIn: parent + text: "0" + font.pixelSize: 0.2 * Math.min(root.width, root.height) + font.bold: true + color: "#FFFFFF" + } + + Text { + id: gaugeUnit + anchors.top: gaugeText.bottom + anchors.horizontalCenter: gaugeText.horizontalCenter + text: unit + font.pixelSize: 18 + font.bold: true + color: "#FFFFFF" + } + + Behavior on value { + NumberAnimation { duration: animationDuration } + } + + onValueChanged: updateGaugeValue(value) + + function updateGaugeValue(value) { + canvas.degree = value * 270 + gaugeText.text = (value * (maxValue - minValue) + minValue).toFixed(0) + } + + MouseArea { + id: dragArea + anchors.fill: parent + hoverEnabled: true + + property bool isDragging: false + property real initialValue: 0 + + onEntered: cursorShape = Qt.PointingHandCursor + onPressed: { + isDragging = true + initialValue = root.value + } + onReleased: isDragging = false + + onMouseXChanged: updateDragValue(mouse.x, mouse.y) + onMouseYChanged: updateDragValue(mouse.x, mouse.y) + + function updateDragValue(x, y) { + if (!isDragging) return + + const centerX = width / 2 + const centerY = height / 2 + const dx = x - centerX + const dy = centerY - y // Note: y-axis is inverted + const angle = Math.atan2(dy, dx) * (180 / Math.PI) + + // Convert angle to gauge value (0 to 1) + let gaugeValue = (angle + 135) / 270 + gaugeValue = Math.max(0, Math.min(gaugeValue, 1)) // Clamp value + + root.value = gaugeValue + } + } +} diff --git a/QMLWidgets/Full_Gauge/assets/Ellipse 1.svg b/QMLWidgets/Full_Gauge/assets/Ellipse 1.svg new file mode 100644 index 0000000..c2666d9 --- /dev/null +++ b/QMLWidgets/Full_Gauge/assets/Ellipse 1.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/QMLWidgets/Full_Gauge/assets/Ellipse 5.svg b/QMLWidgets/Full_Gauge/assets/Ellipse 5.svg new file mode 100644 index 0000000..a4a07a2 --- /dev/null +++ b/QMLWidgets/Full_Gauge/assets/Ellipse 5.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/QMLWidgets/Half_Gauge/CoolantGauge.qml b/QMLWidgets/Half_Gauge/CoolantGauge.qml new file mode 100644 index 0000000..6e1d583 --- /dev/null +++ b/QMLWidgets/Half_Gauge/CoolantGauge.qml @@ -0,0 +1,150 @@ +import QtQuick 2.9 + +Item { + id: root + width: 200 + height: 200 + + property real value: 80 + property real minValue: 0 + property real maxValue: 120 + property string unit: "°C" + + property color primaryColor: "#16CCBA" + property color secondaryColor: "#00000000" + property int animationDuration: 500 + property real startAngle: 0 + + Rectangle { + anchors.fill: parent + color: "#041F2B" + z: -1 + } + + Canvas { + id: canvas + anchors.fill: parent + antialiasing: true + property real degree: 0 + + onDegreeChanged: requestPaint() + + onPaint: { + var ctx = getContext("2d") + var center = Qt.point(width / 2, height / 2) + var side = Math.min(width, height) + var radius = (side - side * 0.25) / 2 + var startAngle = Math.PI * 2 / 3 + var fullAngle = Math.PI * 2 / 3 + var progressAngle = startAngle + (degree / 270) * fullAngle + + ctx.reset() + ctx.lineCap = 'square' + + // background arc + ctx.lineWidth = 25 + ctx.beginPath() + ctx.arc(center.x, center.y, radius, startAngle, startAngle + fullAngle) + ctx.strokeStyle = '#000000' + ctx.stroke() + + // fill arc + ctx.lineWidth = 15 + ctx.beginPath() + ctx.arc(center.x, center.y, radius, startAngle, progressAngle) + ctx.strokeStyle = primaryColor + ctx.stroke() + + // draw tick marks + ctx.lineWidth = 5 + ctx.strokeStyle = '#FFFFFF' + ctx.beginPath() + var tickCount = 2; // Number of tick marks + for (var i = 0; i < tickCount; i++) { + var angle = startAngle + (i / (tickCount - 1)) * (progressAngle - startAngle) + var x1 = center.x + radius * Math.cos(angle) + var y1 = center.y + radius * Math.sin(angle) + var x2 = center.x + (radius - 10) * Math.cos(angle) + var y2 = center.y + (radius - 10) * Math.sin(angle) + ctx.moveTo(x1, y1) + ctx.lineTo(x2, y2) + } + ctx.stroke() + } + } + + Text { + id: gaugeText + anchors.centerIn: parent + text: "0" + font.pixelSize: 0.3 * Math.min(root.width, root.height) + font.bold: true + color: "#FFFFFF" + } + + Text { + id: gaugePercentage + anchors.verticalCenter: gaugeText.verticalCenter + anchors.left: gaugeText.right + text: "%" + font.pixelSize: 0.15 * Math.min(root.width, root.height) + font.bold: true + color: "#FFFFFF" + } + + Text { + id: gaugeUnit + anchors.top: gaugeText.bottom + anchors.horizontalCenter: gaugeText.horizontalCenter + + text: unit + font.pixelSize: 18 + font.bold: true + color: "#FFFFFF" + } + + Behavior on value { + NumberAnimation { duration: animationDuration } + } + + onValueChanged: updateGaugeValue(value) + + function updateGaugeValue(value) { + canvas.degree = value * 270 + gaugeText.text = (value * (maxValue - minValue) + minValue).toFixed(0) + } + + MouseArea { + anchors.fill: parent + hoverEnabled: true + + property bool isDragging: false + property real initialValue: 0 + + onEntered: cursorShape = Qt.PointingHandCursor + onPressed: { + isDragging = true + initialValue = root.value + } + onReleased: isDragging = false + + onMouseXChanged: updateDragValue(mouse.x, mouse.y) + onMouseYChanged: updateDragValue(mouse.x, mouse.y) + + function updateDragValue(x, y) { + if (!isDragging) return + + var centerX = width / 2 + var centerY = height / 2 + var dx = x - centerX + var dy = centerY - y // Note: y-axis is inverted + var angle = Math.atan2(dy, dx) * (180 / Math.PI) + + // Convert angle to gauge value (0 to 1) + var gaugeValue = (angle + 135) / 270; // Adjust based on your gauge's start angle + gaugeValue = Math.max(0, Math.min(gaugeValue, 1)); // Clamp value + + root.value = gaugeValue; + } + } +} diff --git a/QMLWidgets/Half_Gauge/FuelGauge.qml b/QMLWidgets/Half_Gauge/FuelGauge.qml new file mode 100644 index 0000000..21ac4cd --- /dev/null +++ b/QMLWidgets/Half_Gauge/FuelGauge.qml @@ -0,0 +1,158 @@ +import QtQuick 2.9 + +Item { + id: root + width: 200 + height: 200 + + property real value: 80 + property real minValue: 0 + property real maxValue: 100 + property string unit: "NA" + + property color primaryColor: "#16CCBA" + property color secondaryColor: "#00000000" + property int animationDuration: 500 + property real startAngle: 0 + + property var exposedFunction: updateGaugeUnit + + Rectangle { + anchors.fill: parent + color: "#041F2B" + z: -1 + } + + Canvas { + id: canvas + anchors.fill: parent + antialiasing: true + property real degree: 0 + + onDegreeChanged: requestPaint() + + onPaint: { + var ctx = getContext("2d") + var center = Qt.point(width / 2, height / 2) + var side = Math.min(width, height) + var radius = (side - side * 0.25) / 2 + var startAngle = Math.PI * 2 / 3 + var fullAngle = Math.PI * 2 / 3 + var progressAngle = startAngle + (degree / 270) * fullAngle + + ctx.reset() + ctx.lineCap = 'square' + + // background arc + ctx.lineWidth = 25 + ctx.beginPath() + ctx.arc(center.x, center.y, radius, startAngle, startAngle + fullAngle) + ctx.strokeStyle = '#000000' + ctx.stroke() + + // fill arc + ctx.lineWidth = 15 + ctx.beginPath() + ctx.arc(center.x, center.y, radius, startAngle, progressAngle) + ctx.strokeStyle = primaryColor + ctx.stroke() + + // draw tick marks + ctx.lineWidth = 5 + ctx.strokeStyle = '#FFFFFF' + ctx.beginPath() + var tickCount = 2; // Number of tick marks + for (var i = 0; i < tickCount; i++) { + var angle = startAngle + (i / (tickCount - 1)) * (progressAngle - startAngle) + var x1 = center.x + radius * Math.cos(angle) + var y1 = center.y + radius * Math.sin(angle) + var x2 = center.x + (radius - 10) * Math.cos(angle) + var y2 = center.y + (radius - 10) * Math.sin(angle) + ctx.moveTo(x1, y1) + ctx.lineTo(x2, y2) + } + ctx.stroke() + } + } + + Text { + id: gaugeText + anchors.centerIn: parent + text: "0" + font.pixelSize: 0.3 * Math.min(root.width, root.height) + font.bold: true + color: "#FFFFFF" + } + + Text { + id: gaugePercentage + // anchor this to the right of the gaugeText such that their baseline is aligned + + anchors.verticalCenter: gaugeText.verticalCenter + anchors.left: gaugeText.right + text: "%" + font.pixelSize: 0.15 * Math.min(root.width, root.height) + font.bold: true + color: "#FFFFFF" + } + + Text { + id: gaugeUnit + anchors.top: gaugeText.bottom + anchors.horizontalCenter: gaugeText.horizontalCenter + + text: unit + font.pixelSize: 18 + font.bold: true + color: "#FFFFFF" + } + + Behavior on value { + NumberAnimation { duration: animationDuration } + } + + onValueChanged: updateGaugeValue(value) + + function updateGaugeValue(value) { + canvas.degree = value * 270 + gaugeText.text = (value * (maxValue - minValue) + minValue).toFixed(0) + } + + function updateGaugeUnit() { + gaugeUnit.text = unit + } + + MouseArea { + anchors.fill: parent + hoverEnabled: true + + property bool isDragging: false + property real initialValue: 0 + + onEntered: cursorShape = Qt.PointingHandCursor + onPressed: { + isDragging = true + initialValue = root.value + } + onReleased: isDragging = false + + onMouseXChanged: updateDragValue(mouse.x, mouse.y) + onMouseYChanged: updateDragValue(mouse.x, mouse.y) + + function updateDragValue(x, y) { + if (!isDragging) return + + var centerX = width / 2 + var centerY = height / 2 + var dx = x - centerX + var dy = centerY - y // Note: y-axis is inverted + var angle = Math.atan2(dy, dx) * (180 / Math.PI) + + // Convert angle to gauge value (0 to 1) + var gaugeValue = (angle + 135) / 270; // Adjust based on your gauge's start angle + gaugeValue = Math.max(0, Math.min(gaugeValue, 1)); // Clamp value + + root.value = gaugeValue; + } + } +} diff --git a/Scripts/record_playback.py b/Scripts/record_playback.py index e518356..5d3956e 100644 --- a/Scripts/record_playback.py +++ b/Scripts/record_playback.py @@ -139,3 +139,4 @@ def main(): if __name__ == "__main__": main() + diff --git a/Widgets/ICPage.py b/Widgets/ICPage.py index 213e74c..e3d4ddf 100644 --- a/Widgets/ICPage.py +++ b/Widgets/ICPage.py @@ -10,7 +10,8 @@ from PyQt6 import uic, QtCore, QtWidgets from PyQt6.QtWidgets import QApplication from PyQt6.QtGui import QIcon, QPixmap, QPainter from PyQt6.QtCore import QObject, pyqtSignal -from PyQt6.QtWidgets import QWidget +from PyQt6.QtWidgets import QWidget, QFrame +from PyQt6.QtQuickWidgets import QQuickWidget import threading current_dir = os.path.dirname(os.path.abspath(__file__)) @@ -31,6 +32,41 @@ from Scripts.record_playback import CAN_playback import res_rc from Widgets.animatedToggle import AnimatedToggle +def Gauge(gaugeType): + """QWidget + This function creates a full gauge widget with the specified maximum value, current value, and unit. + + Args: + - maxValue: The maximum value of the gauge. + - value: The current value of the gauge. + - unit: The unit of the gauge. + + Returns: + - A QQuickWidget object representing the full gauge widget. + """ + + RPM_GaugeQML = os.path.join(current_dir, "../QMLWidgets/Full_Gauge/RPMGauge.qml") + Speed_GaugeQML = os.path.join(current_dir, "../QMLWidgets/Full_Gauge/SpeedGauge.qml") + Fuel_GaugeQML = os.path.join(current_dir, "../QMLWidgets/Half_Gauge/FuelGauge.qml") + Coolant_GaugeQML = os.path.join(current_dir, "../QMLWidgets/Half_Gauge/CoolantGauge.qml") + + gauge = QQuickWidget() + if gaugeType == "RPM": + gauge.setSource(QtCore.QUrl(RPM_GaugeQML)) + + elif gaugeType == "Speed": + gauge.setSource(QtCore.QUrl(Speed_GaugeQML)) + + elif gaugeType == "Fuel": + gauge.setSource(QtCore.QUrl(Fuel_GaugeQML)) + + elif gaugeType == "Coolant": + gauge.setSource(QtCore.QUrl(Coolant_GaugeQML)) + + gauge.setResizeMode(QQuickWidget.ResizeMode.SizeRootObjectToView) + + return gauge + class IC_Paths(): def __init__(self): self.speed = "Vehicle.Speed" @@ -42,7 +78,6 @@ class IC_Paths(): self.coolantTemp = "Vehicle.Powertrain.CombustionEngine.ECT" self.selectedGear = "Vehicle.Powertrain.Transmission.SelectedGear" - class ICWidget(Base, Form): """ This class represents the ICWidget which is a widget for the AGL Demo Control Panel. @@ -68,7 +103,9 @@ class ICWidget(Base, Form): header_frame = self.findChild(QWidget, "header_frame") layout = header_frame.layout() - self.IC_Frame = self.findChild(QWidget, "frame_1") + self.Frame_1 = self.findChild(QWidget, "frame_1") + self.Fuel_Gauge_Frame = self.findChild(QFrame, "fuel_gauge_frame") + self.Coolant_Gauge_Frame = self.findChild(QFrame, "coolant_gauge_frame") self.Script_toggle = AnimatedToggle() @@ -93,23 +130,41 @@ class ICWidget(Base, Form): self.driveGroupBtns.buttonClicked.connect(self.driveBtnClicked) - self.Speed_slider.valueChanged.connect(self.update_Speed_monitor) + Speed_Gauge_Placeholder = self.findChild(QWidget, "Speed_Gauge_Placeholder") + self.Speed_Gauge = Gauge("Speed") + self.Frame_1.layout().replaceWidget(Speed_Gauge_Placeholder, self.Speed_Gauge) + + self.Speed_slider.valueChanged.connect(self.update_Speed_gauge) self.Speed_slider.setMinimum(0) self.Speed_slider.setMaximum(240) + RPM_Gauge_Placeholder = self.findChild(QWidget, "RPM_Gauge_Placeholder") + self.RPM_Gauge = Gauge("RPM") + self.Frame_1.layout().replaceWidget(RPM_Gauge_Placeholder, self.RPM_Gauge) + self.RPM_slider.valueChanged.connect(self.update_RPM_monitor) self.RPM_slider.setMinimum(0) self.RPM_slider.setMaximum(8000) + fuel_Gauge_Placeholder = self.findChild(QWidget, "fuel_Gauge_Placeholder") + self.Fuel_Gauge = Gauge("Fuel") + self.Fuel_Gauge_Frame.layout().replaceWidget(fuel_Gauge_Placeholder, self.Fuel_Gauge) + + coolant_Gauge_Placeholder = self.findChild(QWidget, "coolant_Gauge_Placeholder") + self.Coolant_Gauge = Gauge("Coolant") + self.Coolant_Gauge_Frame.layout().replaceWidget(coolant_Gauge_Placeholder, self.Coolant_Gauge) + + self.mousePressEvent = lambda event: print("Mouse Pressed") + self.mouseReleaseEvent = lambda event: print("Mouse Released") + + self.Coolant_Gauge.mousePressEvent = self.mousePressEvent + self.Coolant_Gauge.mouseReleaseEvent = self.mouseReleaseEvent + self.coolantTemp_slider.valueChanged.connect( self.update_coolantTemp_monitor) self.fuelLevel_slider.valueChanged.connect( self.update_fuelLevel_monitor) - self.accelerationBtn.pressed.connect(self.accelerationBtnPressed) - self.accelerationBtn.released.connect(self.accelerationBtnReleased) - - # make both buttons checkable self.Script_toggle.clicked.connect(self.handle_Script_toggle) self.leftIndicatorBtn.setCheckable(True) self.rightIndicatorBtn.setCheckable(True) @@ -125,12 +180,13 @@ class ICWidget(Base, Form): def set_Vehicle_RPM(self, rpm): self.RPM_slider.setValue(rpm) - def update_Speed_monitor(self): + def update_Speed_gauge(self): """ Updates the speed monitor with the current speed value. """ speed = int(self.Speed_slider.value()) - self.Speed_monitor.display(speed) + speed = speed / 240 + self.Speed_Gauge.rootObject().setProperty('value', speed) if not self.simulator_running: try: self.kuksa_client.set(self.IC.speed, str(speed), 'value') @@ -142,7 +198,8 @@ class ICWidget(Base, Form): Updates the RPM monitor with the current RPM value. """ rpm = int(self.RPM_slider.value()) - self.RPM_monitor.display(rpm) + rpm = rpm / 8000 + self.RPM_Gauge.rootObject().setProperty('value', rpm) if not self.simulator_running: try: self.kuksa_client.set(self.IC.engineRPM, str(rpm), 'value') @@ -154,6 +211,7 @@ class ICWidget(Base, Form): Updates the coolant temperature monitor with the current coolant temperature value. """ coolantTemp = int(self.coolantTemp_slider.value()) + self.Coolant_Gauge.rootObject().setProperty('value', coolantTemp/100) try: self.kuksa_client.set( self.IC.coolantTemp, str(coolantTemp), 'value') @@ -165,6 +223,8 @@ class ICWidget(Base, Form): Updates the fuel level monitor with the current fuel level value. """ fuelLevel = int(self.fuelLevel_slider.value()) + self.Fuel_Gauge.rootObject().setProperty('value', fuelLevel/100) + print(self.Fuel_Gauge.rootObject().property('value')) try: self.kuksa_client.set(self.IC.fuelLevel, str(fuelLevel)) except Exception as e: @@ -279,7 +339,7 @@ class ICWidget(Base, Form): if self.Script_toggle.isChecked(): self.Speed_slider.setEnabled(False) self.RPM_slider.setEnabled(False) - self.accelerationBtn.setEnabled(False) + # self.accelerationBtn.setEnabled(False) for button in self.driveGroupBtns.buttons(): button.setEnabled(False) self.set_Vehicle_RPM(1000) @@ -291,7 +351,7 @@ class ICWidget(Base, Form): self.simulator_running = False self.Speed_slider.setEnabled(True) self.RPM_slider.setEnabled(True) - self.accelerationBtn.setEnabled(True) + # self.accelerationBtn.setEnabled(True) for button in self.driveGroupBtns.buttons(): button.setEnabled(True) @@ -321,7 +381,7 @@ class ICWidget(Base, Form): self.Speed_slider.setValue(self.current_speed) self.RPM_slider.setValue(self.current_rpm) - self.update_Speed_monitor() + self.update_Speed_gauge() self.update_RPM_monitor() def driveBtnClicked(self): @@ -336,7 +396,7 @@ class ICWidget(Base, Form): if checked_button in gear_mapping: gear_value = gear_mapping[checked_button] - self.accelerationBtn.setEnabled(True) + # self.accelerationBtn.setEnabled(True) self.Speed_slider.setEnabled(checked_button != self.neutralBtn) self.RPM_slider.setEnabled(True) try: @@ -346,7 +406,6 @@ class ICWidget(Base, Form): else: print("Unknown button checked!") - class AccelerationFns(): def calculate_speed(time, acceleration) -> int: # acceleration = 60 / 5 # acceleration from 0 to 60 in 5 seconds diff --git a/Widgets/Keypad.py b/Widgets/Keypad.py new file mode 100644 index 0000000..ad0c17e --- /dev/null +++ b/Widgets/Keypad.py @@ -0,0 +1,53 @@ +import os +import sys +from PyQt6 import uic +from PyQt6.QtWidgets import QApplication, QWidget, QPushButton +import requests +from urllib.parse import urljoin + +current_dir = os.path.dirname(os.path.abspath(__file__)) +sys.path.append(os.path.dirname(current_dir)) + +Form, Base = uic.loadUiType(os.path.join(current_dir, "../ui/Keypad.ui")) + +import res_rc + +class KeypadWidget(Base, Form): + def __init__(self, parent=None): + super(self.__class__, self).__init__(parent) + self.setupUi(self) + self.host = "localhost" + self.port = 8080 + + # Mapping of keys to API endpoints + self.key_endpoints = { + "Key_1": "/cgi-bin/flutter.cgi", + "Key_2": "/cgi-bin/qt.cgi", + "Key_3": "/cgi-bin/momi.cgi", + "Key_4": "/cgi-bin/bomb.cgi", + "Key_5": "", # This key won't do anything + } + + # Connect all keys to the same slot + for key_name in self.key_endpoints.keys(): + key_button = self.findChild(QPushButton, key_name) + key_button.clicked.connect(lambda _, x=key_name: self.trigger_api(x)) + + def trigger_api(self, key_name): + endpoint = self.key_endpoints[key_name] + if endpoint: + url = f"http://{self.host}:{self.port}{endpoint}" + try: + response = requests.get(url, timeout=5) + response.raise_for_status() + print(f"API triggered successfully: {url}") + except requests.exceptions.RequestException as e: + print(f"Error triggering API: {str(e)}") + else: + print("No action defined for this key.") + +if __name__ == '__main__': + app = QApplication(sys.argv) + w = KeypadWidget() + w.show() + sys.exit(app.exec()) diff --git a/requirements.txt b/requirements.txt index 490e913..ab78645 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,4 +5,6 @@ PySide6==6.7.1 PySide6_Addons==6.7.1 PySide6_Essentials==6.7.1 kuksa-client==0.4.0 -python-can>=4.2.2 \ No newline at end of file +python-can>=4.2.2 +requests +rich \ No newline at end of file diff --git a/ui/IC.ui b/ui/IC.ui index b337ef6..256eb81 100644 --- a/ui/IC.ui +++ b/ui/IC.ui @@ -6,8 +6,8 @@ 0 0 - 920 - 800 + 1200 + 880 @@ -166,33 +166,13 @@ QLCDNumber { - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - + + + 0 + 0 + + 500 @@ -206,84 +186,10 @@ QLCDNumber { QFrame::Raised - - - - - 0 - 50 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - - 0 - 0 - - - - - Open Sans Extrabold - 18 - 75 - true - true - - - - Instrument Cluster - - - - - - - - - - - 18 - 75 - true - true - - - - Demo Mode - - - - - - - - - - - - - - - + + - + 0 0 @@ -297,7 +203,7 @@ QLCDNumber { 16777215 - 150 + 16777215 @@ -306,150 +212,83 @@ QLCDNumber { QFrame::Raised - - - - - - - - - :/Images/Images/right.png:/Images/Images/right.png + + + + + + 0 + 0 + - + - 60 + 0 60 - - - - - - Qt::Horizontal - - - QSizePolicy::Fixed + + + 50 + false + - - - 20 - 20 - + + 240 - - - - Qt::Horizontal - - QSizePolicy::Fixed + + QSlider::NoTicks - - - 20 - 20 - + + 0 - + - - - - false + + + + + 0 + 0 + - - false + + + Open Sans + 75 + true + true + - - - - - :/Images/Images/left.png:/Images/Images/left.png - - - - 60 - 60 - - - - false - - - false + Speed (Kmph) - - - - - - - - :/Images/Images/hazard.png:/Images/Images/hazard.png + + + + + 0 + 0 + - - - 60 - 60 - + + false - - - - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 20 - 20 - - - - - - - - - 0 - 0 - - - - - 0 - 200 - - - - - 16777215 - 150 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - + + + + 0 + 0 + + Open Sans @@ -463,50 +302,191 @@ QLCDNumber { - - + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 10 + + + + + + - + 0 0 - - - 50 - false - false - + + + + + + + 0 + 0 + + + + + 0 + 60 + - - Qt::LeftToRight + + Qt::Horizontal - - QFrame::NoFrame + + + + + + + 0 + 0 + - - false + + + 0 + 0 + - - 3 + + + 16777215 + 150 + - - QLCDNumber::Flat + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + + + :/Images/Images/right.png:/Images/Images/right.png + + + + 60 + 60 + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + false + + + false + + + + + + + :/Images/Images/left.png:/Images/Images/left.png + + + + 60 + 60 + + + + false + + + false + + + + + + + + + + + :/Images/Images/hazard.png:/Images/Images/hazard.png + + + + 60 + 60 + + + + + - - + + + + + 0 + 0 + + QFrame::StyledPanel QFrame::Raised - - - 0 - + 0 @@ -519,8 +499,33 @@ QLCDNumber { 4 - + + + + + 0 + 0 + + + + + 60 + 0 + + + + Qt::Vertical + + + + + + + 0 + 0 + + @@ -529,207 +534,51 @@ QLCDNumber { - - + + + + + 0 + 0 + + - 60 + 0 0 - - Qt::Vertical - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 10 - 20 - - - - - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 20 - 10 - - - - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 10 - 20 - - - - - - - - - 0 - 60 - - - - Qt::Horizontal - - - - - - - Qt::Vertical - - - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 20 - 20 - - - - - - + + - + 0 0 - - - 50 - false - - - - QFrame::NoFrame - - - 4 - - - QLCDNumber::Flat - - - - - - - - Open Sans - 75 - true - true - - - - Speed (Kmph) - - - - - - - - 0 - 60 - - - - - 50 - false - - - - 240 - - - Qt::Horizontal - - - QSlider::NoTicks - - - 0 - - - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 20 - 20 - - - - - - QFrame::StyledPanel QFrame::Raised - - - 0 - + 4 - + + + + 0 + 0 + + 60 @@ -758,10 +607,10 @@ QLCDNumber { - + - + 0 0 @@ -785,95 +634,94 @@ QLCDNumber { + + + + + 0 + 0 + + + + + 0 + 0 + + + + - - - - Qt::Vertical - - - QSizePolicy::Fixed - - + + + - 20 - 40 + 0 + 50 - - - - QFrame::StyledPanel QFrame::Raised - - - - - - 0 - 0 - + + + + + QFrame::StyledPanel - - - 0 - 80 - + + QFrame::Raised + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + - Open Sans - 16 + 18 75 true true - Accelerate - - - - :/Misc_icons/Misc_icons/speed-up-fill.svg:/Misc_icons/Misc_icons/speed-up-fill.svg + Demo Mode - - - 60 - 60 - + + + + + + - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 20 - 20 - - - - - + diff --git a/ui/Keypad.ui b/ui/Keypad.ui new file mode 100644 index 0000000..cda5e42 --- /dev/null +++ b/ui/Keypad.ui @@ -0,0 +1,316 @@ + + + HVAC + + + + 0 + 0 + 420 + 690 + + + + Form + + + *{ + border: none; + background-color: transparent; + background: none; + padding: 0; + margin: 0; + color: #fff; +} + +#scrollAreaWidgetContents{ + background-color: #131313 ; /* black */ +} + +#centralwidget{ + background-color: #131313 ; /* black */ +} + +#centralwidget QPushButton{ + background-color: #6C6C85 ; /* pastel purple */ + padding: 5px 10px; + border-radius: 10px; +} + +#centralwidget QPushButton:pressed { + background-color: #4BD7D6 ; /* light blue */ +} + +#centralwidget QSlider::groove:horizontal { + border: 1px solid #6C6C85 ; /* pastel purple */ + height: 15px; + border-radius: 8px; +} + +#centralwidget QSlider::sub-page:horizontal { + background-color: #4BD7D6 ; /* light blue */ + height: 15px; + border-radius: 5px; +} + +#centralwidget QSlider::handle:horizontal { + background: qlineargradient(x1:0, y1:0, x2:1, y2:1, + stop:0 #eee, stop:1 #ccc); + border: 1px solid #777; + width: 20px; + margin-top: -2px; + margin-bottom: -2px; + border-radius: 8px; +} + +QSlider::sub-page:vertical { + background-color: #131313 ; /* black */ + height: 20px; + width: 28px; + margin: 2px; + border: 1px solid #6C6C85 ; /* pastel purple */ + border-radius: 8px; +} + +QSlider::groove:vertical { + border-radius: 8px; + width: 28px; + margin: 2px; + border: 1px solid #6C6C85 ; /* pastel purple */ + background-color: #4BD7D6 ; /* light blue */ +} +QSlider::groove:vertical:hover { + background-color: rgb(55, 62, 76); +} +QSlider::handle:vertical { + background-color: #d5d5d5; + height: 15px; + width: 20px; + border-radius: 5px; +} +QSlider::handle:vertical:hover { + background-color: #6C6C85 ; /* pastel purple */ +} +QSlider::handle:vertical:pressed { + background-color: #4BD7D6 ; /* light blue */ +} + + +QScrollBar:horizontal { + min-width: 240px; + height: 13px; + } + + QScrollBar:vertical { + min-height: 240px; + width: 13px; + } + + QScrollBar::groove { + background: 2A2827; /* dark grey */ + border-radius: 5px; + } + + QScrollBar::handle { + background: #4BD7D6 ; /* light blue */ + border-radius: 5px; + } + + QScrollBar::handle:horizontal { + width: 25px; + } + + QScrollBar::handle:vertical { + height: 25px; scrollAreaWidgetContents + } + +#leftControls{ + background: #041F2B ; /* dark blue */ + border-radius: 10px; +} + +#rightControls{ + background: #041F2B ; /* dark blue */ + border-radius: 10px; +} + + +/*============================================*/ + +QListWidget { + min-height: 150px; + max-height: 150px; + background-color: #131313 ; /* black */ + border: 1px solid #6C6C85 ; /* pastel purple */ + border-radius: 8px; +} + +QListWidget::item { + height: 50px; + width: 50px; + border-radius: 8px; + text-align: center; +} + +QListWidget::item:selected { + background-color: #4BD7D6 ; /* light blue */ + border-radius: 8px; +} + +QListWidget::item:selected:!active { + border-width: 0px; +} + +QListWidget::item:selected:active { + background-color: #4BD7D6 ; /* light blue */ +} + +QListWidget::item:selected:!focus { + color: black; + border-width: 0px; +} + +QListWidget::item:focus { + background-color: #6C6C85 ; /* pastel purple */ +} + +QListWidget::item:hover { + background-color: #6C6C85 ; /* pastel purple */ +} + + + + + + + + QFrame::NoFrame + + + + + + QFrame::NoFrame + + + + + + + + + + :/Keypad_Images/keypad_icons/f2-unpressed.png:/Keypad_Images/keypad_icons/f2-unpressed.png + + + + 100 + 100 + + + + + + + + + + + + :/Keypad_Images/keypad_icons/f3-unpressed.png:/Keypad_Images/keypad_icons/f3-unpressed.png + + + + 100 + 100 + + + + + + + + + + + + :/Keypad_Images/keypad_icons/f1-unpressed.png:/Keypad_Images/keypad_icons/f1-unpressed.png + + + + 100 + 100 + + + + + + + + + + + + :/Keypad_Images/keypad_icons/f5-unpressed.png:/Keypad_Images/keypad_icons/f5-unpressed.png + + + + 100 + 100 + + + + + + + + + + + + :/Keypad_Images/keypad_icons/f4-unpressed.png:/Keypad_Images/keypad_icons/f4-unpressed.png + + + + 100 + 100 + + + + + + + + + 40 + 20 + + + + + + + + + + + QSizePolicy::Fixed + + + + 20 + 40 + + + + + + + + + + + + + + -- cgit 1.2.3-korg