From 5d35b9a34d0e09ce6544747dd613e6f8c67587e8 Mon Sep 17 00:00:00 2001 From: Kirubakaran Date: Thu, 21 Aug 2025 04:14:12 +0530 Subject: [PATCH] feat(ui): polish About & Help tabs; theme-driven styles About: modern card, clickable mail/URL (TextBrowserInteraction + openExternalLinks), single-line values, link color via palette; layout reuse to avoid warnings. Help: scrollable card; moved all styling to theme QSS; added Quick Start, Tips, Warnings, Troubleshooting sections. Theme: added HELP/ABOUT blocks for light & dark; cast font sizes to int to prevent light-mode crashes; consolidated #aboutCard rules; link colors per theme. Fix: removed setLayout(None) and avoided re-adding layouts to the same widget. --- ui/main_window.py | 451 ++++++++++++------------ ui/styles.py | 869 ++++++++++++++++++++++++++++++++++++++++------ ui/widgets.py | 20 +- 3 files changed, 986 insertions(+), 354 deletions(-) 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( "" ) 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( "" ) - 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( "" ) - 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( "" ) - 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")