SwapStation_Dashboard/ui/widgets.py

225 lines
9.5 KiB
Python

# --- REPLACE the entire content of ui/widgets.py with this ---
from PyQt6.QtWidgets import (
QGroupBox, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QFrame, QPushButton, QFormLayout, QSizePolicy
)
from PyQt6.QtCore import Qt, pyqtSignal
from PyQt6.QtGui import QFont
class ChamberWidget(QGroupBox):
# Signals for button presses
open_door_requested = pyqtSignal()
chg_on_requested = pyqtSignal()
chg_off_requested = pyqtSignal()
def __init__(self, title="CHAMBER - X", scale=1.0):
super().__init__(title)
self.setObjectName("ChamberWidget")
self.setFont(QFont("Arial", max(8, int(9 * scale)), QFont.Weight.Bold))
main_layout = QVBoxLayout(self)
main_layout.setSpacing(max(2, int(4 * scale)))
# --- Battery ID ---
id_layout = QHBoxLayout()
id_layout.addWidget(QLabel("BAT ID: "))
self.id_field = self._create_data_field(scale)
self.id_field.setObjectName("BatIdField")
id_layout.addWidget(self.id_field)
main_layout.addLayout(id_layout)
# --- Main Data Columns ---
columns_layout = QHBoxLayout()
# == Left Column: Battery Info ==
battery_form_layout = QFormLayout()
battery_form_layout.setVerticalSpacing(max(2, int(4 * scale)))
self.battery_status_label = self._create_status_label("ABSENT", scale)
self.battery_status_label.setProperty("status", "absent")
self.soc_field = self._create_data_field(scale)
self.soc_field.setObjectName("DataField")
self.voltage_field = self._create_data_field(scale)
self.voltage_field.setObjectName("DataField")
self.temp_field = self._create_data_field(scale)
self.temp_field.setObjectName("DataField")
self.battery_fault_field = self._create_data_field(scale)
self.battery_fault_field.setObjectName("DataField")
battery_form_layout.addRow("Status:", self.battery_status_label)
battery_form_layout.addRow("SOC:", self.soc_field)
battery_form_layout.addRow("Voltage:", self.voltage_field)
battery_form_layout.addRow("Temp:", self.temp_field)
battery_form_layout.addRow("Fault:", self.battery_fault_field)
# == Separator ==
separator_line = QFrame()
separator_line.setFrameShape(QFrame.Shape.VLine)
separator_line.setFrameShadow(QFrame.Shadow.Sunken)
# == Right Column: Charger Info ==
charger_form_layout = QFormLayout()
charger_form_layout.setVerticalSpacing(max(2, int(4 * scale)))
self.charger_status_label = self._create_status_label("OFF", scale)
self.charger_status_label.setProperty("status", "absent")
self.chg_temp_field = self._create_data_field(scale)
self.chg_temp_field.setObjectName("DataField")
self.charger_fault_field = self._create_data_field(scale)
self.charger_fault_field.setObjectName("DataField")
self.current_field = self._create_data_field(scale)
self.current_field.setObjectName("DataField")
self.slot_temp_field = self._create_data_field(scale)
self.slot_temp_field.setObjectName("DataField")
charger_form_layout.addRow("Chg Status:", self.charger_status_label)
charger_form_layout.addRow("Chg Temp:", self.chg_temp_field)
charger_form_layout.addRow("Fault:", self.charger_fault_field)
charger_form_layout.addRow("Current:", self.current_field)
charger_form_layout.addRow("Slot Temp:", self.slot_temp_field)
columns_layout.addLayout(battery_form_layout)
columns_layout.addWidget(separator_line)
columns_layout.addLayout(charger_form_layout)
main_layout.addLayout(columns_layout)
main_layout.addStretch()
# --- Door status and buttons are on one line ---
button_layout = QHBoxLayout()
# Part 1: Door Status (fixed size on the left)
button_layout.addWidget(QLabel("Door Status:"))
self.door_status_field = self._create_data_field(scale)
self.door_status_field.setObjectName("DoorStatusField")
self.door_status_field.setMaximumWidth(int(140 * scale))
button_layout.addWidget(self.door_status_field)
self.door_status_field.setAlignment(Qt.AlignmentFlag.AlignCenter)
# Part 2: Buttons
self.open_door_btn = QPushButton("OPEN DOOR")
self.chg_on_btn = QPushButton("CHG ON")
self.chg_off_btn = QPushButton("CHG OFF")
self.open_door_btn.setObjectName("ChamberOpenDoorButton")
self.chg_on_btn.setObjectName("ChamberChgOnButton")
self.chg_off_btn.setObjectName("ChamberChgOffButton")
self.open_door_btn.clicked.connect(self.open_door_requested.emit)
self.chg_on_btn.clicked.connect(self.chg_on_requested.emit)
self.chg_off_btn.clicked.connect(self.chg_off_requested.emit)
button_layout.addWidget(self.open_door_btn)
button_layout.addWidget(self.chg_on_btn)
button_layout.addWidget(self.chg_off_btn)
main_layout.addLayout(button_layout)
def _create_status_label(self, text, scale):
label = QLabel(text)
label.setAlignment(Qt.AlignmentFlag.AlignCenter)
label.setFont(QFont("Arial", max(7, int(8 * scale)), QFont.Weight.Bold))
return label
def _create_data_field(self, scale):
field = QLineEdit(" ")
field.setReadOnly(True)
field.setFont(QFont("Arial", max(7, int(8 * scale))))
return field
def update_data(self, data):
# --- Get the status of the battery and charger first ---
battery_is_present = data.get("batteryPresent") == 1
charger_is_present = data.get("chargerPresent") == 1
# --- Update Battery Status Label ---
if battery_is_present:
self.battery_status_label.setText("PRESENT")
self.battery_status_label.setProperty("status", "present")
else:
self.battery_status_label.setText("ABSENT")
self.battery_status_label.setProperty("status", "absent")
# --- Update Charger Status Label ---
if charger_is_present:
self.charger_status_label.setText("CHARGER ON")
self.charger_status_label.setProperty("status", "present")
else:
self.charger_status_label.setText("CHARGER OFF")
self.charger_status_label.setProperty("status", "absent")
# Re-apply stylesheets for the status labels
for widget in [self.battery_status_label, self.charger_status_label]:
widget.style().unpolish(widget)
widget.style().polish(widget)
# --- Conditionally update battery-specific fields ---
if battery_is_present:
self.soc_field.setText(f'{data.get("soc", 0)}%')
self.voltage_field.setText(f'{data.get("voltage", 0) / 1000.0:.2f} V')
self.temp_field.setText(f'{data.get("batteryMaxTemp", 0) / 10.0:.1f} °C')
self.battery_fault_field.setText(str(data.get("batteryFaultCode", 0)))
else:
# Clear the fields if the battery is absent
self.soc_field.setText(" ")
self.voltage_field.setText(" ")
self.temp_field.setText(" ")
self.battery_fault_field.setText(" ")
# --- Conditionally update charger-specific fields ---
if charger_is_present:
charger_temp = data.get("chargerMaxTemp", 0)
self.chg_temp_field.setText(f'{charger_temp / 10.0:.1f} °C')
self.charger_fault_field.setText(str(data.get("chargerFaultCode", 0)))
self.current_field.setText(f'{data.get("current", 0) / 1000.0:.2f} A')
else:
# Clear the fields if the charger is off/absent
self.chg_temp_field.setText(" ")
self.charger_fault_field.setText(" ")
self.current_field.setText(" ")
# --- Update fields that are always visible ---
self.id_field.setText(data.get("batteryIdentification", " "))
slot_temp = data.get("slotTemperature", 0)
self.slot_temp_field.setText(f'{slot_temp / 10.0:.1f} °C')
door_status = "OPEN" if data.get("doorStatus") == 1 else "CLOSED"
self.door_status_field.setText(door_status)
if door_status == "OPEN":
self.door_status_field.setStyleSheet("background-color: #30ce72; color: white; border-radius: 3px;")
else:
self.door_status_field.setStyleSheet("background-color: #fd5443; color: white; border-radius: 3px;")
def reset_to_default(self):
"""Resets all fields in this chamber widget to their default state."""
self.battery_status_label.setText("ABSENT")
self.battery_status_label.setProperty("status", "absent")
self.charger_status_label.setText("CHARGER OFF")
self.charger_status_label.setProperty("status", "absent")
for widget in [self.battery_status_label, self.charger_status_label]:
widget.style().unpolish(widget)
widget.style().polish(widget)
self.id_field.setText(" ")
self.soc_field.setText(" ")
self.voltage_field.setText(" ")
self.temp_field.setText(" ")
self.battery_fault_field.setText(" ")
self.chg_temp_field.setText(" ")
self.charger_fault_field.setText(" ")
self.current_field.setText(" ")
self.slot_temp_field.setText(" ")
self.door_status_field.setText(" ")
# --- ADDED: Reset the color ---
self.door_status_field.setStyleSheet("")