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.main
parent
0012e5502c
commit
5d35b9a34d
|
|
@ -11,7 +11,7 @@ from PyQt6.QtWidgets import (
|
||||||
QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QTabWidget,
|
QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QTabWidget,
|
||||||
QGroupBox, QFormLayout, QLineEdit, QPushButton, QLabel, QSpacerItem,
|
QGroupBox, QFormLayout, QLineEdit, QPushButton, QLabel, QSpacerItem,
|
||||||
QSizePolicy, QGridLayout, QMessageBox, QComboBox, QPlainTextEdit,
|
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 PyQt6.QtSvgWidgets import QSvgWidget
|
||||||
from google.protobuf.json_format import MessageToDict
|
from google.protobuf.json_format import MessageToDict
|
||||||
|
|
@ -104,110 +104,62 @@ class MainWindow(QMainWindow):
|
||||||
self.load_settings()
|
self.load_settings()
|
||||||
self._apply_theme()
|
self._apply_theme()
|
||||||
|
|
||||||
|
|
||||||
def setup_help_ui(self):
|
def setup_help_ui(self):
|
||||||
"""
|
"""
|
||||||
Polished Help page:
|
Help page (theme-driven):
|
||||||
- Card-style container with title
|
- Card-style container with title
|
||||||
- Sections: Quick Start, Warnings, Troubleshooting, Shortcuts
|
- Sections: Quick Start, Tips, Warnings, Troubleshooting
|
||||||
- Colored callouts for warnings/tips
|
- Scrollable host
|
||||||
- Scrollable if content grows
|
|
||||||
"""
|
"""
|
||||||
# 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()
|
||||||
root = QVBoxLayout(self.help_tab)
|
if root is None:
|
||||||
|
root = QVBoxLayout()
|
||||||
root.setContentsMargins(12, 12, 12, 12)
|
root.setContentsMargins(12, 12, 12, 12)
|
||||||
root.setSpacing(10)
|
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()
|
||||||
|
|
||||||
# Scroll area so long help stays usable
|
# scroll host
|
||||||
scroll = QScrollArea()
|
scroll = QScrollArea()
|
||||||
scroll.setWidgetResizable(True)
|
scroll.setWidgetResizable(True)
|
||||||
scroll.setFrameShape(QFrame.Shape.NoFrame)
|
scroll.setFrameShape(QFrame.Shape.NoFrame)
|
||||||
|
|
||||||
host = QWidget()
|
host = QWidget()
|
||||||
scroll_lay = QVBoxLayout(host)
|
host_lay = QVBoxLayout(host)
|
||||||
scroll_lay.setContentsMargins(0, 0, 0, 0)
|
host_lay.setContentsMargins(0, 0, 0, 0)
|
||||||
scroll_lay.setSpacing(12)
|
host_lay.setSpacing(12)
|
||||||
|
|
||||||
# Card container
|
# card (styled by theme via objectNames)
|
||||||
card = QFrame()
|
card = QFrame()
|
||||||
card.setObjectName("helpCard")
|
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 = QVBoxLayout(card)
|
||||||
card_lay.setContentsMargins(18, 16, 18, 16)
|
card_lay.setContentsMargins(18, 16, 18, 16)
|
||||||
card_lay.setSpacing(12)
|
card_lay.setSpacing(12)
|
||||||
|
|
||||||
# Header
|
# header
|
||||||
title = QLabel("Help & User Guide")
|
title = QLabel("Help & User Guide"); title.setObjectName("helpTitle")
|
||||||
title.setProperty("class", "title")
|
|
||||||
subtitle = QLabel("Follow these steps to get connected, monitor the station, and troubleshoot issues.")
|
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(title)
|
||||||
card_lay.addWidget(subtitle)
|
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)
|
card_lay.addWidget(div1)
|
||||||
|
|
||||||
# Quick Start
|
# Quick Start
|
||||||
qs_title = QLabel("Quick Start")
|
qs_title = QLabel("Quick Start"); qs_title.setObjectName("sectionTitle")
|
||||||
qs_title.setProperty("class", "h3")
|
|
||||||
qs = QLabel(
|
qs = QLabel(
|
||||||
"<ul style='margin:0 0 0 16px'>"
|
"<ul style='margin:0 0 0 16px'>"
|
||||||
"<li><b>Configure</b>: Open <b>Config</b>, fill MQTT details, click <b>Connect</b>.</li>"
|
"<li><b>Configure</b>: Open <b>Config</b>, fill MQTT details, click <b>Connect</b>.</li>"
|
||||||
|
|
@ -218,16 +170,14 @@ class MainWindow(QMainWindow):
|
||||||
"</ul>"
|
"</ul>"
|
||||||
)
|
)
|
||||||
qs.setOpenExternalLinks(True)
|
qs.setOpenExternalLinks(True)
|
||||||
qs.setProperty("class", "body")
|
qs.setObjectName("bodyText")
|
||||||
|
|
||||||
card_lay.addWidget(qs_title)
|
card_lay.addWidget(qs_title)
|
||||||
card_lay.addWidget(qs)
|
card_lay.addWidget(qs)
|
||||||
|
|
||||||
# Tips (green)
|
# Tips
|
||||||
tip_box = QFrame(); tip_box.setObjectName("tip"); tip_box.setProperty("class", "tip")
|
tip_box = QFrame(); tip_box.setObjectName("tipBox")
|
||||||
tip_lay = QVBoxLayout(tip_box); tip_lay.setContentsMargins(12, 10, 12, 10)
|
tip_lay = QVBoxLayout(tip_box); tip_lay.setContentsMargins(12, 10, 12, 10)
|
||||||
tip_h = QLabel("💡 Tips")
|
tip_h = QLabel("💡 Tips"); tip_h.setObjectName("tipTitle")
|
||||||
tip_h.setProperty("class", "tipTitle")
|
|
||||||
tip_b = QLabel(
|
tip_b = QLabel(
|
||||||
"<ul style='margin:0 0 0 16px'>"
|
"<ul style='margin:0 0 0 16px'>"
|
||||||
"<li>Use a stable, low-latency network for best live updates.</li>"
|
"<li>Use a stable, low-latency network for best live updates.</li>"
|
||||||
|
|
@ -235,17 +185,16 @@ class MainWindow(QMainWindow):
|
||||||
"<li>Use <b>Logs → Export</b> before clearing or reinstalling.</li>"
|
"<li>Use <b>Logs → Export</b> before clearing or reinstalling.</li>"
|
||||||
"</ul>"
|
"</ul>"
|
||||||
)
|
)
|
||||||
tip_b.setProperty("class", "tipText")
|
|
||||||
tip_b.setOpenExternalLinks(True)
|
tip_b.setOpenExternalLinks(True)
|
||||||
|
tip_b.setObjectName("tipText")
|
||||||
tip_lay.addWidget(tip_h)
|
tip_lay.addWidget(tip_h)
|
||||||
tip_lay.addWidget(tip_b)
|
tip_lay.addWidget(tip_b)
|
||||||
card_lay.addWidget(tip_box)
|
card_lay.addWidget(tip_box)
|
||||||
|
|
||||||
# Warnings (amber)
|
# Warnings
|
||||||
warn_box = QFrame(); warn_box.setObjectName("warn"); warn_box.setProperty("class", "warn")
|
warn_box = QFrame(); warn_box.setObjectName("warnBox")
|
||||||
warn_lay = QVBoxLayout(warn_box); warn_lay.setContentsMargins(12, 10, 12, 10)
|
warn_lay = QVBoxLayout(warn_box); warn_lay.setContentsMargins(12, 10, 12, 10)
|
||||||
warn_h = QLabel("⚠️ Important Warnings")
|
warn_h = QLabel("⚠️ Important Warnings"); warn_h.setObjectName("warnTitle")
|
||||||
warn_h.setProperty("class", "warnTitle")
|
|
||||||
warn_b = QLabel(
|
warn_b = QLabel(
|
||||||
"<ul style='margin:0 0 0 16px'>"
|
"<ul style='margin:0 0 0 16px'>"
|
||||||
"<li><b>Disconnected</b> state means no live data. Check server, network, and credentials.</li>"
|
"<li><b>Disconnected</b> state means no live data. Check server, network, and credentials.</li>"
|
||||||
|
|
@ -253,14 +202,13 @@ class MainWindow(QMainWindow):
|
||||||
"<li>Commands during disconnect may be lost. Reconnect before critical actions.</li>"
|
"<li>Commands during disconnect may be lost. Reconnect before critical actions.</li>"
|
||||||
"</ul>"
|
"</ul>"
|
||||||
)
|
)
|
||||||
warn_b.setProperty("class", "warnText")
|
warn_b.setObjectName("warnText")
|
||||||
warn_lay.addWidget(warn_h)
|
warn_lay.addWidget(warn_h)
|
||||||
warn_lay.addWidget(warn_b)
|
warn_lay.addWidget(warn_b)
|
||||||
card_lay.addWidget(warn_box)
|
card_lay.addWidget(warn_box)
|
||||||
|
|
||||||
# Troubleshooting
|
# Troubleshooting
|
||||||
tr_title = QLabel("Troubleshooting")
|
tr_title = QLabel("Troubleshooting"); tr_title.setObjectName("sectionTitle")
|
||||||
tr_title.setProperty("class", "h3")
|
|
||||||
tr = QLabel(
|
tr = QLabel(
|
||||||
"<ul style='margin:0 0 0 16px'>"
|
"<ul style='margin:0 0 0 16px'>"
|
||||||
"<li><b>Cannot connect</b>: Verify broker/port, username/password, and firewall.</li>"
|
"<li><b>Cannot connect</b>: Verify broker/port, username/password, and firewall.</li>"
|
||||||
|
|
@ -269,175 +217,197 @@ class MainWindow(QMainWindow):
|
||||||
"<li><b>Wrong station showing</b>: Confirm the <b>Device ID</b> matches the station you expect.</li>"
|
"<li><b>Wrong station showing</b>: Confirm the <b>Device ID</b> matches the station you expect.</li>"
|
||||||
"</ul>"
|
"</ul>"
|
||||||
)
|
)
|
||||||
tr.setProperty("class", "body")
|
tr.setObjectName("bodyText")
|
||||||
card_lay.addWidget(tr_title)
|
card_lay.addWidget(tr_title)
|
||||||
card_lay.addWidget(tr)
|
card_lay.addWidget(tr)
|
||||||
|
|
||||||
# Footer
|
# footer
|
||||||
div2 = QFrame(); div2.setObjectName("divider"); div2.setProperty("class", "divider")
|
div2 = QFrame(); div2.setObjectName("helpDivider")
|
||||||
card_lay.addWidget(div2)
|
card_lay.addWidget(div2)
|
||||||
foot = QLabel("Need help? Email <a href='mailto:kirubakaran@vecmocon.com' class='link'>kirubakaran@vecmocon.com</a>")
|
foot = QLabel("Need help? Email "
|
||||||
|
"<a href='mailto:kirubakaran@vecmocon.com' style='color:#0d6efd; text-decoration:none'>"
|
||||||
|
"kirubakaran@vecmocon.com</a>")
|
||||||
foot.setOpenExternalLinks(True)
|
foot.setOpenExternalLinks(True)
|
||||||
foot.setProperty("class", "body")
|
foot.setObjectName("bodyText")
|
||||||
card_lay.addWidget(foot)
|
card_lay.addWidget(foot)
|
||||||
|
|
||||||
scroll_lay.addWidget(card)
|
# assemble
|
||||||
scroll_lay.addStretch(1)
|
host_lay.addWidget(card)
|
||||||
|
host_lay.addStretch(1)
|
||||||
scroll.setWidget(host)
|
scroll.setWidget(host)
|
||||||
root.addWidget(scroll)
|
root.addWidget(scroll)
|
||||||
|
|
||||||
def setup_about_ui(self):
|
def _about_stylesheet() -> str:
|
||||||
"""
|
return """
|
||||||
A clean, native-Qt About page:
|
/* Card */
|
||||||
- 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)
|
|
||||||
|
|
||||||
card = QFrame()
|
|
||||||
card.setObjectName("aboutCard")
|
|
||||||
card.setFrameShape(QFrame.Shape.NoFrame)
|
|
||||||
card.setStyleSheet("""
|
|
||||||
#aboutCard {
|
#aboutCard {
|
||||||
background: #2a2a2a;
|
background: #121417;
|
||||||
border: 1px solid #3a3a3a;
|
border: 1px solid #2a2f36;
|
||||||
border-radius: 12px;
|
border-radius: 16px;
|
||||||
}
|
}
|
||||||
QLabel#title {
|
|
||||||
color: #eaeaea;
|
/* Title + subtitle */
|
||||||
|
#title {
|
||||||
|
font-size: 22px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
|
color: #e8edf2;
|
||||||
|
letter-spacing: 0.2px;
|
||||||
}
|
}
|
||||||
QLabel#subtitle {
|
#subtitle {
|
||||||
color: #c7c7c7;
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #9aa4b2;
|
||||||
}
|
}
|
||||||
QLabel#label {
|
|
||||||
color: #b6b6b6;
|
/* Divider */
|
||||||
font-weight: 600;
|
#divider {
|
||||||
}
|
background: qlineargradient(x1:0, y1:0, x2:1, y2:0,
|
||||||
QLabel#value {
|
stop:0 #1b1f24, stop:0.5 #2a2f36, stop:1 #1b1f24);
|
||||||
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;
|
min-height: 1px;
|
||||||
max-height: 1px;
|
max-height: 1px;
|
||||||
border: none;
|
border: none;
|
||||||
|
margin: 6px 0 8px 0;
|
||||||
}
|
}
|
||||||
QLabel.link {
|
|
||||||
color: #6aa9ff;
|
|
||||||
}
|
|
||||||
""")
|
|
||||||
card_lay = QVBoxLayout(card)
|
|
||||||
card_lay.setContentsMargins(18, 16, 18, 16)
|
|
||||||
card_lay.setSpacing(10)
|
|
||||||
|
|
||||||
# ---- Header ----
|
/* 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):
|
||||||
|
# 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.setMinimumWidth(560)
|
||||||
|
card.setMaximumWidth(760)
|
||||||
|
card.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Maximum)
|
||||||
|
|
||||||
|
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 = QLabel("About This Application")
|
||||||
title.setObjectName("title")
|
title.setObjectName("title")
|
||||||
tfont = QFont()
|
|
||||||
tfont.setPointSize(16)
|
|
||||||
tfont.setBold(True)
|
|
||||||
title.setFont(tfont)
|
|
||||||
|
|
||||||
subtitle = QLabel("Battery Swap Station Dashboard")
|
subtitle = QLabel("Battery Swap Station Dashboard")
|
||||||
subtitle.setObjectName("subtitle")
|
subtitle.setObjectName("subtitle")
|
||||||
|
title_box.addWidget(title)
|
||||||
|
title_box.addWidget(subtitle)
|
||||||
|
header.addLayout(title_box, 1)
|
||||||
|
|
||||||
header = QVBoxLayout()
|
badge = QLabel("Version 4.0")
|
||||||
header.setSpacing(4)
|
badge.setObjectName("badge")
|
||||||
header.addWidget(title)
|
header.addWidget(badge, 0, Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
|
||||||
header.addWidget(subtitle)
|
|
||||||
|
|
||||||
# badges row
|
card_layout.addLayout(header)
|
||||||
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("")
|
|
||||||
|
|
||||||
badges.addWidget(ver, 0, Qt.AlignmentFlag.AlignLeft)
|
# --- divider ---
|
||||||
badges.addStretch(1)
|
divider = QFrame()
|
||||||
|
divider.setObjectName("divider")
|
||||||
|
card_layout.addWidget(divider)
|
||||||
|
|
||||||
card_lay.addLayout(header)
|
# --- key–value grid ---
|
||||||
card_lay.addLayout(badges)
|
|
||||||
|
|
||||||
# divider
|
|
||||||
div = QFrame()
|
|
||||||
div.setObjectName("divider")
|
|
||||||
card_lay.addWidget(div)
|
|
||||||
|
|
||||||
# ---- Key–Value grid ----
|
|
||||||
grid = QGridLayout()
|
grid = QGridLayout()
|
||||||
grid.setHorizontalSpacing(24)
|
grid.setHorizontalSpacing(18)
|
||||||
grid.setVerticalSpacing(10)
|
grid.setVerticalSpacing(10)
|
||||||
|
|
||||||
def add_row(r, key, val, is_link=False):
|
def add_row(r: int, key: str, value: str, is_link: bool = False):
|
||||||
l = QLabel(key)
|
k = QLabel(key)
|
||||||
l.setObjectName("label")
|
k.setObjectName("kvLabel")
|
||||||
v = QLabel(val)
|
v = QLabel(value)
|
||||||
v.setObjectName("value")
|
v.setWordWrap(False) # instead of True
|
||||||
|
|
||||||
if is_link:
|
if is_link:
|
||||||
v.setText(f'<a href="{val}">{val}</a>')
|
v.setTextFormat(Qt.TextFormat.RichText)
|
||||||
|
v.setTextInteractionFlags(Qt.TextInteractionFlag.TextBrowserInteraction) # links + selection + keyboard nav
|
||||||
v.setOpenExternalLinks(True)
|
v.setOpenExternalLinks(True)
|
||||||
v.setTextInteractionFlags(Qt.TextInteractionFlag.TextBrowserInteraction)
|
v.setProperty("link", True)
|
||||||
v.setObjectName("") # style via link class
|
|
||||||
v.setProperty("class", "link")
|
else:
|
||||||
grid.addWidget(l, r, 0, Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
|
v.setTextInteractionFlags(Qt.TextInteractionFlag.TextSelectableByMouse)
|
||||||
grid.addWidget(v, r, 1, Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter)
|
|
||||||
|
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(0, "Company", "VECMOCON TECHNOLOGIES")
|
||||||
add_row(1, "Developed by", "Kirubakaran S")
|
add_row(1, "Developed by", "Kirubakaran S")
|
||||||
add_row(2, "Support", "kirubakaran@vecmocon.com", is_link=True) # mailto auto-handled by client
|
add_row(2, "Support",
|
||||||
add_row(3, "Website", "https://www.vecmocon.com", is_link=True)
|
"<a href='mailto:kirubakaran@vecmocon.com' style='color:#0d6efd; text-decoration:none'>kirubakaran@vecmocon.com</a>",
|
||||||
|
is_link=True)
|
||||||
|
|
||||||
card_lay.addLayout(grid)
|
add_row(3, "Website",
|
||||||
|
"<a href='https://www.vecmocon.com' style='color:#0d6efd; text-decoration:none'>www.vecmocon.com</a>",
|
||||||
|
is_link=True)
|
||||||
|
|
||||||
# footer
|
card_layout.addLayout(grid)
|
||||||
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)
|
|
||||||
|
|
||||||
# center the card horizontally
|
# --- tiny space + footer ---
|
||||||
root.addWidget(card)
|
spacer = QFrame(); spacer.setFixedHeight(4)
|
||||||
root.addStretch(1)
|
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):
|
def load_settings(self):
|
||||||
|
|
@ -590,6 +560,8 @@ class MainWindow(QMainWindow):
|
||||||
# 3. Immediately apply the new theme underneath the overlay
|
# 3. Immediately apply the new theme underneath the overlay
|
||||||
self.is_dark_theme = not self.is_dark_theme
|
self.is_dark_theme = not self.is_dark_theme
|
||||||
self._apply_theme()
|
self._apply_theme()
|
||||||
|
self.setup_about_ui()
|
||||||
|
self.setup_help_ui()
|
||||||
|
|
||||||
# 4. Set up the opacity effect and animation for the overlay
|
# 4. Set up the opacity effect and animation for the overlay
|
||||||
opacity_effect = QGraphicsOpacityEffect(overlay)
|
opacity_effect = QGraphicsOpacityEffect(overlay)
|
||||||
|
|
@ -609,6 +581,7 @@ class MainWindow(QMainWindow):
|
||||||
self.animation.start(self.animation.DeletionPolicy.DeleteWhenStopped)
|
self.animation.start(self.animation.DeletionPolicy.DeleteWhenStopped)
|
||||||
|
|
||||||
def create_status_bar(self):
|
def create_status_bar(self):
|
||||||
|
|
||||||
status_bar_widget = QWidget()
|
status_bar_widget = QWidget()
|
||||||
status_bar_widget.setStyleSheet(f"background-color: #2c3e50; padding: {int(6*self.scale_factor)}px;")
|
status_bar_widget.setStyleSheet(f"background-color: #2c3e50; padding: {int(6*self.scale_factor)}px;")
|
||||||
status_bar_layout = QHBoxLayout(status_bar_widget)
|
status_bar_layout = QHBoxLayout(status_bar_widget)
|
||||||
|
|
@ -634,6 +607,17 @@ class MainWindow(QMainWindow):
|
||||||
right_layout.addStretch()
|
right_layout.addStretch()
|
||||||
self.connect_button = QPushButton("Connect")
|
self.connect_button = QPushButton("Connect")
|
||||||
self.disconnect_button = QPushButton("Disconnect")
|
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.connect_button.setObjectName("ConnectButton")
|
||||||
self.disconnect_button.setObjectName("DisconnectButton")
|
self.disconnect_button.setObjectName("DisconnectButton")
|
||||||
self.disconnect_button.setEnabled(False)
|
self.disconnect_button.setEnabled(False)
|
||||||
|
|
@ -781,14 +765,19 @@ class MainWindow(QMainWindow):
|
||||||
int(8 * self.scale_factor), int(8 * self.scale_factor)
|
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 = QFrame()
|
||||||
self.top_bar_frame.setObjectName("topBarFrame")
|
self.top_bar_frame.setObjectName("topBarFrame")
|
||||||
|
|
||||||
top_bar_layout = QHBoxLayout()
|
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 = QLineEdit("No Data")
|
||||||
self.last_recv_ts_field.setReadOnly(True)
|
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.addWidget(self.last_recv_ts_field)
|
||||||
top_bar_layout.addSpacerItem(QSpacerItem(20, 20, QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Minimum))
|
top_bar_layout.addSpacerItem(QSpacerItem(20, 20, QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Minimum))
|
||||||
|
|
||||||
|
|
|
||||||
795
ui/styles.py
795
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 ---
|
# --- Dynamic Theme Stylesheets ---
|
||||||
|
|
||||||
def get_light_theme_styles(scale=1.0):
|
def get_light_theme_styles(scale=1.0):
|
||||||
|
|
||||||
log_font_size = max(10, int(11 * scale))
|
log_font_size = max(10, int(11 * scale))
|
||||||
button_font_size = max(7, int(10 * scale))
|
button_font_size = max(7, int(10 * scale))
|
||||||
|
big_button_font = int(button_font_size * 13 // 10) # 1.3x as int
|
||||||
|
|
||||||
return f"""
|
return f"""
|
||||||
QMainWindow, QWidget {{
|
QMainWindow, QWidget {{
|
||||||
background-color: #f0f0f0;
|
background-color: #f0f0f0;
|
||||||
color: #000;
|
color: #000;
|
||||||
}}
|
}}
|
||||||
|
|
||||||
|
/* Status Bar */
|
||||||
|
#StatusBar {{
|
||||||
|
background-color: #34495e;
|
||||||
|
padding: {int(6 * scale)}px;
|
||||||
|
}}
|
||||||
|
|
||||||
|
/* Logs */
|
||||||
#LogPanel {{
|
#LogPanel {{
|
||||||
font-family: "Courier New", Consolas, monospace;
|
font-family: "Courier New", Consolas, monospace;
|
||||||
font-size: {log_font_size}pt;
|
font-size: {log_font_size}pt;
|
||||||
background-color: #212121;
|
background-color: #ffffff;
|
||||||
color: #eceff1;
|
color: #2b2b2b;
|
||||||
border: 1px solid #455a64;
|
border: 1px solid #c8c8c8;
|
||||||
}}
|
}}
|
||||||
|
|
||||||
|
/* Containers */
|
||||||
QGroupBox {{
|
QGroupBox {{
|
||||||
font-family: Arial;
|
font-family: Arial;
|
||||||
border: 1px solid #4a4a4a;
|
border: 1px solid #c8c8c8;
|
||||||
border-radius: {int(8 * scale)}px;
|
border-radius: {int(8 * scale)}px;
|
||||||
margin-top: {int(6 * 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;
|
padding: 0 {int(10 * scale)}px;
|
||||||
color: #000;
|
color: #000;
|
||||||
}}
|
}}
|
||||||
|
|
||||||
|
/* Tabs */
|
||||||
QTabWidget::pane {{ border-top: 2px solid #c8c8c8; }}
|
QTabWidget::pane {{ border-top: 2px solid #c8c8c8; }}
|
||||||
QTabBar::tab {{
|
QTabBar::tab {{
|
||||||
background: #e1e1e1; border: 1px solid #c8c8c8;
|
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;
|
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; }}
|
QTabBar::tab:selected {{ background: #f0f0f0; border-bottom-color: #f0f0f0; }}
|
||||||
|
|
||||||
|
/* Forms */
|
||||||
QFormLayout::label {{ color: #000; padding-top: {int(3 * scale)}px; }}
|
QFormLayout::label {{ color: #000; padding-top: {int(3 * scale)}px; }}
|
||||||
QLineEdit, QPlainTextEdit, QComboBox {{
|
QLineEdit, QPlainTextEdit, QComboBox {{
|
||||||
background-color: #fff; border: 1px solid #c8c8c8;
|
background-color: #fff; border: 1px solid #c8c8c8;
|
||||||
border-radius: {int(4 * scale)}px; padding: {int(4 * scale)}px;
|
border-radius: {int(4 * scale)}px; padding: {int(4 * scale)}px;
|
||||||
font-size: {max(7, int(9 * scale))}pt;
|
font-size: {max(7, int(9 * scale))}pt;
|
||||||
|
color: #000;
|
||||||
}}
|
}}
|
||||||
QLineEdit:read-only {{ background-color: #e9e9e9; }}
|
QLineEdit:read-only {{ background-color: #e9e9e9; }}
|
||||||
|
|
||||||
|
/* Buttons (generic) */
|
||||||
QPushButton {{
|
QPushButton {{
|
||||||
background-color: #e1e1e1; border: 1px solid #c8c8c8;
|
background-color: #e1e1e1; border: 1px solid #c8c8c8;
|
||||||
padding: {int(5 * scale)}px {int(10 * scale)}px;
|
padding: {int(5 * scale)}px {int(10 * scale)}px;
|
||||||
border-radius: {int(4 * scale)}px;
|
border-radius: {int(4 * scale)}px;
|
||||||
|
color: #000;
|
||||||
}}
|
}}
|
||||||
QPushButton:hover {{ background-color: #dcdcdc; }}
|
QPushButton:hover {{ background-color: #dcdcdc; }}
|
||||||
QPushButton:pressed {{ background-color: #c8c8c8; }}
|
QPushButton:pressed {{ background-color: #c8c8c8; }}
|
||||||
|
QPushButton:disabled {{ background-color: #d3d3d3; color: #a0a0a0; }}
|
||||||
|
|
||||||
|
/* Primary/Destructive (larger) */
|
||||||
#RefreshButton, #ResetButton {{
|
#RefreshButton, #ResetButton {{
|
||||||
padding: {int(6 * scale)}px {int(16 * scale)}px;
|
padding: {int(6 * scale)}px {int(16 * scale)}px;
|
||||||
font-size: {button_font_size * 1.3}pt;
|
font-size: {big_button_font}pt;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
color: white;
|
||||||
border-radius: {int(4*scale)}px;
|
border-radius: {int(4*scale)}px;
|
||||||
}}
|
}}
|
||||||
#RefreshButton {{
|
#RefreshButton {{ background-color: #2e7d32; }}
|
||||||
background-color: #2e7d32; /* A slightly darker green */
|
#ResetButton {{ background-color: #c62828; }}
|
||||||
|
|
||||||
|
/* 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;
|
||||||
}}
|
}}
|
||||||
#ResetButton {{
|
#ConnectButton, #StartSwapButton {{ background-color: #28a745; }}
|
||||||
background-color: #c62828; /* A slightly darker red */
|
#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: #b0bec5; color: #78909c;
|
||||||
}}
|
}}
|
||||||
|
|
||||||
|
/* 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 {{
|
#ChamberOpenDoorButton, #ChamberChgOnButton, #ChamberChgOffButton {{
|
||||||
padding: {int(8 * scale)}px;
|
padding: {int(8 * scale)}px;
|
||||||
font-size: {button_font_size}pt;
|
font-size: {button_font_size}pt;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
border-radius: {int(4*scale)}px;
|
border-radius: {int(4*scale)}px;
|
||||||
}}
|
}}
|
||||||
|
#ChamberOpenDoorButton:hover {{ background-color: #607d8b; }}
|
||||||
|
#ChamberChgOnButton:hover {{ background-color: #52be80; }}
|
||||||
|
#ChamberChgOffButton:hover {{ background-color: #cd6155; }}
|
||||||
|
|
||||||
#ChamberOpenDoorButton {{ background-color: #E1E1E1; }}
|
/* Status & alarms */
|
||||||
#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;
|
|
||||||
}}
|
|
||||||
|
|
||||||
#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: #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="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[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="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; }}
|
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):
|
def get_dark_theme_styles(scale=1.0):
|
||||||
|
|
||||||
log_font_size = max(10, int(11 * scale))
|
log_font_size = max(10, int(11 * scale))
|
||||||
button_font_size = max(7, int(10 * scale))
|
button_font_size = max(7, int(10 * scale))
|
||||||
|
|
||||||
return f"""
|
return f"""
|
||||||
QMainWindow, QWidget {{ background-color: #2b2b2b; color: #f0f0f0; }}
|
QMainWindow, QWidget {{ background-color: #2b2b2b; color: #f0f0f0; }}
|
||||||
|
|
||||||
#LogPanel {{
|
#LogPanel {{
|
||||||
font-family: "Courier New", Consolas, monospace;
|
font-family: "Courier New", Consolas, monospace;
|
||||||
font-size: {log_font_size}pt;
|
font-size: {log_font_size}pt;
|
||||||
|
|
@ -128,11 +677,13 @@ def get_dark_theme_styles(scale=1.0):
|
||||||
color: #eceff1;
|
color: #eceff1;
|
||||||
border: 1px solid #455a64;
|
border: 1px solid #455a64;
|
||||||
}}
|
}}
|
||||||
|
|
||||||
QGroupBox {{
|
QGroupBox {{
|
||||||
font-family: Arial; border: 1px solid #4a4a4a;
|
font-family: Arial; border: 1px solid #4a4a4a;
|
||||||
border-radius: {int(8 * scale)}px; margin-top: {int(6 * scale)}px;
|
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; }}
|
QGroupBox::title {{ subcontrol-origin: margin; subcontrol-position: top center; padding: 0 {int(10 * scale)}px; color: #f0f0f0; }}
|
||||||
|
|
||||||
QTabWidget::pane {{ border-top: 2px solid #4a4a4a; }}
|
QTabWidget::pane {{ border-top: 2px solid #4a4a4a; }}
|
||||||
QTabBar::tab {{
|
QTabBar::tab {{
|
||||||
background: #3c3c3c; border: 1px solid #4a4a4a; color: #f0f0f0;
|
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;
|
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; }}
|
QTabBar::tab:selected {{ background: #2b2b2b; border-bottom-color: #2b2b2b; }}
|
||||||
|
|
||||||
QFormLayout::label {{ color: #f0f0f0; padding-top: {int(3 * scale)}px; }}
|
QFormLayout::label {{ color: #f0f0f0; padding-top: {int(3 * scale)}px; }}
|
||||||
QLineEdit, QPlainTextEdit, QComboBox {{
|
QLineEdit, QPlainTextEdit, QComboBox {{
|
||||||
background-color: #3c3c3c; border: 1px solid #4a4a4a;
|
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;
|
font-size: {max(7, int(9 * scale))}pt;
|
||||||
}}
|
}}
|
||||||
QLineEdit:read-only {{ background-color: #333333; }}
|
QLineEdit:read-only {{ background-color: #333333; }}
|
||||||
|
|
||||||
QPushButton {{
|
QPushButton {{
|
||||||
background-color: #555555; border: 1px solid #4a4a4a;
|
background-color: #555555; border: 1px solid #4a4a4a;
|
||||||
padding: {int(5 * scale)}px {int(10 * scale)}px;
|
padding: {int(5 * scale)}px {int(10 * scale)}px;
|
||||||
|
|
@ -155,29 +708,25 @@ def get_dark_theme_styles(scale=1.0):
|
||||||
QPushButton:hover {{ background-color: #6a6a6a; }}
|
QPushButton:hover {{ background-color: #6a6a6a; }}
|
||||||
QPushButton:pressed {{ background-color: #4a4a4a; }}
|
QPushButton:pressed {{ background-color: #4a4a4a; }}
|
||||||
QPushButton:disabled {{ background-color: #404040; color: #888888; }}
|
QPushButton:disabled {{ background-color: #404040; color: #888888; }}
|
||||||
|
|
||||||
#RefreshButton, #ResetButton {{
|
#RefreshButton, #ResetButton {{
|
||||||
padding: {int(6 * scale)}px {int(16 * scale)}px;
|
padding: {int(6 * scale)}px {int(16 * scale)}px;
|
||||||
font-size: {button_font_size * 1.3}pt;
|
font-size: {int(button_font_size * 1.3)}pt;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
border-radius: {int(4*scale)}px;
|
border-radius: {int(4*scale)}px;
|
||||||
}}
|
}}
|
||||||
#RefreshButton {{
|
#RefreshButton {{ background-color: #2e7d32; }}
|
||||||
background-color: #2e7d32; /* A slightly darker green */
|
#ResetButton {{ background-color: #c62828; }}
|
||||||
}}
|
|
||||||
#ResetButton {{
|
|
||||||
background-color: #c62828; /* A slightly darker red */
|
|
||||||
}}
|
|
||||||
#ChamberOpenDoorButton, #ChamberChgOnButton, #ChamberChgOffButton {{
|
#ChamberOpenDoorButton, #ChamberChgOnButton, #ChamberChgOffButton {{
|
||||||
padding: {int(8 * scale)}px;
|
padding: {int(8 * scale)}px;
|
||||||
font-size: {button_font_size}pt;
|
font-size: {button_font_size}pt;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
border-radius: {int(4*scale)}px;
|
border-radius: {int(4*scale)}px;
|
||||||
}}
|
}}
|
||||||
|
|
||||||
#ChamberOpenDoorButton {{ background-color: #3C3C3C; }}
|
#ChamberOpenDoorButton {{ background-color: #3C3C3C; }}
|
||||||
#ChamberChgOnButton {{ background-color: #3C3C3C; }}
|
#ChamberChgOnButton {{ background-color: #3C3C3C; }}
|
||||||
#ChamberChgOffButton {{ background-color: #3C3C3C; }}
|
#ChamberChgOffButton {{ background-color: #3C3C3C; }}
|
||||||
|
|
||||||
#ChamberOpenDoorButton:hover {{ background-color: #607d8b; }}
|
#ChamberOpenDoorButton:hover {{ background-color: #607d8b; }}
|
||||||
#ChamberChgOnButton:hover {{ background-color: #52be80; }}
|
#ChamberChgOnButton:hover {{ background-color: #52be80; }}
|
||||||
#ChamberChgOffButton:hover {{ background-color: #cd6155; }}
|
#ChamberChgOffButton:hover {{ background-color: #cd6155; }}
|
||||||
|
|
@ -189,19 +738,14 @@ def get_dark_theme_styles(scale=1.0):
|
||||||
border-radius: {int(4*scale)}px;
|
border-radius: {int(4*scale)}px;
|
||||||
color: white;
|
color: white;
|
||||||
}}
|
}}
|
||||||
|
#ConnectButton {{ background-color: #27ae60; }}
|
||||||
#ConnectButton {{ background-color: #27ae60; }} /* Green */
|
#DisconnectButton {{ background-color: #c0392b; }}
|
||||||
#DisconnectButton {{ background-color: #c0392b; }} /* Red */
|
|
||||||
|
|
||||||
#ConnectButton:hover {{ background-color: #52be80; }}
|
#ConnectButton:hover {{ background-color: #52be80; }}
|
||||||
#DisconnectButton:hover {{ background-color: #cd6155; }}
|
#DisconnectButton:hover {{ background-color: #cd6155; }}
|
||||||
|
|
||||||
#ConnectButton:pressed {{ background-color: #52be80; }}
|
#ConnectButton:pressed {{ background-color: #52be80; }}
|
||||||
#DisconnectButton:pressed {{ background-color: #cd6155; }}
|
#DisconnectButton:pressed {{ background-color: #cd6155; }}
|
||||||
|
|
||||||
#ConnectButton:disabled, #DisconnectButton:disabled {{
|
#ConnectButton:disabled, #DisconnectButton:disabled {{
|
||||||
background-color: #546e7a;
|
background-color: #546e7a; color: #90a4ae;
|
||||||
color: #90a4ae;
|
|
||||||
}}
|
}}
|
||||||
|
|
||||||
#RefreshButton, #StartSwapButton {{ background-color: #27ae60; color: white; border: none; }}
|
#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; }}
|
#ResetButton:hover, #AbortSwapButton:hover {{ background-color: #cd6155; }}
|
||||||
#SendAudioButton {{ background-color: #3498db; color: white; border: none; font-size: {max(10, int(14 * scale))}px; }}
|
#SendAudioButton {{ background-color: #3498db; color: white; border: none; font-size: {max(10, int(14 * scale))}px; }}
|
||||||
#SendAudioButton:hover {{ background-color: #5dade2; }}
|
#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="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[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="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; }}
|
QLabel[alarm="inactive"] {{ background-color: transparent; color: #f0f0f0; }}
|
||||||
QGroupBox#ChamberWidget {{ border: 2px solid #3498db; }}
|
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;
|
||||||
|
}}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
@ -17,24 +17,6 @@ class ChamberWidget(QGroupBox):
|
||||||
self.setObjectName("ChamberWidget")
|
self.setObjectName("ChamberWidget")
|
||||||
self.setFont(QFont("Arial", max(8, int(9 * scale)), QFont.Weight.Bold))
|
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 = QVBoxLayout(self)
|
||||||
main_layout.setSpacing(max(2, int(4 * scale)))
|
main_layout.setSpacing(max(2, int(4 * scale)))
|
||||||
|
|
||||||
|
|
@ -90,7 +72,7 @@ class ChamberWidget(QGroupBox):
|
||||||
self.chg_temp_field.setObjectName("DataField")
|
self.chg_temp_field.setObjectName("DataField")
|
||||||
|
|
||||||
self.door_status_field = self._create_data_field(scale)
|
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 = self._create_data_field(scale)
|
||||||
self.charger_fault_field.setObjectName("DataField")
|
self.charger_fault_field.setObjectName("DataField")
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue