diff --git a/ui/main_window.py b/ui/main_window.py
index 5215d84..99b8708 100644
--- a/ui/main_window.py
+++ b/ui/main_window.py
@@ -11,7 +11,7 @@ from PyQt6.QtWidgets import (
QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QTabWidget,
QGroupBox, QFormLayout, QLineEdit, QPushButton, QLabel, QSpacerItem,
QSizePolicy, QGridLayout, QMessageBox, QComboBox, QPlainTextEdit,
- QCheckBox, QFileDialog, QLayout, QFrame, QSizePolicy, QGraphicsOpacityEffect, QVBoxLayout, QTextBrowser, QScrollArea
+ QCheckBox, QFileDialog, QLayout, QFrame, QSizePolicy, QGraphicsOpacityEffect, QVBoxLayout, QTextBrowser, QScrollArea, QGraphicsDropShadowEffect
)
from PyQt6.QtSvgWidgets import QSvgWidget
from google.protobuf.json_format import MessageToDict
@@ -104,110 +104,62 @@ class MainWindow(QMainWindow):
self.load_settings()
self._apply_theme()
+
def setup_help_ui(self):
"""
- Polished Help page:
+ Help page (theme-driven):
- Card-style container with title
- - Sections: Quick Start, Warnings, Troubleshooting, Shortcuts
- - Colored callouts for warnings/tips
- - Scrollable if content grows
+ - Sections: Quick Start, Tips, Warnings, Troubleshooting
+ - Scrollable host
"""
- # Clear existing layout
- def clear_layout(w: QWidget):
- lay = w.layout()
- if not lay: return
- while lay.count():
- it = lay.takeAt(0)
- if it.widget(): it.widget().deleteLater()
- elif it.layout():
- while it.layout().count():
- sub = it.layout().takeAt(0)
- if sub.widget(): sub.widget().deleteLater()
- it.layout().deleteLater()
- lay.deleteLater()
- clear_layout(self.help_tab)
+ # get or create root layout (never replace/delete the layout itself)
+ root = self.help_tab.layout()
+ if root is None:
+ root = QVBoxLayout()
+ root.setContentsMargins(12, 12, 12, 12)
+ root.setSpacing(10)
+ self.help_tab.setLayout(root)
+ else:
+ # clear previous contents
+ while root.count():
+ it = root.takeAt(0)
+ w = it.widget()
+ if w:
+ w.setParent(None)
+ w.deleteLater()
- root = QVBoxLayout(self.help_tab)
- root.setContentsMargins(12, 12, 12, 12)
- root.setSpacing(10)
-
- # Scroll area so long help stays usable
+ # scroll host
scroll = QScrollArea()
scroll.setWidgetResizable(True)
scroll.setFrameShape(QFrame.Shape.NoFrame)
host = QWidget()
- scroll_lay = QVBoxLayout(host)
- scroll_lay.setContentsMargins(0, 0, 0, 0)
- scroll_lay.setSpacing(12)
+ host_lay = QVBoxLayout(host)
+ host_lay.setContentsMargins(0, 0, 0, 0)
+ host_lay.setSpacing(12)
- # Card container
+ # card (styled by theme via objectNames)
card = QFrame()
card.setObjectName("helpCard")
- card.setStyleSheet("""
- #helpCard {
- background: #2a2a2a;
- border: 1px solid #3a3a3a;
- border-radius: 12px;
- }
- QLabel.title {
- color: #eaeaea;
- font-weight: 700;
- font-size: 18px;
- }
- QLabel.subtitle {
- color: #c8c8c8;
- font-size: 12px;
- }
- QFrame.divider {
- background: #3a3a3a;
- min-height: 1px; max-height: 1px; border: none;
- }
- QLabel.h3 {
- color: #e6e6e6;
- font-weight: 600;
- margin-top: 6px;
- }
- QLabel.body {
- color: #dcdcdc;
- }
- /* Callouts */
- QFrame.warn {
- background: #3b2e1b;
- border: 1px solid #ffb74d;
- border-radius: 8px;
- }
- QLabel.warnTitle { color: #ffd561; font-weight: 700; }
- QLabel.warnText { color: #f0e0c0; }
- QFrame.tip {
- background: #1f3326;
- border: 1px solid #62d39b;
- border-radius: 8px;
- }
- QLabel.tipTitle { color: #a8f5c9; font-weight: 700; }
- QLabel.tipText { color: #d6ffe9; }
- QLabel.link { color: #6aa9ff; }
- """)
+
card_lay = QVBoxLayout(card)
card_lay.setContentsMargins(18, 16, 18, 16)
card_lay.setSpacing(12)
- # Header
- title = QLabel("Help & User Guide")
- title.setProperty("class", "title")
+ # header
+ title = QLabel("Help & User Guide"); title.setObjectName("helpTitle")
subtitle = QLabel("Follow these steps to get connected, monitor the station, and troubleshoot issues.")
- subtitle.setProperty("class", "subtitle")
+ subtitle.setObjectName("helpSubtitle")
card_lay.addWidget(title)
card_lay.addWidget(subtitle)
- div1 = QFrame(); div1.setObjectName("divider"); div1.setFrameShape(QFrame.Shape.NoFrame); div1.setProperty("class", "divider")
+ div1 = QFrame(); div1.setObjectName("helpDivider")
card_lay.addWidget(div1)
# Quick Start
- qs_title = QLabel("Quick Start")
- qs_title.setProperty("class", "h3")
+ qs_title = QLabel("Quick Start"); qs_title.setObjectName("sectionTitle")
qs = QLabel(
"
"
"- Configure: Open Config, fill MQTT details, click Connect.
"
@@ -218,16 +170,14 @@ class MainWindow(QMainWindow):
"
"
)
qs.setOpenExternalLinks(True)
- qs.setProperty("class", "body")
-
+ qs.setObjectName("bodyText")
card_lay.addWidget(qs_title)
card_lay.addWidget(qs)
- # Tips (green)
- tip_box = QFrame(); tip_box.setObjectName("tip"); tip_box.setProperty("class", "tip")
+ # Tips
+ tip_box = QFrame(); tip_box.setObjectName("tipBox")
tip_lay = QVBoxLayout(tip_box); tip_lay.setContentsMargins(12, 10, 12, 10)
- tip_h = QLabel("đź’ˇ Tips")
- tip_h.setProperty("class", "tipTitle")
+ tip_h = QLabel("đź’ˇ Tips"); tip_h.setObjectName("tipTitle")
tip_b = QLabel(
""
"- Use a stable, low-latency network for best live updates.
"
@@ -235,17 +185,16 @@ class MainWindow(QMainWindow):
"- Use Logs → Export before clearing or reinstalling.
"
"
"
)
- tip_b.setProperty("class", "tipText")
tip_b.setOpenExternalLinks(True)
+ tip_b.setObjectName("tipText")
tip_lay.addWidget(tip_h)
tip_lay.addWidget(tip_b)
card_lay.addWidget(tip_box)
- # Warnings (amber)
- warn_box = QFrame(); warn_box.setObjectName("warn"); warn_box.setProperty("class", "warn")
+ # Warnings
+ warn_box = QFrame(); warn_box.setObjectName("warnBox")
warn_lay = QVBoxLayout(warn_box); warn_lay.setContentsMargins(12, 10, 12, 10)
- warn_h = QLabel("⚠️ Important Warnings")
- warn_h.setProperty("class", "warnTitle")
+ warn_h = QLabel("⚠️ Important Warnings"); warn_h.setObjectName("warnTitle")
warn_b = QLabel(
""
"- Disconnected state means no live data. Check server, network, and credentials.
"
@@ -253,14 +202,13 @@ class MainWindow(QMainWindow):
"- Commands during disconnect may be lost. Reconnect before critical actions.
"
"
"
)
- warn_b.setProperty("class", "warnText")
+ warn_b.setObjectName("warnText")
warn_lay.addWidget(warn_h)
warn_lay.addWidget(warn_b)
card_lay.addWidget(warn_box)
# Troubleshooting
- tr_title = QLabel("Troubleshooting")
- tr_title.setProperty("class", "h3")
+ tr_title = QLabel("Troubleshooting"); tr_title.setObjectName("sectionTitle")
tr = QLabel(
""
"- Cannot connect: Verify broker/port, username/password, and firewall.
"
@@ -269,175 +217,197 @@ class MainWindow(QMainWindow):
"- Wrong station showing: Confirm the Device ID matches the station you expect.
"
"
"
)
- tr.setProperty("class", "body")
+ tr.setObjectName("bodyText")
card_lay.addWidget(tr_title)
card_lay.addWidget(tr)
- # Footer
- div2 = QFrame(); div2.setObjectName("divider"); div2.setProperty("class", "divider")
+ # footer
+ div2 = QFrame(); div2.setObjectName("helpDivider")
card_lay.addWidget(div2)
- foot = QLabel("Need help? Email kirubakaran@vecmocon.com")
+ foot = QLabel("Need help? Email "
+ ""
+ "kirubakaran@vecmocon.com")
foot.setOpenExternalLinks(True)
- foot.setProperty("class", "body")
+ foot.setObjectName("bodyText")
card_lay.addWidget(foot)
- scroll_lay.addWidget(card)
- scroll_lay.addStretch(1)
+ # assemble
+ host_lay.addWidget(card)
+ host_lay.addStretch(1)
scroll.setWidget(host)
root.addWidget(scroll)
+ def _about_stylesheet() -> str:
+ return """
+ /* Card */
+ #aboutCard {
+ background: #121417;
+ border: 1px solid #2a2f36;
+ border-radius: 16px;
+ }
+
+ /* Title + subtitle */
+ #title {
+ font-size: 22px;
+ font-weight: 700;
+ color: #e8edf2;
+ letter-spacing: 0.2px;
+ }
+ #subtitle {
+ font-size: 13px;
+ font-weight: 500;
+ color: #9aa4b2;
+ }
+
+ /* Divider */
+ #divider {
+ background: qlineargradient(x1:0, y1:0, x2:1, y2:0,
+ stop:0 #1b1f24, stop:0.5 #2a2f36, stop:1 #1b1f24);
+ min-height: 1px;
+ max-height: 1px;
+ border: none;
+ margin: 6px 0 8px 0;
+ }
+
+ /* Small label on the left column */
+ #kvLabel {
+ color: #aab4c0;
+ font-weight: 600;
+ }
+
+ /* Pill badge */
+ #badge {
+ color: #eaf3ff;
+ background: qlineargradient(x1:0, y1:0, x2:1, y2:0,
+ stop:0 #2563eb, stop:1 #7c3aed);
+ border-radius: 999px;
+ padding: 5px 12px;
+ font-weight: 700;
+ letter-spacing: 0.3px;
+ }
+
+ /* Links */
+ QLabel { color: #d7dde6; }
+ QLabel:hover { text-decoration: none; }
+ QLabel[link='true'] { color: #6aa8ff; }
+ QLabel[link='true']:hover { color: #8fc2ff; text-decoration: underline; }
+
+ /* Footer */
+ #footer {
+ color: #7e8895;
+ font-size: 12px;
+ }
+ """
+
def setup_about_ui(self):
- """
- A clean, native-Qt About page:
- - Card-style container with rounded corners
- - Bold title + subtitle
- - Small badges for version/build
- - Left/right aligned key–value grid
- """
-
- # Clear existing layout if re-building
- def clear_layout(w: QWidget):
- lay = w.layout()
- if lay:
- while lay.count():
- item = lay.takeAt(0)
- if item.widget():
- item.widget().deleteLater()
- elif item.layout():
- while item.layout().count():
- sub = item.layout().takeAt(0)
- if sub.widget():
- sub.widget().deleteLater()
- item.layout().deleteLater()
- lay.deleteLater()
-
- clear_layout(self.about_tab)
-
- build_num = "4.0"
-
- # ---- Main container ----
- root = QVBoxLayout(self.about_tab)
- root.setContentsMargins(16, 16, 16, 16)
- root.setSpacing(12)
+ # get or create root layout
+ root = self.about_tab.layout()
+ if root is None:
+ root = QVBoxLayout()
+ root.setContentsMargins(24, 24, 24, 24)
+ root.setAlignment(Qt.AlignmentFlag.AlignTop | Qt.AlignmentFlag.AlignHCenter)
+ self.about_tab.setLayout(root)
+ else:
+ # clear existing items
+ while root.count():
+ item = root.takeAt(0)
+ w = item.widget()
+ if w:
+ w.setParent(None)
+ w.deleteLater()
+ # --- card ---
card = QFrame()
card.setObjectName("aboutCard")
- card.setFrameShape(QFrame.Shape.NoFrame)
- card.setStyleSheet("""
- #aboutCard {
- background: #2a2a2a;
- border: 1px solid #3a3a3a;
- border-radius: 12px;
- }
- QLabel#title {
- color: #eaeaea;
- font-weight: 700;
- }
- QLabel#subtitle {
- color: #c7c7c7;
- }
- QLabel#label {
- color: #b6b6b6;
- font-weight: 600;
- }
- QLabel#value {
- color: #e6e6e6;
- }
- QLabel.badge {
- background: #343a40;
- border: 1px solid #454d55;
- border-radius: 999px;
- padding: 2px 10px;
- color: #e6e6e6;
- font-size: 11px;
- }
- QFrame#divider {
- background: #3a3a3a;
- min-height: 1px;
- max-height: 1px;
- border: none;
- }
- QLabel.link {
- color: #6aa9ff;
- }
- """)
- card_lay = QVBoxLayout(card)
- card_lay.setContentsMargins(18, 16, 18, 16)
- card_lay.setSpacing(10)
+ card.setMinimumWidth(560)
+ card.setMaximumWidth(760)
+ card.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Maximum)
- # ---- Header ----
+ shadow = QGraphicsDropShadowEffect(blurRadius=28, xOffset=0, yOffset=12)
+ shadow.setColor(Qt.GlobalColor.black)
+ card.setGraphicsEffect(shadow)
+
+ card_layout = QVBoxLayout(card)
+ card_layout.setContentsMargins(28, 22, 28, 22)
+ card_layout.setSpacing(14)
+
+ # --- header row (logo + titles + badge) ---
+ header = QHBoxLayout()
+ header.setSpacing(12)
+
+ # optional logo (put a 48px logo at ./logo/app.png if you have one)
+ logo_label = QLabel()
+ logo_path = "logo/v_logo.png"
+ pix = QPixmap(logo_path)
+ if not pix.isNull():
+ logo_label.setPixmap(pix.scaled(40, 40, Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.SmoothTransformation))
+ logo_label.setFixedSize(44, 44)
+ header.addWidget(logo_label, 0, Qt.AlignmentFlag.AlignTop)
+
+ title_box = QVBoxLayout()
title = QLabel("About This Application")
title.setObjectName("title")
- tfont = QFont()
- tfont.setPointSize(16)
- tfont.setBold(True)
- title.setFont(tfont)
-
subtitle = QLabel("Battery Swap Station Dashboard")
subtitle.setObjectName("subtitle")
+ title_box.addWidget(title)
+ title_box.addWidget(subtitle)
+ header.addLayout(title_box, 1)
- header = QVBoxLayout()
- header.setSpacing(4)
- header.addWidget(title)
- header.addWidget(subtitle)
+ badge = QLabel("Version 4.0")
+ badge.setObjectName("badge")
+ header.addWidget(badge, 0, Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
- # badges row
- badges = QHBoxLayout()
- badges.setSpacing(8)
- ver = QLabel(f"Version: {build_num}")
- ver.setObjectName("")
- ver.setProperty("class", "badge")
- ver.setAlignment(Qt.AlignmentFlag.AlignCenter)
- ver.setMinimumHeight(20)
- ver.setStyleSheet("")
+ card_layout.addLayout(header)
- badges.addWidget(ver, 0, Qt.AlignmentFlag.AlignLeft)
- badges.addStretch(1)
+ # --- divider ---
+ divider = QFrame()
+ divider.setObjectName("divider")
+ card_layout.addWidget(divider)
- card_lay.addLayout(header)
- card_lay.addLayout(badges)
-
- # divider
- div = QFrame()
- div.setObjectName("divider")
- card_lay.addWidget(div)
-
- # ---- Key–Value grid ----
+ # --- key–value grid ---
grid = QGridLayout()
- grid.setHorizontalSpacing(24)
+ grid.setHorizontalSpacing(18)
grid.setVerticalSpacing(10)
- def add_row(r, key, val, is_link=False):
- l = QLabel(key)
- l.setObjectName("label")
- v = QLabel(val)
- v.setObjectName("value")
+ def add_row(r: int, key: str, value: str, is_link: bool = False):
+ k = QLabel(key)
+ k.setObjectName("kvLabel")
+ v = QLabel(value)
+ v.setWordWrap(False) # instead of True
+
if is_link:
- v.setText(f'{val}')
+ v.setTextFormat(Qt.TextFormat.RichText)
+ v.setTextInteractionFlags(Qt.TextInteractionFlag.TextBrowserInteraction) # links + selection + keyboard nav
v.setOpenExternalLinks(True)
- v.setTextInteractionFlags(Qt.TextInteractionFlag.TextBrowserInteraction)
- v.setObjectName("") # style via link class
- v.setProperty("class", "link")
- grid.addWidget(l, r, 0, Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
- grid.addWidget(v, r, 1, Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter)
+ v.setProperty("link", True)
- add_row(0, "Company", "VECMOCON TECHNOLOGIES")
+ else:
+ v.setTextInteractionFlags(Qt.TextInteractionFlag.TextSelectableByMouse)
+
+ grid.addWidget(k, r, 0, Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignTop)
+ grid.addWidget(v, r, 1, Qt.AlignmentFlag.AlignLeft)
+
+ add_row(0, "Company", "VECMOCON TECHNOLOGIES")
add_row(1, "Developed by", "Kirubakaran S")
- add_row(2, "Support", "kirubakaran@vecmocon.com", is_link=True) # mailto auto-handled by client
- add_row(3, "Website", "https://www.vecmocon.com", is_link=True)
+ add_row(2, "Support",
+ "kirubakaran@vecmocon.com",
+ is_link=True)
- card_lay.addLayout(grid)
+ add_row(3, "Website",
+ "www.vecmocon.com",
+ is_link=True)
- # footer
- footer = QLabel("© 2025 VECMOCON TECHNOLOGIES. All rights reserved. • Made with ♥ for reliability & clarity.")
- footer.setAlignment(Qt.AlignmentFlag.AlignLeft)
- footer.setObjectName("subtitle") # subtle color
- card_lay.addSpacing(6)
- card_lay.addWidget(footer)
+ card_layout.addLayout(grid)
- # center the card horizontally
- root.addWidget(card)
- root.addStretch(1)
+ # --- tiny space + footer ---
+ spacer = QFrame(); spacer.setFixedHeight(4)
+ card_layout.addWidget(spacer)
+
+ footer = QLabel("© 2025 VECMOCON TECHNOLOGIES. All rights reserved.")
+ footer.setObjectName("footer")
+ card_layout.addWidget(footer, 0, Qt.AlignmentFlag.AlignHCenter)
+
+ root.addWidget(card, 0, Qt.AlignmentFlag.AlignHCenter)
def load_settings(self):
@@ -590,6 +560,8 @@ class MainWindow(QMainWindow):
# 3. Immediately apply the new theme underneath the overlay
self.is_dark_theme = not self.is_dark_theme
self._apply_theme()
+ self.setup_about_ui()
+ self.setup_help_ui()
# 4. Set up the opacity effect and animation for the overlay
opacity_effect = QGraphicsOpacityEffect(overlay)
@@ -609,6 +581,7 @@ class MainWindow(QMainWindow):
self.animation.start(self.animation.DeletionPolicy.DeleteWhenStopped)
def create_status_bar(self):
+
status_bar_widget = QWidget()
status_bar_widget.setStyleSheet(f"background-color: #2c3e50; padding: {int(6*self.scale_factor)}px;")
status_bar_layout = QHBoxLayout(status_bar_widget)
@@ -634,6 +607,17 @@ class MainWindow(QMainWindow):
right_layout.addStretch()
self.connect_button = QPushButton("Connect")
self.disconnect_button = QPushButton("Disconnect")
+ button_font_size = max(10, int(12 * self.scale_factor))
+ button_stylesheet = f"""
+ QPushButton {{
+ font-size: {button_font_size}px;
+ font-weight: bold;
+ padding: 4px 14px;
+ background-color: #3498db;
+ }}
+ """
+ self.connect_button.setStyleSheet(button_stylesheet)
+ self.disconnect_button.setStyleSheet(button_stylesheet)
self.connect_button.setObjectName("ConnectButton")
self.disconnect_button.setObjectName("DisconnectButton")
self.disconnect_button.setEnabled(False)
@@ -781,14 +765,19 @@ class MainWindow(QMainWindow):
int(8 * self.scale_factor), int(8 * self.scale_factor)
)
- # You correctly created the frame here
self.top_bar_frame = QFrame()
self.top_bar_frame.setObjectName("topBarFrame")
top_bar_layout = QHBoxLayout()
- top_bar_layout.addWidget(QLabel("LAST RECV TS:"))
+
+ ts_label = QLabel("LAST RECV TS:")
+ ts_label.setObjectName("TimestampTitleLabel")
+ top_bar_layout.addWidget(ts_label)
+
self.last_recv_ts_field = QLineEdit("No Data")
self.last_recv_ts_field.setReadOnly(True)
+
+ self.last_recv_ts_field.setObjectName("TimestampDataField")
top_bar_layout.addWidget(self.last_recv_ts_field)
top_bar_layout.addSpacerItem(QSpacerItem(20, 20, QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Minimum))
diff --git a/ui/styles.py b/ui/styles.py
index 7265d5d..423d87c 100644
--- a/ui/styles.py
+++ b/ui/styles.py
@@ -1,25 +1,451 @@
+# # --- Dynamic Theme Stylesheets ---
+
+# def get_light_theme_styles(scale=1.0):
+# log_font_size = max(10, int(11 * scale))
+# button_font_size = max(7, int(10 * scale))
+
+# return f"""
+# QMainWindow, QWidget {{
+# background-color: #f0f0f0;
+# color: #000;
+# }}
+
+# /* --- ADDED THIS RULE --- */
+# #StatusBar {{
+# background-color: #34495e; /* A dark blue-grey for light theme */
+# padding: {int(6 * scale)}px;
+# }}
+
+# #LogPanel {{
+# font-family: "Courier New", Consolas, monospace;
+# font-size: {log_font_size}pt;
+# background-color: #ffffff; /* White background for logs */
+# color: #2b2b2b;
+# border: 1px solid #c8c8c8;
+# }}
+# QGroupBox {{
+# font-family: Arial;
+# border: 1px solid #c8c8c8;
+# border-radius: {int(8 * scale)}px;
+# margin-top: {int(6 * scale)}px;
+# }}
+# QGroupBox::title {{
+# subcontrol-origin: margin;
+# subcontrol-position: top center;
+# padding: 0 {int(10 * scale)}px;
+# color: #000;
+# }}
+# QTabWidget::pane {{ border-top: 2px solid #c8c8c8; }}
+# QTabBar::tab {{
+# background: #e1e1e1; border: 1px solid #c8c8c8;
+# padding: {int(6 * scale)}px {int(15 * scale)}px;
+# border-top-left-radius: {int(4 * scale)}px; border-top-right-radius: {int(4 * scale)}px;
+# }}
+# QTabBar::tab:selected {{ background: #f0f0f0; border-bottom-color: #f0f0f0; }}
+# QFormLayout::label {{ color: #000; padding-top: {int(3 * scale)}px; }}
+# QLineEdit, QPlainTextEdit, QComboBox {{
+# background-color: #fff; border: 1px solid #c8c8c8;
+# border-radius: {int(4 * scale)}px; padding: {int(4 * scale)}px;
+# font-size: {max(7, int(9 * scale))}pt;
+# color: #000;
+# }}
+# QLineEdit:read-only {{ background-color: #e9e9e9; }}
+# QPushButton {{
+# background-color: #e1e1e1; border: 1px solid #c8c8c8;
+# padding: {int(5 * scale)}px {int(10 * scale)}px;
+# border-radius: {int(4 * scale)}px;
+# color: #000;
+# }}
+# QPushButton:hover {{ background-color: #dcdcdc; }}
+# QPushButton:pressed {{ background-color: #c8c8c8; }}
+# QPushButton:disabled {{ background-color: #d3d3d3; color: #a0a0a0; }}
+
+# #RefreshButton, #ResetButton {{
+# padding: {int(6 * scale)}px {int(16 * scale)}px;
+# font-size: {button_font_size * 1.3}pt;
+# font-weight: bold;
+# color: white;
+# border-radius: {int(4*scale)}px;
+# }}
+
+# #RefreshButton {{
+# background-color: #2e7d32; /* A slightly darker green */
+# }}
+# #ResetButton {{
+# background-color: #c62828; /* A slightly darker red */
+# }}
+
+# /* --- UPDATED BUTTON STYLES FOR LIGHT THEME --- */
+# #ConnectButton, #DisconnectButton, #StartSwapButton, #AbortSwapButton {{
+# padding: {int(6 * scale)}px {int(16 * scale)}px;
+# font-size: {button_font_size}pt;
+# font-weight: bold;
+# border-radius: {int(4 * scale)}px;
+# color: white;
+# border: none;
+# }}
+
+# /* Positive Actions (Green) */
+# #ConnectButton, #StartSwapButton {{
+# background-color: #28a745;
+# }}
+# #ConnectButton:hover, #StartSwapButton:hover {{
+# background-color: #218838;
+# }}
+# /* Negative Actions (Red) */
+# #DisconnectButton, #AbortSwapButton {{
+# background-color: #dc3545;
+# }}
+# #DisconnectButton:hover, #AbortSwapButton:hover {{
+# background-color: #c82333;
+# }}
+# /* Disabled States */
+# #ConnectButton:disabled, #DisconnectButton:disabled {{
+# background-color: #b0bec5;
+# color: #78909c;
+# }}
+# /* Unique Buttons */
+# #SendAudioButton {{
+# background-color: #007bff;
+# color: white;
+# border: none;
+# font-size: {max(10, int(14 * scale))}px;
+# }}
+# #SendAudioButton:hover {{
+# background-color: #0069d9;
+# }}
+
+# #ChamberOpenDoorButton, #ChamberChgOnButton, #ChamberChgOffButton {{
+# padding: {int(8 * scale)}px;
+# font-size: {button_font_size}pt;
+# font-weight: bold;
+# border-radius: {int(4*scale)}px;
+# }}
+
+# #ChamberOpenDoorButton:hover {{ background-color: #607d8b; }}
+# #ChamberChgOnButton:hover {{ background-color: #52be80; }}
+# #ChamberChgOffButton:hover {{ background-color: #cd6155; }}
+
+# /* --- STATUS & ALARM LABELS --- */
+# QLabel[status="present"] {{ background-color: #2ecc71; color: white; border-radius: {int(4*scale)}px; padding: {int(3*scale)}px; }}
+# QLabel[status="absent"] {{ background-color: #e74c3c; color: white; border-radius: {int(4*scale)}px; padding: {int(3*scale)}px; }}
+# QLabel[alarm="active"] {{ background-color: #e74c3c; color: white; font-weight: bold; border-radius: {int(4*scale)}px; padding: {int(2*scale)}px; }}
+# QLabel[alarm="inactive"] {{ background-color: transparent; color: black; }}
+# QGroupBox#ChamberWidget {{ border: 2px solid #007bff; }}
+
+# /* --- ADDED TIMESTAMP STYLES --- */
+# QLabel#TimestampTitleLabel {{
+# font-size: {max(9, int(11 * scale))}pt;
+# font-weight: bold;
+# color: #333;
+# }}
+# QLineEdit#TimestampDataField {{
+# font-size: {max(11, int(13 * scale))}pt;
+# font-weight: bold;
+# color: #000;
+# }}
+
+# QGroupBox#ChamberWidget QFormLayout QLabel {{
+# font-size: {max(10, int(12 * scale))}px;
+# color: #555555;
+# }}
+# QGroupBox#ChamberWidget QLineEdit#BatIdField {{
+# font-size: {max(14, int(16 * scale))}px;
+# font-weight: bold;
+# color: #000000;
+# }}
+# QGroupBox#ChamberWidget QLineEdit#DataField {{
+# font-size: {max(13, int(14 * scale))}px;
+# color: #222222;
+# }}
+# QGroupBox#ChamberWidget QLineEdit#DoorStatusField {{
+# font-size: {max(12, int(14 * scale))}px;
+# font-weight: bold;
+# color: #222222;
+# }}
+
+# #aboutCard {{
+# background-color: #ffffff; /* White background */
+# border: 1px solid #dee2e6; /* Light grey border */
+# border-radius: 12px;
+# }}
+# #aboutCard QLabel#title {{
+# color: #212529; /* Dark text for title */
+# font-size: 18pt;
+# font-weight: bold;
+# }}
+# #aboutCard QLabel#subtitle {{
+# color: #6c757d; /* Grey for subtitle */
+# }}
+# #aboutCard QLabel#label {{
+# color: #495057; /* Dark grey for labels */
+# font-weight: bold;
+# }}
+# #aboutCard QLabel {{ /* General text color inside the card */
+# color: #212529;
+# }}
+# #aboutCard QLabel.badge {{
+# background-color: #e9ecef; /* Light grey for badge */
+# border-radius: 8px;
+# padding: 3px 10px;
+# font-size: 9pt;
+# }}
+# #aboutCard QFrame#divider {{
+# background-color: #dee2e6; /* Light grey for divider line */
+# max-height: 1px;
+# border: none;
+# }}
+# #aboutCard a {{
+# color: #007bff; /* Standard blue for links */
+# text-decoration: none;
+# }}
+# """
+
+# def get_dark_theme_styles(scale=1.0):
+
+# log_font_size = max(10, int(11 * scale))
+# button_font_size = max(7, int(10 * scale))
+
+# return f"""
+# QMainWindow, QWidget {{ background-color: #2b2b2b; color: #f0f0f0; }}
+# #LogPanel {{
+# font-family: "Courier New", Consolas, monospace;
+# font-size: {log_font_size}pt;
+# background-color: #212121;
+# color: #eceff1;
+# border: 1px solid #455a64;
+# }}
+# QGroupBox {{
+# font-family: Arial; border: 1px solid #4a4a4a;
+# border-radius: {int(8 * scale)}px; margin-top: {int(6 * scale)}px;
+# }}
+# QGroupBox::title {{ subcontrol-origin: margin; subcontrol-position: top center; padding: 0 {int(10 * scale)}px; color: #f0f0f0; }}
+# QTabWidget::pane {{ border-top: 2px solid #4a4a4a; }}
+# QTabBar::tab {{
+# background: #3c3c3c; border: 1px solid #4a4a4a; color: #f0f0f0;
+# padding: {int(6 * scale)}px {int(15 * scale)}px;
+# border-top-left-radius: {int(4 * scale)}px; border-top-right-radius: {int(4 * scale)}px;
+# }}
+# QTabBar::tab:selected {{ background: #2b2b2b; border-bottom-color: #2b2b2b; }}
+# QFormLayout::label {{ color: #f0f0f0; padding-top: {int(3 * scale)}px; }}
+# QLineEdit, QPlainTextEdit, QComboBox {{
+# background-color: #3c3c3c; border: 1px solid #4a4a4a;
+# border-radius: {int(4 * scale)}px; padding: {int(4 * scale)}px; color: #f0f0f0;
+# font-size: {max(7, int(9 * scale))}pt;
+# }}
+# QLineEdit:read-only {{ background-color: #333333; }}
+# QPushButton {{
+# background-color: #555555; border: 1px solid #4a4a4a;
+# padding: {int(5 * scale)}px {int(10 * scale)}px;
+# border-radius: {int(4 * scale)}px; color: #f0f0f0;
+# }}
+# QPushButton:hover {{ background-color: #6a6a6a; }}
+# QPushButton:pressed {{ background-color: #4a4a4a; }}
+# QPushButton:disabled {{ background-color: #404040; color: #888888; }}
+# #RefreshButton, #ResetButton {{
+# padding: {int(6 * scale)}px {int(16 * scale)}px;
+# font-size: {button_font_size * 1.3}pt;
+# font-weight: bold;
+# border-radius: {int(4*scale)}px;
+# }}
+# #RefreshButton {{
+# background-color: #2e7d32; /* A slightly darker green */
+# }}
+# #ResetButton {{
+# background-color: #c62828; /* A slightly darker red */
+# }}
+# #ChamberOpenDoorButton, #ChamberChgOnButton, #ChamberChgOffButton {{
+# padding: {int(8 * scale)}px;
+# font-size: {button_font_size}pt;
+# font-weight: bold;
+# border-radius: {int(4*scale)}px;
+# }}
+
+# #ChamberOpenDoorButton {{ background-color: #3C3C3C; }}
+# #ChamberChgOnButton {{ background-color: #3C3C3C; }}
+# #ChamberChgOffButton {{ background-color: #3C3C3C; }}
+
+# #ChamberOpenDoorButton:hover {{ background-color: #607d8b; }}
+# #ChamberChgOnButton:hover {{ background-color: #52be80; }}
+# #ChamberChgOffButton:hover {{ background-color: #cd6155; }}
+
+# #ConnectButton, #DisconnectButton {{
+# padding: {int(6 * scale)}px {int(16 * scale)}px;
+# font-size: {button_font_size}pt;
+# font-weight: bold;
+# border-radius: {int(4*scale)}px;
+# color: white;
+# }}
+
+# #ConnectButton {{ background-color: #27ae60; }} /* Green */
+# #DisconnectButton {{ background-color: #c0392b; }} /* Red */
+
+# #ConnectButton:hover {{ background-color: #52be80; }}
+# #DisconnectButton:hover {{ background-color: #cd6155; }}
+
+# #ConnectButton:pressed {{ background-color: #52be80; }}
+# #DisconnectButton:pressed {{ background-color: #cd6155; }}
+
+# #ConnectButton:disabled, #DisconnectButton:disabled {{
+# background-color: #546e7a;
+# color: #90a4ae;
+# }}
+
+# #RefreshButton, #StartSwapButton {{ background-color: #27ae60; color: white; border: none; }}
+# #RefreshButton:hover, #StartSwapButton:hover {{ background-color: #52be80; }}
+# #ResetButton, #AbortSwapButton {{ background-color: #c0392b; color: white; border: none; }}
+# #ResetButton:hover, #AbortSwapButton:hover {{ background-color: #cd6155; }}
+# #SendAudioButton {{ background-color: #3498db; color: white; border: none; font-size: {max(10, int(14 * scale))}px; }}
+# #SendAudioButton:hover {{ background-color: #5dade2; }}
+# QLabel[status="present"] {{ background-color: #2ecc71; color: white; border-radius: {int(4*scale)}px; padding: {int(3*scale)}px; }}
+# QLabel[status="absent"] {{ background-color: #e74c3c; color: white; border-radius: {int(4*scale)}px; padding: {int(3*scale)}px; }}
+# QLabel[alarm="active"] {{ background-color: #e74c3c; color: white; font-weight: bold; border-radius: {int(4*scale)}px; padding: {int(2*scale)}px; }}
+# QLabel[alarm="inactive"] {{ background-color: transparent; color: #f0f0f0; }}
+# QGroupBox#ChamberWidget {{ border: 2px solid #3498db; }}
+
+# /* Style for the timestamp TITLE label */
+# QLabel#TimestampTitleLabel {{
+# font-size: {max(9, int(11 * scale))}pt; /* Normal font size */
+# font-weight: bold;
+# }}
+
+# /* Style for the timestamp DATA field */
+# QLineEdit#TimestampDataField {{
+# font-size: {max(11, int(13 * scale))}pt; /* Larger font */
+# font-weight: bold;
+# color: #f0f0f0;
+# }}
+
+# QGroupBox#ChamberWidget QFormLayout QLabel {{
+# font-size: {max(10, int(12 * scale))}px;
+# color: #AAAAAA;
+# }}
+# QGroupBox#ChamberWidget QLineEdit#BatIdField {{
+# font-size: {max(14, int(16 * scale))}px;
+# font-weight: bold;
+# color: #FFFFFF;
+# }}
+# QGroupBox#ChamberWidget QLineEdit#DataField {{
+# font-size: {max(13, int(14 * scale))}px;
+# color: #E0E0E0;
+# }}
+# QGroupBox#ChamberWidget QLineEdit#DoorStatusField {{
+# font-size: {max(12, int(14 * scale))}px;
+# font-weight: bold;
+# color: #E0E0E0;
+# }}
+
+# QFrame#aboutCard {{
+# background: #2a2a2a;
+# border: 1px solid #3a3a3a;
+# border-radius: 12px;
+# }}
+# QFrame#aboutCard QLabel#title {{
+# color: #eaeaea;
+# font-size: 16pt;
+# font-weight: bold;
+# }}
+# QFrame#aboutCard QLabel#subtitle {{
+# color: #c7c7c7;
+# }}
+# QFrame#aboutCard QLabel#label {{
+# color: #b6b6b6;
+# font-weight: 600;
+# }}
+# QFrame#aboutCard QLabel#value {{
+# color: #e6e6e6;
+# }}
+# QFrame#aboutCard QLabel.badge {{
+# background: #343a40;
+# border: 1px solid #454d55;
+# border-radius: 8px;
+# padding: 2px 10px;
+# color: #e6e6e6;
+# font-size: 11px;
+# }}
+# QFrame#aboutCard QFrame#divider {{
+# background: #3a3a3a;
+# min-height: 1px; max-height: 1px; border: none;
+# }}
+# QFrame#aboutCard a {{
+# color: #6aa9ff;
+# text-decoration: none;
+# }}
+
+# #aboutCard {{
+# background-color: #3c3c3c;
+# border: 1px solid #4a4a4a;
+# border-radius: 12px;
+# }}
+# #aboutCard QLabel#title {{ color: #eaeaea; font-size: 18pt; font-weight: bold; }}
+# #aboutCard QLabel#subtitle {{ color: #a0a0a0; }}
+# #aboutCard QLabel#label {{ color: #b0b0b0; font-weight: bold; }}
+# #aboutCard QLabel.badge {{
+# background-color: #555555; border-radius: 8px;
+# padding: 3px 10px; font-size: 9pt;
+# }}
+# #aboutCard QFrame#divider {{ background-color: #4a4a4a; max-height: 1px; border: none; }}
+# #aboutCard a {{ color: #6aa9ff; text-decoration: none; }}
+
+# """
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# --- Dynamic Theme Stylesheets ---
def get_light_theme_styles(scale=1.0):
-
log_font_size = max(10, int(11 * scale))
button_font_size = max(7, int(10 * scale))
+ big_button_font = int(button_font_size * 13 // 10) # 1.3x as int
return f"""
QMainWindow, QWidget {{
background-color: #f0f0f0;
color: #000;
}}
+
+ /* Status Bar */
+ #StatusBar {{
+ background-color: #34495e;
+ padding: {int(6 * scale)}px;
+ }}
+
+ /* Logs */
#LogPanel {{
font-family: "Courier New", Consolas, monospace;
font-size: {log_font_size}pt;
- background-color: #212121;
- color: #eceff1;
- border: 1px solid #455a64;
+ background-color: #ffffff;
+ color: #2b2b2b;
+ border: 1px solid #c8c8c8;
}}
+
+ /* Containers */
QGroupBox {{
font-family: Arial;
- border: 1px solid #4a4a4a;
+ border: 1px solid #c8c8c8;
border-radius: {int(8 * scale)}px;
margin-top: {int(6 * scale)}px;
}}
@@ -29,6 +455,8 @@ def get_light_theme_styles(scale=1.0):
padding: 0 {int(10 * scale)}px;
color: #000;
}}
+
+ /* Tabs */
QTabWidget::pane {{ border-top: 2px solid #c8c8c8; }}
QTabBar::tab {{
background: #e1e1e1; border: 1px solid #c8c8c8;
@@ -36,91 +464,212 @@ def get_light_theme_styles(scale=1.0):
border-top-left-radius: {int(4 * scale)}px; border-top-right-radius: {int(4 * scale)}px;
}}
QTabBar::tab:selected {{ background: #f0f0f0; border-bottom-color: #f0f0f0; }}
+
+ /* Forms */
QFormLayout::label {{ color: #000; padding-top: {int(3 * scale)}px; }}
QLineEdit, QPlainTextEdit, QComboBox {{
background-color: #fff; border: 1px solid #c8c8c8;
border-radius: {int(4 * scale)}px; padding: {int(4 * scale)}px;
font-size: {max(7, int(9 * scale))}pt;
+ color: #000;
}}
QLineEdit:read-only {{ background-color: #e9e9e9; }}
+
+ /* Buttons (generic) */
QPushButton {{
background-color: #e1e1e1; border: 1px solid #c8c8c8;
padding: {int(5 * scale)}px {int(10 * scale)}px;
border-radius: {int(4 * scale)}px;
+ color: #000;
}}
QPushButton:hover {{ background-color: #dcdcdc; }}
QPushButton:pressed {{ background-color: #c8c8c8; }}
- #RefreshButton, #ResetButton {{
- padding: {int(6 * scale)}px {int(16 * scale)}px;
- font-size: {button_font_size * 1.3}pt;
- font-weight: bold;
- border-radius: {int(4*scale)}px;
- }}
- #RefreshButton {{
- background-color: #2e7d32; /* A slightly darker green */
- }}
- #ResetButton {{
- background-color: #c62828; /* A slightly darker red */
- }}
- #ChamberOpenDoorButton, #ChamberChgOnButton, #ChamberChgOffButton {{
- padding: {int(8 * scale)}px;
- font-size: {button_font_size}pt;
- font-weight: bold;
- border-radius: {int(4*scale)}px;
- }}
-
- #ChamberOpenDoorButton {{ background-color: #E1E1E1; }}
- #ChamberChgOnButton {{ background-color: #E1E1E1; }}
- #ChamberChgOffButton {{ background-color: #E1E1E1; }}
-
- #ChamberOpenDoorButton:hover {{ background-color: #3498DB; }}
- #ChamberChgOnButton:hover {{ background-color: #229954; }}
- #ChamberChgOffButton:hover {{ background-color: #c0392b; }}
-
QPushButton:disabled {{ background-color: #d3d3d3; color: #a0a0a0; }}
-
- #ConnectButton, #DisconnectButton {{
- padding: {int(6 * scale)}px {int(16 * scale)}px;
- font-size: {button_font_size}pt;
- font-weight: bold;
- border-radius: {int(4 * scale)}px;
- color: white;
+
+ /* Primary/Destructive (larger) */
+ #RefreshButton, #ResetButton {{
+ padding: {int(6 * scale)}px {int(16 * scale)}px;
+ font-size: {big_button_font}pt;
+ font-weight: bold;
+ color: white;
+ border-radius: {int(4*scale)}px;
}}
+ #RefreshButton {{ background-color: #2e7d32; }}
+ #ResetButton {{ background-color: #c62828; }}
- #ConnectButton {{ background-color: #27ae60; }} /* Green */
- #DisconnectButton {{ background-color: #c0392b; }} /* Red */
-
- #ConnectButton:hover {{ background-color: #52be80; }}
- #DisconnectButton:hover {{ background-color: #cd6155; }}
-
- #ConnectButton:pressed {{ background-color: #52be80; }}
- #DisconnectButton:pressed {{ background-color: #cd6155; }}
-
+ /* Action buttons */
+ #ConnectButton, #DisconnectButton, #StartSwapButton, #AbortSwapButton {{
+ padding: {int(6 * scale)}px {int(16 * scale)}px;
+ font-size: {button_font_size}pt;
+ font-weight: bold;
+ border-radius: {int(4 * scale)}px;
+ color: white;
+ border: none;
+ }}
+ #ConnectButton, #StartSwapButton {{ background-color: #28a745; }}
+ #ConnectButton:hover, #StartSwapButton:hover {{ background-color: #218838; }}
+ #DisconnectButton, #AbortSwapButton {{ background-color: #dc3545; }}
+ #DisconnectButton:hover, #AbortSwapButton:hover {{ background-color: #c82333; }}
#ConnectButton:disabled, #DisconnectButton:disabled {{
- background-color: #546e7a;
- color: #90a4ae;
+ background-color: #b0bec5; color: #78909c;
}}
- #RefreshButton, #StartSwapButton {{ background-color: #27ae60; color: white; border: none; }}
- #RefreshButton:hover, #StartSwapButton:hover {{ background-color: #229954; }}
- #ResetButton, #AbortSwapButton {{ background-color: #c0392b; color: white; border: none; }}
- #ResetButton:hover, #AbortSwapButton:hover {{ background-color: #c0392b; }}
- #SendAudioButton {{ background-color: #3498db; color: white; border: none; font-size: {max(10, int(14 * scale))}px; }}
- #SendAudioButton:hover {{ background-color: #2980b9; }}
- QLabel[status="present"] {{ background-color: #2ecc71; color: white; border-radius: {int(4*scale)}px; padding: {int(3*scale)}px; }}
- QLabel[status="absent"] {{ background-color: #e74c3c; color: white; border-radius: {int(4*scale)}px; padding: {int(3*scale)}px; }}
- QLabel[alarm="active"] {{ background-color: #e74c3c; color: white; font-weight: bold; border-radius: {int(4*scale)}px; padding: {int(2*scale)}px; }}
+ /* Unique */
+ #SendAudioButton {{
+ background-color: #007bff; color: white; border: none;
+ font-size: {max(10, int(14 * scale))}px;
+ }}
+ #SendAudioButton:hover {{ background-color: #0069d9; }}
+
+ #ChamberOpenDoorButton, #ChamberChgOnButton, #ChamberChgOffButton {{
+ padding: {int(8 * scale)}px;
+ font-size: {button_font_size}pt;
+ font-weight: bold;
+ border-radius: {int(4*scale)}px;
+ }}
+ #ChamberOpenDoorButton:hover {{ background-color: #607d8b; }}
+ #ChamberChgOnButton:hover {{ background-color: #52be80; }}
+ #ChamberChgOffButton:hover {{ background-color: #cd6155; }}
+
+ /* Status & alarms */
+ QLabel[status="present"] {{ background-color: #2ecc71; color: white; border-radius: {int(4*scale)}px; padding: {int(3*scale)}px; }}
+ QLabel[status="absent"] {{ background-color: #e74c3c; color: white; border-radius: {int(4*scale)}px; padding: {int(3*scale)}px; }}
+ QLabel[alarm="active"] {{ background-color: #e74c3c; color: white; font-weight: bold; border-radius: {int(4*scale)}px; padding: {int(2*scale)}px; }}
QLabel[alarm="inactive"] {{ background-color: transparent; color: black; }}
- QGroupBox#ChamberWidget {{ border: 2px solid #3498db; }}
+ QGroupBox#ChamberWidget {{ border: 2px solid #007bff; }}
+
+ /* Timestamp */
+ QLabel#TimestampTitleLabel {{
+ font-size: {max(9, int(11 * scale))}pt; font-weight: bold; color: #333;
+ }}
+ QLineEdit#TimestampDataField {{
+ font-size: {max(11, int(13 * scale))}pt; font-weight: bold; color: #000;
+ }}
+
+ /* ---------- ABOUT CARD (LIGHT) ---------- */
+ #aboutCard {{
+ background: #ffffff;
+ border: 1px solid #dee2e6;
+ border-radius: 12px;
+ }}
+ #aboutCard #title {{
+ color: #212529;
+ font-size: {max(16, int(18 * scale))}pt;
+ font-weight: 700;
+ letter-spacing: 0.2px;
+ }}
+ #aboutCard #subtitle {{
+ color: #6c757d;
+ font-size: {max(9, int(11 * scale))}pt;
+ font-weight: 500;
+ }}
+ #aboutCard #kvLabel {{
+ color: #495057;
+ font-weight: 600;
+ }}
+ #aboutCard #badge {{
+ color: #0b1220;
+ background: #e9ecef;
+ border-radius: 999px;
+ padding: {int(5*scale)}px {int(12*scale)}px;
+ font-weight: 700;
+ letter-spacing: 0.3px;
+ font-size: {max(8, int(10*scale))}pt;
+ }}
+ #aboutCard #divider {{
+ background: #dee2e6;
+ min-height: 1px; max-height: 1px; border: none;
+ margin: {int(6*scale)}px 0 {int(8*scale)}px 0;
+ }}
+ #aboutCard #footer {{
+ color: #7a8794;
+ font-size: {max(8, int(10*scale))}pt;
+ }}
+ #aboutCard a {{
+ color: #007bff; text-decoration: none;
+ }}
+ #aboutCard a:hover {{
+ text-decoration: underline;
+ }}
+
+ #aboutCard [link="true"] {{
+ color: #0d6efd; /* Bootstrap dark blue */
+ text-decoration: none;
+ }}
+
+ #aboutCard [link="true"]:hover {{
+ color: #0a58ca; /* darker blue on hover */
+ text-decoration: underline;
+ }}
+
+ /* ---------- HELP CARD (LIGHT) ---------- */
+ #helpCard {{
+ background: #ffffff;
+ border: 1px solid #dee2e6;
+ border-radius: 12px;
+ }}
+
+ #helpTitle {{
+ color: #212529;
+ font-weight: 700;
+ font-size: 18px;
+ }}
+
+ #helpSubtitle {{
+ color: #495057;
+ font-size: 12px;
+ }}
+
+ #helpDivider {{
+ background: #dee2e6;
+ min-height: 1px; max-height: 1px; border: none;
+ }}
+
+ #sectionTitle {{
+ color: #212529;
+ font-weight: 600;
+ margin-top: 6px;
+ }}
+
+ #bodyText {{
+ color: #343a40;
+ }}
+
+ /* Callouts (light theme) */
+ #tipBox {{
+ background: #e6ffed;
+ border: 1px solid #28a745;
+ border-radius: 8px;
+ }}
+ #tipTitle {{ color: #155724; font-weight: 700; }}
+ #tipText {{ color: #155724; }}
+
+ #warnBox {{
+ background: #fff3cd;
+ border: 1px solid #ffb74d;
+ border-radius: 8px;
+ }}
+ #warnTitle {{ color: #856404; font-weight: 700; }}
+ #warnText {{ color: #856404; }}
+
+ #link {{
+ color: #0827F5; /* Current */
+ text-decoration: none;
+ }}
+ #link:hover {{
+ color: #00008b; /* Hover color */
+ text-decoration: underline;
+ }}
+
"""
def get_dark_theme_styles(scale=1.0):
-
log_font_size = max(10, int(11 * scale))
button_font_size = max(7, int(10 * scale))
return f"""
QMainWindow, QWidget {{ background-color: #2b2b2b; color: #f0f0f0; }}
+
#LogPanel {{
font-family: "Courier New", Consolas, monospace;
font-size: {log_font_size}pt;
@@ -128,11 +677,13 @@ def get_dark_theme_styles(scale=1.0):
color: #eceff1;
border: 1px solid #455a64;
}}
+
QGroupBox {{
font-family: Arial; border: 1px solid #4a4a4a;
border-radius: {int(8 * scale)}px; margin-top: {int(6 * scale)}px;
}}
QGroupBox::title {{ subcontrol-origin: margin; subcontrol-position: top center; padding: 0 {int(10 * scale)}px; color: #f0f0f0; }}
+
QTabWidget::pane {{ border-top: 2px solid #4a4a4a; }}
QTabBar::tab {{
background: #3c3c3c; border: 1px solid #4a4a4a; color: #f0f0f0;
@@ -140,6 +691,7 @@ def get_dark_theme_styles(scale=1.0):
border-top-left-radius: {int(4 * scale)}px; border-top-right-radius: {int(4 * scale)}px;
}}
QTabBar::tab:selected {{ background: #2b2b2b; border-bottom-color: #2b2b2b; }}
+
QFormLayout::label {{ color: #f0f0f0; padding-top: {int(3 * scale)}px; }}
QLineEdit, QPlainTextEdit, QComboBox {{
background-color: #3c3c3c; border: 1px solid #4a4a4a;
@@ -147,6 +699,7 @@ def get_dark_theme_styles(scale=1.0):
font-size: {max(7, int(9 * scale))}pt;
}}
QLineEdit:read-only {{ background-color: #333333; }}
+
QPushButton {{
background-color: #555555; border: 1px solid #4a4a4a;
padding: {int(5 * scale)}px {int(10 * scale)}px;
@@ -155,53 +708,44 @@ def get_dark_theme_styles(scale=1.0):
QPushButton:hover {{ background-color: #6a6a6a; }}
QPushButton:pressed {{ background-color: #4a4a4a; }}
QPushButton:disabled {{ background-color: #404040; color: #888888; }}
+
#RefreshButton, #ResetButton {{
- padding: {int(6 * scale)}px {int(16 * scale)}px;
- font-size: {button_font_size * 1.3}pt;
- font-weight: bold;
- border-radius: {int(4*scale)}px;
- }}
- #RefreshButton {{
- background-color: #2e7d32; /* A slightly darker green */
- }}
- #ResetButton {{
- background-color: #c62828; /* A slightly darker red */
- }}
+ padding: {int(6 * scale)}px {int(16 * scale)}px;
+ font-size: {int(button_font_size * 1.3)}pt;
+ font-weight: bold;
+ border-radius: {int(4*scale)}px;
+ }}
+ #RefreshButton {{ background-color: #2e7d32; }}
+ #ResetButton {{ background-color: #c62828; }}
+
#ChamberOpenDoorButton, #ChamberChgOnButton, #ChamberChgOffButton {{
- padding: {int(8 * scale)}px;
- font-size: {button_font_size}pt;
- font-weight: bold;
- border-radius: {int(4*scale)}px;
- }}
-
- #ChamberOpenDoorButton {{ background-color: #3C3C3C; }}
- #ChamberChgOnButton {{ background-color: #3C3C3C; }}
- #ChamberChgOffButton {{ background-color: #3C3C3C; }}
-
- #ChamberOpenDoorButton:hover {{ background-color: #607d8b; }}
- #ChamberChgOnButton:hover {{ background-color: #52be80; }}
- #ChamberChgOffButton:hover {{ background-color: #cd6155; }}
-
- #ConnectButton, #DisconnectButton {{
- padding: {int(6 * scale)}px {int(16 * scale)}px;
- font-size: {button_font_size}pt;
- font-weight: bold;
- border-radius: {int(4*scale)}px;
- color: white;
- }}
-
- #ConnectButton {{ background-color: #27ae60; }} /* Green */
- #DisconnectButton {{ background-color: #c0392b; }} /* Red */
+ padding: {int(8 * scale)}px;
+ font-size: {button_font_size}pt;
+ font-weight: bold;
+ border-radius: {int(4*scale)}px;
+ }}
+ #ChamberOpenDoorButton {{ background-color: #3C3C3C; }}
+ #ChamberChgOnButton {{ background-color: #3C3C3C; }}
+ #ChamberChgOffButton {{ background-color: #3C3C3C; }}
+ #ChamberOpenDoorButton:hover {{ background-color: #607d8b; }}
+ #ChamberChgOnButton:hover {{ background-color: #52be80; }}
+ #ChamberChgOffButton:hover {{ background-color: #cd6155; }}
+ #ConnectButton, #DisconnectButton {{
+ padding: {int(6 * scale)}px {int(16 * scale)}px;
+ font-size: {button_font_size}pt;
+ font-weight: bold;
+ border-radius: {int(4*scale)}px;
+ color: white;
+ }}
+ #ConnectButton {{ background-color: #27ae60; }}
+ #DisconnectButton {{ background-color: #c0392b; }}
#ConnectButton:hover {{ background-color: #52be80; }}
#DisconnectButton:hover {{ background-color: #cd6155; }}
-
#ConnectButton:pressed {{ background-color: #52be80; }}
#DisconnectButton:pressed {{ background-color: #cd6155; }}
-
#ConnectButton:disabled, #DisconnectButton:disabled {{
- background-color: #546e7a;
- color: #90a4ae;
+ background-color: #546e7a; color: #90a4ae;
}}
#RefreshButton, #StartSwapButton {{ background-color: #27ae60; color: white; border: none; }}
@@ -210,9 +754,126 @@ def get_dark_theme_styles(scale=1.0):
#ResetButton:hover, #AbortSwapButton:hover {{ background-color: #cd6155; }}
#SendAudioButton {{ background-color: #3498db; color: white; border: none; font-size: {max(10, int(14 * scale))}px; }}
#SendAudioButton:hover {{ background-color: #5dade2; }}
+
QLabel[status="present"] {{ background-color: #2ecc71; color: white; border-radius: {int(4*scale)}px; padding: {int(3*scale)}px; }}
- QLabel[status="absent"] {{ background-color: #e74c3c; color: white; border-radius: {int(4*scale)}px; padding: {int(3*scale)}px; }}
- QLabel[alarm="active"] {{ background-color: #e74c3c; color: white; font-weight: bold; border-radius: {int(4*scale)}px; padding: {int(2*scale)}px; }}
+ QLabel[status="absent"] {{ background-color: #e74c3c; color: white; border-radius: {int(4*scale)}px; padding: {int(3*scale)}px; }}
+ QLabel[alarm="active"] {{ background-color: #e74c3c; color: white; font-weight: bold; border-radius: {int(4*scale)}px; padding: {int(2*scale)}px; }}
QLabel[alarm="inactive"] {{ background-color: transparent; color: #f0f0f0; }}
- QGroupBox#ChamberWidget {{ border: 2px solid #3498db; }}
- """
\ No newline at end of file
+ QGroupBox#ChamberWidget {{ border: 2px solid #3498db; }}
+
+ QLabel#TimestampTitleLabel {{
+ font-size: {max(9, int(11 * scale))}pt;
+ font-weight: bold;
+ }}
+ QLineEdit#TimestampDataField {{
+ font-size: {max(11, int(13 * scale))}pt;
+ font-weight: bold;
+ color: #f0f0f0;
+ }}
+
+ QGroupBox#ChamberWidget QFormLayout QLabel {{
+ font-size: {max(10, int(12 * scale))}px;
+ color: #AAAAAA;
+ }}
+ QGroupBox#ChamberWidget QLineEdit#BatIdField {{
+ font-size: {max(14, int(16 * scale))}px;
+ font-weight: bold;
+ color: #FFFFFF;
+ }}
+ QGroupBox#ChamberWidget QLineEdit#DataField {{
+ font-size: {max(13, int(14 * scale))}px;
+ color: #E0E0E0;
+ }}
+ QGroupBox#ChamberWidget QLineEdit#DoorStatusField {{
+ font-size: {max(12, int(14 * scale))}px;
+ font-weight: bold;
+ color: #E0E0E0;
+ }}
+
+ /* ---------- ABOUT CARD (DARK) ---------- */
+ #aboutCard {{
+ background: #2a2a2a;
+ border: 1px solid #3a3a3a;
+ border-radius: 12px;
+ }}
+ #aboutCard #title {{
+ color: #eaeaea;
+ font-size: {max(16, int(18 * scale))}pt;
+ font-weight: 700;
+ letter-spacing: 0.2px;
+ }}
+ #aboutCard #subtitle {{
+ color: #aab4c0;
+ font-size: {max(9, int(11 * scale))}pt;
+ font-weight: 500;
+ }}
+ #aboutCard #kvLabel {{
+ color: #b6b6b6;
+ font-weight: 600;
+ }}
+ #aboutCard #badge {{
+ color: #eaf3ff;
+ background: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #2563eb, stop:1 #7c3aed);
+ border-radius: 999px;
+ padding: {int(5*scale)}px {int(12*scale)}px;
+ font-weight: 700;
+ letter-spacing: 0.3px;
+ font-size: {max(8, int(10*scale))}pt;
+ text-transform: uppercase;
+ }}
+ #aboutCard #divider {{
+ background: #3a3a3a;
+ margin: {int(6*scale)}px 0 {int(8*scale)}px 0;
+ min-height: 1px;
+ max-height: 1px;
+ border: none;
+ }}
+ #aboutCard #footer {{
+ color: #7e8895;
+ font-size: {max(8, int(10*scale))}pt;
+ }}
+ #aboutCard a {{
+ color: #6aa9ff; text-decoration: none;
+ }}
+ #aboutCard a:hover {{ color: #82b1ff; text-decoration: underline; }}
+
+ /* ---------- HELP CARD (DARK) ---------- */
+ #helpCard {{
+ background: #2a2a2a;
+ border: 1px solid #3a3a3a;
+ border-radius: 12px;
+ }}
+
+ #helpTitle {{ color: #eaeaea; font-weight: 700; font-size: 18px; }}
+ #helpSubtitle {{ color: #c8c8c8; font-size: 12px; }}
+ #helpDivider {{ background: #3a3a3a; min-height: 1px; max-height: 1px; border: none; }}
+ #sectionTitle {{ color: #e6e6e6; font-weight: 600; margin-top: 6px; }}
+ #bodyText {{ color: #dcdcdc; }}
+
+ /* Callouts (dark theme) */
+ #tipBox {{
+ background: #1f3326;
+ border: 1px solid #62d39b;
+ border-radius: 8px;
+ }}
+ #tipTitle {{ color: #a8f5c9; font-weight: 700; }}
+ #tipText {{ color: #d6ffe9; }}
+
+ #warnBox {{
+ background: #3b2e1b;
+ border: 1px solid #ffb74d;
+ border-radius: 8px;
+ }}
+ #warnTitle {{ color: #ffd561; font-weight: 700; }}
+ #warnText {{ color: #f0e0c0; }}
+
+ #link {{
+ color: #007bff; /* Standard Bootstrap blue */
+ text-decoration: none;
+ }}
+ #link:hover {{
+ color: #0056b3; /* Darker blue on hover */
+ text-decoration: underline;
+ }}
+
+ """
diff --git a/ui/widgets.py b/ui/widgets.py
index 744722d..e4eeb5f 100644
--- a/ui/widgets.py
+++ b/ui/widgets.py
@@ -17,24 +17,6 @@ class ChamberWidget(QGroupBox):
self.setObjectName("ChamberWidget")
self.setFont(QFont("Arial", max(8, int(9 * scale)), QFont.Weight.Bold))
- # --- ADD THIS STYLESHEET TO CONTROL FONT SIZES ---
- self.setStyleSheet(f"""
- QFormLayout QLabel {{
- font-size: {max(10, int(12 * scale))}px;
- color: #AAAAAA;
- }}
- QLineEdit#BatIdField {{
- font-size: {max(14, int(16 * scale))}px;
- font-weight: bold;
- color: #FFFFFF;
- }}
- QLineEdit#DataField {{
- font-size: {max(13, int(14 * scale))}px;
- color: #E0E0E0;
- }}
- """)
- # --- END OF STYLESHEET ---
-
main_layout = QVBoxLayout(self)
main_layout.setSpacing(max(2, int(4 * scale)))
@@ -90,7 +72,7 @@ class ChamberWidget(QGroupBox):
self.chg_temp_field.setObjectName("DataField")
self.door_status_field = self._create_data_field(scale)
- self.door_status_field.setObjectName("DataField")
+ self.door_status_field.setObjectName("DoorStatusField")
self.charger_fault_field = self._create_data_field(scale)
self.charger_fault_field.setObjectName("DataField")