diff options
Diffstat (limited to 'Widgets')
-rw-r--r-- | Widgets/HVACPage.py | 112 | ||||
-rw-r--r-- | Widgets/ICPage.py | 353 | ||||
-rw-r--r-- | Widgets/NavPage.py | 179 | ||||
-rw-r--r-- | Widgets/SteeringCtrlPage.py | 166 | ||||
-rw-r--r-- | Widgets/settings.py | 116 |
5 files changed, 926 insertions, 0 deletions
diff --git a/Widgets/HVACPage.py b/Widgets/HVACPage.py new file mode 100644 index 0000000..99ee798 --- /dev/null +++ b/Widgets/HVACPage.py @@ -0,0 +1,112 @@ +""" + Copyright 2023 Suchinton Chakravarty + + 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. +""" + +import os +import sys +from PyQt5 import uic +from PyQt5.QtWidgets import QApplication, QListWidget, QSlider, QPushButton + +current_dir = os.path.dirname(os.path.abspath(__file__)) + +# ======================================== + +sys.path.append(os.path.dirname(current_dir)) + +from extras.FeedKuksa import FeedKuksa + +Form, Base = uic.loadUiType(os.path.join(current_dir, "../ui/HVAC.ui")) + +# ======================================== + +class HVAC_Paths(): + def __init__(self): + self.leftTemp = "Vehicle.Cabin.HVAC.Station.Row1.Left.Temperature" + self.leftFanSpeed = "Vehicle.Cabin.HVAC.Station.Row1.Left.FanSpeed" + self.rightTemp = "Vehicle.Cabin.HVAC.Station.Row1.Right.Temperature" + self.rightFanSpeed = "Vehicle.Cabin.HVAC.Station.Row1.Right.FanSpeed" + + # temperatureList contains values from 32 to 16 + self.temperatureList = [str(i) + "°C" for i in range(32, 15, -1)] + +class HVACWidget(Base, Form): + def __init__(self, parent=None): + super(self.__class__, self).__init__(parent) + self.setupUi(self) + + self.HVAC = HVAC_Paths() + + self.feed_kuksa = FeedKuksa() + + self.leftTempList = self.findChild(QListWidget, "leftTempList") + self.leftTempList.addItems(self.HVAC.temperatureList) + self.leftTempList.setCurrentRow(0) + self.leftTempList.itemClicked.connect(self.leftTempListClicked) + self.leftTempList.itemSelectionChanged.connect(self.leftTempListClicked) + self.leftTempList.wheelEvent = lambda event: None + + self.rightTempList = self.findChild(QListWidget, "rightTempList") + self.rightTempList.addItems(self.HVAC.temperatureList) + self.rightTempList.setCurrentRow(0) + self.rightTempList.itemClicked.connect(self.rightTempListClicked) + self.rightTempList.itemSelectionChanged.connect(self.rightTempListClicked) + self.rightTempList.wheelEvent = lambda event: None + + self.leftTempUp = self.findChild(QPushButton, "leftTempUp") + self.leftTempUp.clicked.connect(lambda: self.leftTempList.setCurrentRow(self.leftTempList.currentRow() - 1)) + + self.leftTempDown = self.findChild(QPushButton, "leftTempDown") + self.leftTempDown.clicked.connect(lambda: self.leftTempList.setCurrentRow(self.leftTempList.currentRow() + 1)) + + self.rightTempUp = self.findChild(QPushButton, "rightTempUp") + self.rightTempUp.clicked.connect(lambda: self.rightTempList.setCurrentRow(self.rightTempList.currentRow() - 1)) + + self.rightTempDown = self.findChild(QPushButton, "rightTempDown") + self.rightTempDown.clicked.connect(lambda: self.rightTempList.setCurrentRow(self.rightTempList.currentRow() + 1)) + + self.leftFanSpeed_slider = self.findChild(QSlider, "leftFanSpeed_slider") + self.leftFanSpeed_slider.valueChanged.connect(self.leftFanSpeed_sliderChanged) + + self.rightFanSpeed_slider = self.findChild(QSlider, "rightFanSpeed_slider") + self.rightFanSpeed_slider.valueChanged.connect(self.rightFanSpeed_sliderChanged) + + def leftTempListClicked(self): + item = self.leftTempList.currentItem() + self.leftTempList.scrollToItem(item, 1) + self.feed_kuksa.send_values(self.HVAC.leftTemp, item.text()[:-2]) + print(item.text()) + + def rightTempListClicked(self): + item = self.rightTempList.currentItem() + self.rightTempList.scrollToItem(item, 1) + self.feed_kuksa.send_values(self.HVAC.rightTemp, item.text()[:-2]) + print(item.text()) + + def leftFanSpeed_sliderChanged(self): + value = self.leftFanSpeed_slider.value() + self.feed_kuksa.send_values(self.HVAC.leftFanSpeed, str(value)) + print(value) + + def rightFanSpeed_sliderChanged(self): + value = self.rightFanSpeed_slider.value() + self.feed_kuksa.send_values(self.HVAC.rightFanSpeed, str(value)) + print(value) + +if __name__ == '__main__': + import sys + app = QApplication(sys.argv) + w = HVACWidget() + w.show() + sys.exit(app.exec_()) diff --git a/Widgets/ICPage.py b/Widgets/ICPage.py new file mode 100644 index 0000000..64fa2d3 --- /dev/null +++ b/Widgets/ICPage.py @@ -0,0 +1,353 @@ +""" + Copyright 2023 Suchinton Chakravarty + + 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. +""" + +import os +import sys +from PyQt5 import uic, QtCore, QtWidgets +from PyQt5.QtWidgets import QApplication +from PyQt5.QtWidgets import QSlider, QLCDNumber, QPushButton +from PyQt5.QtGui import QIcon, QPixmap, QPainter +import time +from PyQt5.QtCore import QThread + +current_dir = os.path.dirname(os.path.abspath(__file__)) + +# ======================================== + +sys.path.append(os.path.dirname(current_dir)) + +from extras.FeedKuksa import FeedKuksa + +Form, Base = uic.loadUiType(os.path.join(current_dir, "../ui/IC.ui")) + +# ======================================== + + +class IC_Paths(): + def __init__(self): + self.speed = "Vehicle.Speed" + self.engineRPM = "Vehicle.Powertrain.CombustionEngine.Speed" + self.leftIndicator = "Vehicle.Body.Lights.DirectionIndicator.Left.IsSignaling" + self.rightIndicator = "Vehicle.Body.Lights.DirectionIndicator.Right.IsSignaling" + self.hazard = "Vehicle.Body.Lights.Hazard.IsSignaling" + self.fuelLevel = "Vehicle.Powertrain.FuelSystem.Level" + self.coolantTemp = "Vehicle.Powertrain.CombustionEngine.ECT" + self.selectedGear = "Vehicle.Powertrain.Transmission.SelectedGear" + + +class ICWidget(Base, Form): + def __init__(self, parent=None): + super(self.__class__, self).__init__(parent) + self.setupUi(self) + + self.IC = IC_Paths() + + self.feed_kuksa = FeedKuksa() + self.feed_kuksa.start() + + # # load the stylesheet + # theme = open(os.path.join(current_dir, "../ui/styles/Tron/ICPage.qss"), 'r') + # self.setStyleSheet(theme.read()) + # theme.close() + + self.scriptBtn = self.findChild(QPushButton, "scriptBtn") + + self.Speed_slider = self.findChild(QSlider, "Speed_slider") + self.Speed_monitor = self.findChild(QLCDNumber, "Speed_monitor") + self.RPM_slider = self.findChild(QSlider, "RPM_slider") + self.RPM_monitor = self.findChild(QLCDNumber, "RPM_monitor") + + self.coolantTemp_slider = self.findChild(QSlider, "coolantTemp_slider") + self.fuelLevel_slider = self.findChild(QSlider, "fuelLevel_slider") + + self.accelerationBtn = self.findChild(QPushButton, "accelerationBtn") + + self.leftIndicatorBtn = self.findChild(QPushButton, "leftIndicatorBtn") + self.rightIndicatorBtn = self.findChild(QPushButton, "rightIndicatorBtn") + self.hazardBtn = self.findChild(QPushButton, "hazardBtn") + + buttons = [self.parkBtn, + self.reverseBtn, + self.neutralBtn, + self.driveBtn] + + # group for the buttons for mutual exclusion + self.driveGroupBtns = QtWidgets.QButtonGroup(self) + self.driveGroupBtns.setExclusive(True) + + for button in buttons: + self.driveGroupBtns.addButton(button) + + self.driveGroupBtns.buttonClicked.connect(self.driveBtnClicked) + + self.scriptBtn.clicked.connect(self.scriptBtnClicked) + + self.Speed_slider.valueChanged.connect(self.update_Speed_monitor) + self.Speed_slider.setMinimum(0) + self.Speed_slider.setMaximum(240) + + self.RPM_slider.valueChanged.connect(self.update_RPM_monitor) + self.RPM_slider.setMinimum(0) + self.RPM_slider.setMaximum(8000) + + 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.leftIndicatorBtn.setCheckable(True) + self.rightIndicatorBtn.setCheckable(True) + self.hazardBtn.setCheckable(True) + + self.leftIndicatorBtn.clicked.connect(self.leftIndicatorBtnClicked) + self.rightIndicatorBtn.clicked.connect(self.rightIndicatorBtnClicked) + self.hazardBtn.clicked.connect(self.hazardBtnClicked) + + def scriptBtnClicked(self): + if self.scriptBtn.isChecked(): + ICScript.start_script() + + if not self.ScriptBtn.isChecked(): + ICScript.stop_script() + + def update_Speed_monitor(self): + speed = int(self.Speed_slider.value()) + self.Speed_monitor.display(speed) + self.feed_kuksa.send_values(self.IC.speed, str(speed), 'value') + + def update_RPM_monitor(self): + rpm = int(self.RPM_slider.value()) + self.RPM_monitor.display(rpm) + self.feed_kuksa.send_values(self.IC.engineRPM, str(rpm), 'value') + + def update_coolantTemp_monitor(self): + coolantTemp = int(self.coolantTemp_slider.value()) + self.feed_kuksa.send_values(self.IC.coolantTemp, str(coolantTemp), 'value') + + def update_fuelLevel_monitor(self): + fuelLevel = int(self.fuelLevel_slider.value()) + self.feed_kuksa.send_values(self.IC.fuelLevel, str(fuelLevel)) + + def hazardBtnClicked(self): + hazardIcon = QPixmap(":/Images/Images/hazard.png") + if self.hazardBtn.isChecked(): + painter = QPainter(hazardIcon) + painter.setCompositionMode(QPainter.CompositionMode_SourceIn) + painter.fillRect(hazardIcon.rect(), QtCore.Qt.yellow) + painter.end() + self.hazardBtn.setIcon(QIcon(hazardIcon)) + + self.leftIndicatorBtn.setChecked(True) + self.rightIndicatorBtn.setChecked(True) + self.feed_kuksa.send_values(self.IC.leftIndicator, "true") + self.feed_kuksa.send_values(self.IC.rightIndicator, "true") + self.feed_kuksa.send_values(self.IC.hazard, "true") + else: + painter = QPainter(hazardIcon) + painter.setCompositionMode(QPainter.CompositionMode_SourceIn) + painter.fillRect(hazardIcon.rect(), QtCore.Qt.black) + painter.end() + self.hazardBtn.setIcon(QIcon(hazardIcon)) + + self.leftIndicatorBtn.setChecked(False) + self.rightIndicatorBtn.setChecked(False) + self.feed_kuksa.send_values(self.IC.leftIndicator, "false") + self.feed_kuksa.send_values(self.IC.rightIndicator, "false") + self.feed_kuksa.send_values(self.IC.hazard, "false") + + def leftIndicatorBtnClicked(self): + leftIndicatorIcon = QPixmap(":/Images/Images/left.png") + if self.leftIndicatorBtn.isChecked(): + + painter = QPainter(leftIndicatorIcon) + painter.setCompositionMode(QPainter.CompositionMode_SourceIn) + painter.fillRect(leftIndicatorIcon.rect(), QtCore.Qt.green) + painter.end() + + self.leftIndicatorBtn.setIcon(QIcon(leftIndicatorIcon)) + self.feed_kuksa.send_values(self.IC.leftIndicator, "true") + else: + + painter = QPainter(leftIndicatorIcon) + painter.setCompositionMode(QPainter.CompositionMode_SourceIn) + painter.fillRect(leftIndicatorIcon.rect(), QtCore.Qt.black) + painter.end() + + self.leftIndicatorBtn.setIcon(QIcon(leftIndicatorIcon)) + self.feed_kuksa.send_values(self.IC.leftIndicator, "false") + + def rightIndicatorBtnClicked(self): + rightIndicatorIcon = QPixmap(":/Images/Images/right.png") + if self.rightIndicatorBtn.isChecked(): + + painter = QPainter(rightIndicatorIcon) + painter.setCompositionMode(QPainter.CompositionMode_SourceIn) + painter.fillRect(rightIndicatorIcon.rect(), QtCore.Qt.green) + painter.end() + self.rightIndicatorBtn.setIcon(QIcon(rightIndicatorIcon)) + self.feed_kuksa.send_values(self.IC.rightIndicator, "true") + else: + + painter = QPainter(rightIndicatorIcon) + painter.setCompositionMode(QPainter.CompositionMode_SourceIn) + painter.fillRect(rightIndicatorIcon.rect(), QtCore.Qt.black) + painter.end() + self.rightIndicatorBtn.setIcon(QIcon(rightIndicatorIcon)) + self.feed_kuksa.send_values(self.IC.rightIndicator, "false") + + def accelerationBtnPressed(self): + self.startTime = QtCore.QTime.currentTime() + self.acceleration_timer = QtCore.QTimer() + self.acceleration_timer.timeout.connect( + lambda: self.updateSpeedAndEngineRpm("Accelerate")) + self.acceleration_timer.start(100) + + def accelerationBtnReleased(self): + if self.Speed_slider.value() <= 0: + self.acceleration_timer.stop() + else: + self.acceleration_timer.timeout.connect( + lambda: self.updateSpeedAndEngineRpm("Decelerate")) + self.acceleration_timer.start(100) + + def updateSpeedAndEngineRpm(self, action, acceleration=(60/5)): + if action == "Accelerate": + pass + elif action == "Decelerate": + acceleration = -acceleration + + currentTime = QtCore.QTime.currentTime() + duration = self.startTime.msecsTo(currentTime) + self.current_speed = AccelerationFns.calculate_speed( + duration, acceleration) + self.current_rpm = AccelerationFns.calculate_engine_rpm( + self.current_speed) + + if self.current_speed <= 0: + self.current_speed = 0 + self.current_rpm = 0 + self.acceleration_timer.stop() + + if self.current_speed >= 240: + self.current_speed = 240 + self.current_rpm = 0 + self.acceleration_timer.stop() + + self.Speed_slider.setValue(self.current_speed) + self.RPM_slider.setValue(self.current_rpm) + + self.update_Speed_monitor() + self.update_RPM_monitor() + + def driveBtnClicked(self): + # // Selected Gear output = > 0 = Neutral, 1/2/.. = Forward, -1/.. = Reverse, 126 = Park, 127 = Drive + # #859287 ; /* light green */ + + if self.driveGroupBtns.checkedButton() == self.driveBtn: + self.accelerationBtn.setEnabled(True) + self.Speed_slider.setEnabled(True) + self.RPM_slider.setEnabled(True) + self.feed_kuksa.send_values(self.IC.selectedGear, "127") + + if self.driveGroupBtns.checkedButton() == self.parkBtn: + self.accelerationBtn.setEnabled(False) + self.Speed_slider.setEnabled(False) + self.RPM_slider.setEnabled(False) + self.feed_kuksa.send_values(self.IC.selectedGear, "126") + + if self.driveGroupBtns.checkedButton() == self.reverseBtn: + self.accelerationBtn.setEnabled(True) + self.Speed_slider.setEnabled(True) + self.RPM_slider.setEnabled(True) + self.feed_kuksa.send_values(self.IC.selectedGear, "-1") + + if self.driveGroupBtns.checkedButton() == self.neutralBtn: + self.accelerationBtn.setEnabled(False) + self.Speed_slider.setEnabled(False) + self.RPM_slider.setEnabled(True) + self.feed_kuksa.send_values(self.IC.selectedGear, "0") + + +class AccelerationFns(): + def calculate_speed(time, acceleration) -> int: + # acceleration = 60 / 5 # acceleration from 0 to 60 in 5 seconds + time = time / 1000 # convert milliseconds to seconds + speed = acceleration * time # calculate speed + return int(speed) + + def calculate_engine_rpm(speed) -> int: + wheel_diameter = 0.48 # in meters + wheel_circumference = wheel_diameter * 3.14 # in meters + + # Adjust the gear ratios to match the desired speed and rpm + gear_ratios = [3.36, 2.10, 1.48, 1.16, 0.95, 0.75] + speed = speed * 1000 / 3600 # Convert speed from km/h to m/s + wheel_rps = speed / wheel_circumference + + current_gear = None + for i in range(len(gear_ratios)): + if wheel_rps * gear_ratios[i] < 8000 / 60: + current_gear = i + 1 + break + + # If no gear is found, use the highest gear + if current_gear is None: + current_gear = len(gear_ratios) + + engine_rpm = wheel_rps * gear_ratios[current_gear - 1] * 60 + + return int(engine_rpm) + + +class ICScript(ICWidget): + def start_script(self): + ICWidget.reset() + + # disable all widgets in the scroll area + for widget in ICWidget.scrollAreaWidgetContents.children(): + widget.setEnabled(False) + + rates = [(60/5), (60/4), (60/3)] + + # start assigning values to the speed and rpm sliders and send them to the IC do this in a loop for each rate + for rate in rates: + ICWidget.accelerationBtnPressed() + ICWidget.acceleration_timer.timeout.connect( + lambda: ICWidget.updateSpeedAndEngineRpm("Accelerate"), rate) + ICWidget.acceleration_timer.start(100) + time.sleep(5) + ICWidget.accelerationBtnReleased() + ICWidget.acceleration_timer.timeout.connect( + lambda: ICWidget.updateSpeedAndEngineRpm("Decelerate"), rate) + ICWidget.acceleration_timer.start(100) + time.sleep(5) + + def stop_script(self): + ICWidget.reset() + + # enable all widgets in the scroll area + for widget in ICWidget.scrollAreaWidgetContents.children(): + widget.setEnabled(True) + +if __name__ == '__main__': + app = QApplication(sys.argv) + w = ICWidget() + w.show() + sys.exit(app.exec_())
\ No newline at end of file diff --git a/Widgets/NavPage.py b/Widgets/NavPage.py new file mode 100644 index 0000000..61d63aa --- /dev/null +++ b/Widgets/NavPage.py @@ -0,0 +1,179 @@ +""" + Copyright 2023 Suchinton Chakravarty + + 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. +""" + +import extras.Kuksa_Instance as kuksa_instance +import os +import sys +import requests +import folium +from PyQt5 import uic, QtCore + +from PyQt5.QtCore import QUrl, QObject +from PyQt5.QtCore import QThread, pyqtSignal, pyqtSlot +from PyQt5.QtWebEngineWidgets import QWebEngineView +from PyQt5.QtWidgets import QApplication, QListWidget, QLineEdit, QCompleter, QListView +from PyQt5.QtGui import QPainterPath, QRegion, QStandardItemModel, QStandardItem +from PyQt5.QtCore import QRectF +from PyQt5.QtCore import QTimer +from PyQt5.QtCore import Qt + +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/Nav.ui")) + +# ======================================== + + +class Nav_Paths(): + def __init__(self): + self.currLat = "Vehicle.CurrentLocation.Latitude" + self.currLng = "Vehicle.CurrentLocation.Longitude" + self.desLat = "Vehicle.Cabin.Infotainment.Navigation.DestinationSet.Latitude" + self.desLng = "Vehicle.Cabin.Infotainment.Navigation.DestinationSet.Longitude" + + +class NavWidget(Base, Form): + suggestionsUpdated = pyqtSignal(list) + + def __init__(self, parent=None): + super(self.__class__, self).__init__(parent) + self.setupUi(self) + + self.From_address = self.findChild(QLineEdit, "From_address") + self.To_address = self.findChild(QLineEdit, "To_address") + self.map_view = self.findChild(QWebEngineView, "map_view") + + path = QPainterPath() + path.addRoundedRect(QRectF(self.map_view.rect()), 10, 10) + mask = QRegion(path.toFillPolygon().toPolygon()) + self.map_view.setMask(mask) + + self.searching_thread = QThread() + + self.suggested_addresses = QStandardItemModel() + completer = CustomCompleter(self.suggested_addresses) + self.From_address.setCompleter(completer) + + self.From_address.textChanged.connect(self.delayed_search) + # self.To_address.textChanged.connect(lambda: self.start_search(self.To_address.text())) + + self.suggestionsUpdated.connect(self.update_suggestions) + + self.timer = QTimer() + self.timer.setInterval(500) # Adjust delay as needed + self.timer.setSingleShot(True) + self.timer.timeout.connect(self.start_search) + + def delayed_search(self): + self.timer.start() + + def start_search(self): + query = self.From_address.text().strip() + if query: + self.searching_thread.run = lambda: self.search_address(query) + self.searching_thread.start() + + def search_address(self, query): + options = self.fetch_address_suggestions(query) + self.suggestionsUpdated.emit(options) + + def fetch_address_suggestions(self, query): + url = f"https://nominatim.openstreetmap.org/search?format=json&limit=5&q={requests.utils.quote(query)}" + response = requests.get(url) + if response.status_code == 200: + return response.json() + else: + return [] + + def show_suggestions(self, options): + current_query = self.From_address.text().strip() + if current_query: + self.suggested_addresses.clear() + for suggestion in options: + address = suggestion.get("display_name", "") + self.suggested_addresses.appendRow(QStandardItem(address)) + + @pyqtSlot(list) + def update_suggestions(self, options): + self.show_suggestions(options) + + def select_suggestion(self, item): + address = item.text() + # self.address_input.setText(address) + coordinates = self.show_location(address) + + def show_location(self, query): + url = f"https://nominatim.openstreetmap.org/search?format=json&limit=1&q={requests.utils.quote(query)}" + response = requests.get(url) + if response.status_code == 200: + data = response.json() + if data: + lat = data[0]["lat"] + lon = data[0]["lon"] + location = [float(lat), float(lon)] + + self.update_map(location) + return location + + def update_map(self, location): + map_html = self.create_map_html(location) + file_path = os.path.abspath("map.html") + with open(file_path, "w") as f: + f.write(map_html) + self.map_view.load(QUrl.fromLocalFile(file_path)) + + def create_map_html(self, location): + map = folium.Map(location=location, zoom_start=15) + marker = folium.Marker(location=location) + marker.add_to(map) + return map._repr_html_() + + +class CustomCompleter(QCompleter): + def __init__(self, parent=None): + super().__init__(parent) + self.setPopup(QListView()) + self.popup().setStyleSheet(""" + QListView { + background-color: #131313 ; /* black */ + color: #fff; + border: 1px solid #4BD7D6 ; /* light blue */ + border-radius: 2px; + padding: 10px; + margin: 2px; + } + """) + + self.popup().setWindowFlags(Qt.Popup | Qt.FramelessWindowHint) + self.popup().setUniformItemSizes(True) + self.popup().setWordWrap(True) + self.popup().setSpacing(1) + self.popup().setFrameShape(QListView.NoFrame) + self.popup().setFrameShadow(QListView.Plain) + self.popup().setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + + +if __name__ == '__main__': + import sys + app = QApplication(sys.argv) + w = NavWidget() + w.show() + sys.exit(app.exec_()) diff --git a/Widgets/SteeringCtrlPage.py b/Widgets/SteeringCtrlPage.py new file mode 100644 index 0000000..61271c2 --- /dev/null +++ b/Widgets/SteeringCtrlPage.py @@ -0,0 +1,166 @@ +""" + Copyright 2023 Suchinton Chakravarty + + 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. +""" + +import os +import sys +from PyQt5 import uic +from PyQt5.QtWidgets import QApplication, QButtonGroup +from PyQt5.QtCore import QThread + +import time + +current_dir = os.path.dirname(os.path.abspath(__file__)) + +# ======================================== + +sys.path.append(os.path.dirname(current_dir)) + +import extras.Kuksa_Instance as kuksa_instance + +Form, Base = uic.loadUiType(os.path.join(current_dir, "../ui/SteeringControls.ui")) + +# ======================================== + +class Steering_Paths(): + def __init__(self): + self.VolumeUp = "Vehicle.Cabin.SteeringWheel.Switches.VolumeUp" + self.VolumeDown = "Vehicle.Cabin.SteeringWheel.Switches.VolumeDown" + self.VolumeMute = "Vehicle.Cabin.SteeringWheel.Switches.VolumeMute" + + self.Mode = "Vehicle.Cabin.SteeringWheel.Switches.Mode" + + self.NextTrack = "Vehicle.Cabin.SteeringWheel.Switches.Next" + self.PreviousTrack = "Vehicle.Cabin.SteeringWheel.Switches.Previous" + + self.Info = "Vehicle.Cabin.SteeringWheel.Switches.Info" + + self.PhoneCall = "Vehicle.Cabin.SteeringWheel.Switches.PhoneCall" + self.PhoneHangup = "Vehicle.Cabin.SteeringWheel.Switches.PhoneHangup" + self.Voice = "Vehicle.Cabin.SteeringWheel.Switches.Voice" + self.LaneDeparture = "Vehicle.Cabin.SteeringWheel.Switches.LaneDepartureWarning" + + self.Horn = "Vehicle.Cabin.SteeringWheel.Switches.Horn" + + self.CruiseEnable = "Vehicle.Cabin.SteeringWheel.Switches.CruiseEnable" + self.CruseSet = "Vehicle.Cabin.SteeringWheel.Switches.CruiseSet" + self.CruiseResume = "Vehicle.Cabin.SteeringWheel.Switches.CruiseResume" + self.CruiseCancel = "Vehicle.Cabin.SteeringWheel.Switches.CruiseCancel" + + self.CruiseLimit = "Vehicle.Cabin.SteeringWheel.Switches.CruiseLimit" + self.CruiseDistance = "Vehicle.Cabin.SteeringWheel.Switches.CruiseDistance" + +class SteeringCtrlWidget(Base, Form): + def __init__(self, parent=None): + super(self.__class__, self).__init__(parent) + self.setupUi(self) + + self.Steering = Steering_Paths() + self.feed_kuksa = FeedKuksa() + self.add_buttons() + + def add_buttons(self): + + # Define button groups and actions + LeftControlsBtns = [self.VolUpBtn, + self.VolDownBtn, + self.ModeBtn, + self.VolMuteBtn, + self.NextTrackBtn, + self.PrevTrackBtn, + self.InfoBtn] + + + PhoneBtns = [self.PhoneCallBtn, self.PhoneHangupBtn] + ExtraContolsBtns = [self.VoiceBtn, self.LaneDepartureBtn] + + RightControlsBtns = [self.CruiseEnableBtn, + self.CruiseSetBtn, + self.CruiseResumeBtn, + self.CruiseCancelBtn, + self.CruiseLimitBtn, + self.CruiseDistanceBtn] + + self.LeftControlsBtnsGroup = QButtonGroup() + self.PhoneBtnsGroup = QButtonGroup() + self.ExtraContolsBtnsGroup = QButtonGroup() + self.RiqhtControlsBtnsGroup = QButtonGroup() + + for btn in LeftControlsBtns: + self.LeftControlsBtnsGroup.addButton(btn) + + for btn in PhoneBtns: + self.PhoneBtnsGroup.addButton(btn) + + + for btn in RightControlsBtns: + self.RiqhtControlsBtnsGroup.addButton(btn) + + self.LeftControlsBtnsGroup.buttonClicked.connect(self.left_controls_clicked) + self.RiqhtControlsBtnsGroup.buttonClicked.connect(self.right_controls_clicked) + + self.HornBtn.clicked.connect(self.horn_clicked) + + def left_controls_clicked(self): + print("Left controls clicked") + + def right_controls_clicked(self): + print("Right controls clicked") + + def horn_clicked(self): + print("Horn clicked") + +class FeedKuksa(QThread): + def __init__(self, parent=None): + QThread.__init__(self,parent) + self.stop_flag = False + self.set_instance() + + def run(self): + print("Starting thread") + self.set_instance() + while not self.stop_flag: + self.send_values() + + def stop(self): + self.stop_flag = True + print("Stopping thread") + + def set_instance(self): + self.kuksa = kuksa_instance.KuksaClientSingleton.get_instance() + self.client = self.kuksa.get_client() + + def send_values(self, Path=None, Value=None, Attribute=None): + if self.client is not None: + if self.client.checkConnection() is True: + + if Attribute is not None: + self.client.setValue(Path, Value, Attribute) + else: + self.client.setValue(Path, Value) + else: + print("Could not connect to Kuksa") + self.set_instance() + else: + print("Kuksa client is None, try reconnecting") + time.sleep(2) + self.set_instance() + +if __name__ == '__main__': + import sys + app = QApplication(sys.argv) + w = SteeringCtrlWidget() + w.show() + sys.exit(app.exec_())
\ No newline at end of file diff --git a/Widgets/settings.py b/Widgets/settings.py new file mode 100644 index 0000000..11800a2 --- /dev/null +++ b/Widgets/settings.py @@ -0,0 +1,116 @@ +# Copyright 2023 Suchinton Chakravarty +# +# 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. + +import extras.Kuksa_Instance as kuksa_instance +import os +import sys +import time +from PyQt5 import uic +from PyQt5.QtWidgets import QApplication, QLineEdit, QPushButton, QLabel, QCheckBox + +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/Settings_Window.ui")) + +# ======================================== + + +class settings(Base, Form): + def __init__(self, parent=None): + super(self.__class__, self).__init__(parent) + self.setupUi(self) + + self.SSLToggle = self.findChild(QCheckBox, "SSLToggle") + + self.connectionStatus = self.findChild(QLabel, "connectionStatus") + self.connectionLogo = self.findChild(QLabel, "connectionLogo") + + self.IPAddrInput = self.findChild(QLineEdit, "IPAddrInput") + self.tokenPathInput = self.findChild(QLineEdit, "tokenPathInput") + + self.reconnectBtn = self.findChild(QPushButton, "reconnectBtn") + self.refreshBtn = self.findChild(QPushButton, "refreshBtn") + self.startClientBtn = self.findChild(QPushButton, "startClientBtn") + + self.startClientBtn.clicked.connect(self.set_instance) + self.reconnectBtn.clicked.connect(self.reconnectClient) + self.refreshBtn.clicked.connect(self.refreshStatus) + + self.refreshStatus() + self.show() + + def set_instance(self): + self.kuksa = kuksa_instance.KuksaClientSingleton.get_instance() + self.client = self.kuksa.get_client() + + self.config = self.kuksa.get_config() + self.token = self.kuksa.get_token() + + self.IPAddrInput.setText(self.config["ip"]) + self.SSLToggle.setChecked(self.config["insecure"]) + self.tokenPathInput.setText(self.token) + + time.sleep(2) + + if (self.client is None): + self.connectionStatus.setText('Not Connected') + self.connectionLogo.setStyleSheet("background-color: red") + + self.refreshStatus() + + def refreshStatus(self): + try: + if (self.client is None): + self.connectionStatus.setText('Not Connected') + self.connectionLogo.setStyleSheet("background-color: red") + return None + + if (self.client.checkConnection() == True): + self.connectionStatus.setText('Connected') + self.connectionLogo.setPixmap(":/icons/feather/check-circle.svg") + self.connectionLogo.setStyleSheet("background-color: green") + self.client.start() + return True + + if (self.client.checkConnection() == False): + self.client.stop() + self.connectionStatus.setText('Disconnected') + self.connectionLogo.setStyleSheet("background-color: yellow") + return False + except: + pass + + def reconnectClient(self): + try: + self.config["ip"] = self.IPAddrInput.text() + self.config["insecure"] = self.SSLToggle.isChecked() + self.token = self.tokenPathInput.text() + self.client = self.kuksa.reconnect_client(self.config, self.token) + self.client.start() + self.refreshStatus() + except Exception as e: + print(e) + +if __name__ == '__main__': + import sys + app = QApplication(sys.argv) + w = settings() + w.show() + sys.exit(app.exec_()) |