diff options
author | Suchinton Chakravarty <suchinton.2001@gmail.com> | 2024-10-15 23:57:36 +0530 |
---|---|---|
committer | Suchinton Chakravarty <suchinton.2001@gmail.com> | 2024-10-22 16:09:43 +0530 |
commit | 6fdddee7d45206dc64eacd08700f79566ad9b4a6 (patch) | |
tree | 7d688595f69cdb7d7484f37542059d7406da28c5 /Widgets | |
parent | 2e2dd79e41b27a1d50be3c118955cdbd76e65539 (diff) |
Fix Visual Bugs and Add options for Keypad input
- Fixed spin wheel input alignment for HVAC controls
- Minor tweaks to Gauge input, Added new tick marks and improved gradient
- Adding option(s) in config to handle Keypad input settings
- Reconnect QML signals to enable two way input for Speed, RPM and other QML elements
- Refactor and Add CLI option to start and stop playback.
- Make Tire Pressure Dock into floating window and align to screen center.
- Update resources to include keypad icons.
- Add new tile for Keypad inputs
Bug-AGL: SPEC-5161
Change-Id: I1ecefdfd114ecad081c138e74c1598907d91fd23
Signed-off-by: Suchinton Chakravarty <suchinton.2001@gmail.com>
Diffstat (limited to 'Widgets')
-rw-r--r-- | Widgets/Dashboard.py | 11 | ||||
-rw-r--r-- | Widgets/HVACPage.py | 13 | ||||
-rw-r--r-- | Widgets/ICPage.py | 188 | ||||
-rw-r--r-- | Widgets/Keypad.py | 56 |
4 files changed, 199 insertions, 69 deletions
diff --git a/Widgets/Dashboard.py b/Widgets/Dashboard.py index 9df290f..b8dfc92 100644 --- a/Widgets/Dashboard.py +++ b/Widgets/Dashboard.py @@ -57,6 +57,7 @@ class Dashboard(Base, Form): Dashboard_tiles = (self.DB_IC_Tile, self.DB_HVAC_Tile, self.DB_Steering_Tile, + self.DB_Keypad_Tile, self.DB_Settings_Tile) DashboardTiles = QtWidgets.QButtonGroup(self) @@ -73,6 +74,12 @@ class Dashboard(Base, Form): self.DB_Steering_Tile.setEnabled(False) self.DB_Steering_Tile.setStyleSheet("background-color : darkGray; color: gray") enabled = False + if tile == self.DB_Keypad_Tile and not config.keypad_enabled(): + self.DB_Keypad_Tile.setEnabled(False) + # hide the keypad tile + self.DB_Keypad_Tile.hide() + self.DB_Keypad_Tile.setStyleSheet("background-color : darkGray; color: gray") + enabled = False self.set_icon(tile, 90) DashboardTiles.addButton(tile) @@ -120,8 +127,10 @@ class Dashboard(Base, Form): self.parent().setCurrentIndex(2) elif tile == self.DB_Steering_Tile: self.parent().setCurrentIndex(3) - elif tile == self.DB_Settings_Tile: + elif tile == self.DB_Keypad_Tile: self.parent().setCurrentIndex(4) + elif tile == self.DB_Settings_Tile: + self.parent().setCurrentIndex(5) self.tileClickedSignal.emit() diff --git a/Widgets/HVACPage.py b/Widgets/HVACPage.py index 8371dfe..0a72866 100644 --- a/Widgets/HVACPage.py +++ b/Widgets/HVACPage.py @@ -6,11 +6,11 @@ import os import sys from PyQt6 import uic -from PyQt6.QtWidgets import QApplication, QFrame, QSlider, QPushButton, QWidget +from PyQt6.QtWidgets import QApplication, QFrame, QSlider, QWidget from PyQt6 import QtWidgets from PyQt6.QtQuickWidgets import QQuickWidget -from PyQt6.QtQuick import QQuickItem from PyQt6.QtCore import QUrl +from PyQt6.QtCore import QTimer current_dir = os.path.dirname(os.path.abspath(__file__)) @@ -69,7 +69,6 @@ class HVACWidget(Base, Form): # Create left temperature tumbler self.LeftTempTumbler = TumblerWidget() - # Connect the left tumbler signal to the handler self.LeftTempTumbler.rootObject().valueChanged.connect(self.onLeftTempChanged) @@ -96,6 +95,14 @@ class HVACWidget(Base, Form): QSlider, "rightFanSpeed_slider") self.rightFanSpeed_slider.valueChanged.connect( self.rightFanSpeed_sliderChanged) + + # after 2 seconds reconnect the signals + QTimer.singleShot(500, self.reconnectSignals) + + # funcyion to reconnect the tumbler signals + def reconnectSignals(self): + self.LeftTempTumbler.rootObject().valueChanged.connect(self.onLeftTempChanged) + self.RightTempTumbler.rootObject().valueChanged.connect(self.onRightTempChanged) def changeTemperature(self, tumbler_widget, change): """Change tumbler value by incrementing or decrementing.""" diff --git a/Widgets/ICPage.py b/Widgets/ICPage.py index 0f44703..a298d2b 100644 --- a/Widgets/ICPage.py +++ b/Widgets/ICPage.py @@ -12,6 +12,7 @@ from PyQt6.QtGui import QIcon, QPixmap, QPainter from PyQt6.QtCore import QObject, pyqtSignal, QThread from PyQt6.QtWidgets import QWidget, QFrame, QDockWidget from PyQt6.QtQuickWidgets import QQuickWidget +from PyQt6.QtCore import QTimer import threading current_dir = os.path.dirname(os.path.abspath(__file__)) @@ -65,6 +66,8 @@ def Gauge(gaugeType): gauge.setResizeMode(QQuickWidget.ResizeMode.SizeRootObjectToView) gauge.setSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding) + + gauge.rootContext().setContextObject(gauge) return gauge @@ -139,7 +142,6 @@ class ICWidget(Base, Form): 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) @@ -148,7 +150,6 @@ class ICWidget(Base, Form): 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) @@ -164,11 +165,6 @@ class ICWidget(Base, Form): self.Coolant_Gauge_Frame.layout().replaceWidget( coolant_Gauge_Placeholder, self.Coolant_Gauge) - self.coolantTemp_slider.valueChanged.connect( - self.update_coolantTemp_monitor) - self.fuelLevel_slider.valueChanged.connect( - self.update_fuelLevel_monitor) - self.leftIndicatorBtn.setCheckable(True) self.rightIndicatorBtn.setCheckable(True) self.hazardBtn.setCheckable(True) @@ -190,6 +186,26 @@ class ICWidget(Base, Form): QtWidgets.QPushButton, "TirePressureBtn") self.TirePressureBtn.clicked.connect(self.toggle_TirePressureDock) + # after 2 seconds reconnect the signals + QTimer.singleShot(500, self.reconnectSignals) + + # hide Tirepressure dock by default + self.Hide_TirePressure(True) + + # function to reconnect all the gauge signals + def reconnectSignals(self): + self.Speed_Gauge.rootObject().speedValueChanged.connect(lambda value: self.update_Speed("gauge", value)) + self.Speed_slider.valueChanged.connect(lambda : self.update_Speed("slider", self.Speed_slider.value())) + + self.RPM_Gauge.rootObject().rpmValueChanged.connect(lambda value: self.update_RPM("gauge", value)) + self.RPM_slider.valueChanged.connect(lambda : self.update_RPM("slider", self.RPM_slider.value())) + + self.Coolant_Gauge.rootObject().coolantTempValueChanged.connect(lambda value: self.update_coolantTemp("gauge", value)) + self.coolantTemp_slider.valueChanged.connect(lambda : self.update_coolantTemp("slider", self.coolantTemp_slider.value())) + + self.Fuel_Gauge.rootObject().fuelLevelValueChanged.connect(lambda value: self.update_fuelLevel("gauge", value)) + self.fuelLevel_slider.valueChanged.connect(lambda : self.update_fuelLevel("slider", self.fuelLevel_slider.value())) + def toggle_TirePressureDock(self): if self.TirePressureBtn.isChecked(): self.Hide_TirePressure(True) @@ -233,58 +249,126 @@ class ICWidget(Base, Form): self.rightIndicatorBtn.setChecked(False) def set_Vehicle_Speed(self, speed): - self.Speed_slider.setValue(speed) + self.Speed_Gauge.rootObject().setProperty('value', speed) def set_Vehicle_RPM(self, rpm): - self.RPM_slider.setValue(rpm) + self.RPM_Gauge.rootObject().setProperty('value', rpm) - def update_Speed_gauge(self): + def update_Speed(self, source, value): """ - Updates the speed monitor with the current speed value. + Updates the speed value with the current value from either the gauge or the slider. + + Parameters: + value: The speed value to update. + source: A string indicating the source of the value ('gauge' or 'slider'). """ - speed = int(self.Speed_slider.value()) - speed = speed / 240 - self.Speed_Gauge.rootObject().setProperty('value', speed) + + speed = int(value) + + if source == 'gauge': + # Update slider to reflect the gauge's value + self.Speed_slider.blockSignals(True) + self.Speed_slider.setValue(speed) + print(speed) + self.Speed_slider.blockSignals(False) + + elif source == 'slider': + # Set animation duration to 0 for immediate update + self.Speed_Gauge.rootObject().setProperty('animationDuration', 0) + + # Update gauge to reflect the slider's value + self.Speed_Gauge.blockSignals(True) + self.Speed_Gauge.rootObject().setProperty('value', speed) + self.Speed_Gauge.blockSignals(False) + + # Reset animation duration for smooth transitions + self.Speed_Gauge.rootObject().setProperty('animationDuration', 500) + + # Send updated speed to kuksa if simulator is not running if not self.simulator_running: try: self.kuksa_client.set(self.IC.speed, str(speed), 'value') except Exception as e: logging.error(f"Error sending values to kuksa {e}") - def update_RPM_monitor(self): + def update_RPM(self, source, value): """ - Updates the RPM monitor with the current RPM value. + Updates the RPM value with the current value from either the gauge or the slider. + + Parameters: + value: The RPM value to update. + source: A string indicating the source of the value ('gauge' or 'slider'). """ - rpm = int(self.RPM_slider.value()) - rpm = rpm / 8000 - self.RPM_Gauge.rootObject().setProperty('value', rpm) + + rpm = int(value) + + if source == 'gauge': + # Update slider to reflect the gauge's value + self.RPM_slider.blockSignals(True) + self.RPM_slider.setValue(int(rpm)) + self.RPM_slider.blockSignals(False) + + elif source == 'slider': + # Set animation duration to 0 for immediate update + self.RPM_Gauge.rootObject().setProperty('animationDuration', 0) + + # Update gauge to reflect the slider's value + self.RPM_Gauge.blockSignals(True) + self.RPM_Gauge.rootObject().setProperty('value', rpm) + self.RPM_Gauge.blockSignals(False) + + # Reset animation duration for smooth transitions + self.RPM_Gauge.rootObject().setProperty('animationDuration', 1000) + + # Send updated RPM to kuksa if simulator is not running if not self.simulator_running: try: self.kuksa_client.set(self.IC.engineRPM, str(rpm), 'value') except Exception as e: logging.error(f"Error sending values to kuksa {e}") - def update_coolantTemp_monitor(self): + def update_coolantTemp(self, source, value): """ - Updates the coolant temperature monitor with the current coolant temperature value. + Updates the coolant temperature with the current coolant temperature value from the gauge. or the slider. """ - coolantTemp = int(self.coolantTemp_slider.value()) - gaugeValue = coolantTemp / 100 - self.Coolant_Gauge.rootObject().setProperty('value', gaugeValue) + coolantTemp = int(value) + + if source == 'gauge': + # Update slider to reflect the gauge's value + self.coolantTemp_slider.blockSignals(True) + self.coolantTemp_slider.setValue(coolantTemp) + self.coolantTemp_slider.blockSignals(False) + elif source == 'slider': + # Update gauge to reflect the slider's value + self.Coolant_Gauge.rootObject().setProperty('animationDuration', 0) + self.Coolant_Gauge.blockSignals(True) + self.Coolant_Gauge.rootObject().setProperty('value', coolantTemp) + self.Coolant_Gauge.blockSignals(False) + self.Coolant_Gauge.rootObject().setProperty('animationDuration', 1000) try: self.kuksa_client.set( self.IC.coolantTemp, str(coolantTemp), 'value') except Exception as e: logging.error(f"Error sending values to kuksa {e}") - def update_fuelLevel_monitor(self): + def update_fuelLevel(self, source, value): """ - Updates the fuel level monitor with the current fuel level value. + Updates the fuel level with the current fuel level value from the gauge. or the slider. """ - fuelLevel = int(self.fuelLevel_slider.value()) - FuelGaugeLevel = fuelLevel / 100 - self.Fuel_Gauge.rootObject().setProperty('value', FuelGaugeLevel) - print(self.Fuel_Gauge.rootObject().property('value')) + fuelLevel = int(value) + + if source == 'gauge': + # Update slider to reflect the gauge's value + self.fuelLevel_slider.blockSignals(True) + self.fuelLevel_slider.setValue(fuelLevel) + self.fuelLevel_slider.blockSignals(False) + elif source == 'slider': + # Update gauge to reflect the slider's value + self.Fuel_Gauge.rootObject().setProperty('animationDuration', 0) + self.Fuel_Gauge.blockSignals(True) + self.Fuel_Gauge.rootObject().setProperty('value', fuelLevel) + self.Fuel_Gauge.blockSignals(False) + self.Fuel_Gauge.rootObject().setProperty('animationDuration', 1000) try: self.kuksa_client.set(self.IC.fuelLevel, str(fuelLevel)) except Exception as e: @@ -394,20 +478,23 @@ class ICWidget(Base, Form): self.Script_toggle.showError() self.Script_toggle.setChecked(False) return - try: - self.Playback = CAN_playback() - self.Playback_connections() - except Exception as e: - self.Script_toggle.showError() - logging.error(f"Error creating playback object {e}") - return if self.Script_toggle.isChecked(): - # start the playback thread - self.thread = QThread() - self.Playback.moveToThread(self.thread) - self.thread.started.connect(self.Playback.playback_messages) - self.thread.start() + # self.Playback.start() + print("Playback started") + + try: + if self.Playback is None: + self.Playback = CAN_playback() + except Exception as e: + logging.error(f"Error creating playback object {e}") + self.Script_toggle.showError() + return + + if not self.Playback.isRunning(): + self.Playback.start_playback() + else: + self.Playback_connections() # hide sliders from the layout, their space will be taken by the playback widgets self.Speed_slider.hide() self.RPM_slider.hide() @@ -415,12 +502,15 @@ class ICWidget(Base, Form): # set default values for coolent and fuel self.coolantTemp_slider.setValue(90) self.fuelLevel_slider.setValue(50) - - elif not self.Script_toggle.isChecked(): - self.Playback.stop() - self.thread.quit() - self.thread.wait() - + else: + # self.Playback.stop_and_join() + if self.Playback and self.Playback.isRunning: + self.Playback.stop_playback() + self.Playback.wait() + #self.Playback.finished.connect(self.Playback.deleteLater) + self.Playback = None + + print("Playback stopped") self.Speed_slider.show() self.RPM_slider.show() @@ -471,7 +561,7 @@ class ICWidget(Base, Form): self.RPM_slider.setValue(self.current_rpm) self.update_Speed_gauge() - self.update_RPM_monitor() + self.update_RPM_Gauge() def driveBtnClicked(self): gear_mapping = { diff --git a/Widgets/Keypad.py b/Widgets/Keypad.py index 264bb5d..158849e 100644 --- a/Widgets/Keypad.py +++ b/Widgets/Keypad.py @@ -1,10 +1,9 @@ import os import sys from PyQt6 import uic -from PyQt6.QtWidgets import QPushButton -from PyQt6.QtWidgets import QApplication, QWidget +from PyQt6.QtWidgets import QToolButton, QApplication, QWidget +from PyQt6.QtGui import QPixmap, QIcon import requests -from urllib.parse import urljoin current_dir = os.path.dirname(os.path.abspath(__file__)) sys.path.append(os.path.dirname(current_dir)) @@ -12,30 +11,55 @@ sys.path.append(os.path.dirname(current_dir)) Form, Base = uic.loadUiType(os.path.join(current_dir, "../ui/Keypad.ui")) import res_rc +from extras import config 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 + # Load configuration + self.host, self.port = config.get_keypad_config() + + frame = self.findChild(QWidget, "frame") + + # Mapping of keys to API endpoints and button labels 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 + "Key_1": ("/cgi-bin/flutter.cgi", "Flutter Demo"), + "Key_2": ("/cgi-bin/qt.cgi", "Qt Demo"), + "Key_3": ("/cgi-bin/momi.cgi", "Momi Demo"), + "Key_4": ("/cgi-bin/bomb.cgi", "Bomb Demo") } - # Connect all keys to the same slot - for key_name in self.key_endpoints.keys(): - key_button = self.findChild(QPushButton, key_name) + # Connect all keys to the same slot and configure buttons + for key_name, (endpoint, label) in self.key_endpoints.items(): + key_button = self.findChild(QToolButton, key_name) + key_button.setObjectName(key_name) # Set button name + + # Set button label + key_button.setText(label) + # set font size and style based on height of the button + font = key_button.font() + font.setPointSize(int(key_button.height())) + font.setBold(True) + font.setItalic(True) + key_button.setFont(font) + + # place the label under the icon + # key_button.setIconSize(key_button.size()) + + + # Connect button click to API trigger key_button.clicked.connect(lambda _, x=key_name: self.trigger_api(x)) + # Hide unused keys based on configuration + keys_to_hide = config.get_keypad_keys_to_hide() + for key_name in keys_to_hide: + key_button = self.findChild(QToolButton, "Key_" + key_name) + key_button.hide() + def trigger_api(self, key_name): - endpoint = self.key_endpoints[key_name] + endpoint = self.key_endpoints[key_name][0] # Get the endpoint from the tuple if endpoint: url = f"http://{self.host}:{self.port}{endpoint}" try: @@ -51,4 +75,4 @@ if __name__ == '__main__': app = QApplication(sys.argv) w = KeypadWidget() w.show() - sys.exit(app.exec()) + sys.exit(app.exec())
\ No newline at end of file |