5238bd31f0
ar_style.py — global QSS dark theme + QPalette matching the Flutter brand palette (navy #0D1B2A, electric blue #2563EB, glow #60B8FF). Single call: apply_ar_style(app). app.py — applies AR style and window icon on startup. main_window.py — complete rewrite of the layout: - Sidebar: AR logo (PNG), user/role display, capabilities list, version stamp - 5 tabs: Overview · ⚡ Flash ESP32 · 📋 Proyecto · 📡 Telemetría · 💾 Instalar J6412 - Overview tab: rich-text guide with icons for each tab's purpose telemetry_widget.py — live $PARP STATUS chart tab: - QSerialPort RX-only connection to AR-Concentrador (port selector + Refresh) - Python $PARP XOR-checksum parser (mirrors Dart ParpCodec) - _RollingChart: pure QPainter scrolling time-series, 60 s window, no external charting library - Heading + Setpoint on one chart; Rudder on a second chart - Live value strip shows Rumbo / Setpoint / Timón + mode label installer_widget.py — J6412 USB image builder tab: - Vessel name + serial number (auto-generate or paste) - Optional CSV log path for CRM - App checkboxes (AR-ECDIS / AR-Autopilot / skip Flutter build) - Worker thread runs installer/build_usb.py with streamed log output - "Abrir dist/" button when build succeeds - RBAC gated: Engineer or Super Admin only pyproject.toml — adds [installer] and [license-server] optional dep groups AR_electronics — AR-Autopilot Project
271 lines
8.4 KiB
Python
271 lines
8.4 KiB
Python
"""AR Electronics global Qt stylesheet and palette constants.
|
|
|
|
Apply once with::
|
|
|
|
from arautopilot.studio.ar_style import apply_ar_style
|
|
apply_ar_style(app) # QApplication instance
|
|
|
|
All Studio widgets inherit the style automatically.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from PySide6.QtGui import QColor, QPalette
|
|
from PySide6.QtWidgets import QApplication
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Brand colours (matches Flutter AutopilotTheme and web CSS vars)
|
|
# ---------------------------------------------------------------------------
|
|
NAVY = "#0D1B2A"
|
|
PANEL = "#1A2B3C"
|
|
PANEL_LIGHT = "#243447"
|
|
BORDER = "#2B3F5C"
|
|
ACCENT = "#2563EB"
|
|
ACCENT_MID = "#4A9FE8"
|
|
GLOW = "#60B8FF"
|
|
TEXT_MAIN = "#E2E8F0"
|
|
TEXT_MUTED = "#8899AA"
|
|
TEXT_DIM = "#445566"
|
|
OK = "#22C55E"
|
|
WARN = "#F59E0B"
|
|
ERROR = "#EF4444"
|
|
|
|
AR_QSS = f"""
|
|
/* ── Root ──────────────────────────────────────────────────────────────── */
|
|
QMainWindow, QDialog, QWidget {{
|
|
background-color: {NAVY};
|
|
color: {TEXT_MAIN};
|
|
font-family: "Segoe UI", "Inter", sans-serif;
|
|
font-size: 12px;
|
|
}}
|
|
|
|
/* ── Group boxes ─────────────────────────────────────────────────────── */
|
|
QGroupBox {{
|
|
border: 1px solid {BORDER};
|
|
border-radius: 5px;
|
|
margin-top: 10px;
|
|
padding-top: 10px;
|
|
color: {ACCENT_MID};
|
|
font-weight: bold;
|
|
font-size: 11px;
|
|
letter-spacing: 0.5px;
|
|
}}
|
|
QGroupBox::title {{
|
|
subcontrol-origin: margin;
|
|
left: 10px;
|
|
padding: 0 5px;
|
|
}}
|
|
|
|
/* ── Tabs ──────────────────────────────────────────────────────────────── */
|
|
QTabWidget::pane {{
|
|
border: 1px solid {BORDER};
|
|
background-color: {NAVY};
|
|
top: -1px;
|
|
}}
|
|
QTabBar::tab {{
|
|
background: {PANEL};
|
|
color: {TEXT_MUTED};
|
|
padding: 7px 18px;
|
|
border: 1px solid {BORDER};
|
|
border-bottom: none;
|
|
margin-right: 2px;
|
|
border-top-left-radius: 4px;
|
|
border-top-right-radius: 4px;
|
|
}}
|
|
QTabBar::tab:selected {{
|
|
background: {NAVY};
|
|
color: {ACCENT_MID};
|
|
border-bottom: 2px solid {ACCENT};
|
|
}}
|
|
QTabBar::tab:hover:!selected {{
|
|
color: {TEXT_MAIN};
|
|
}}
|
|
|
|
/* ── Buttons ───────────────────────────────────────────────────────────── */
|
|
QPushButton {{
|
|
background-color: {PANEL};
|
|
color: {TEXT_MAIN};
|
|
border: 1px solid {ACCENT};
|
|
border-radius: 4px;
|
|
padding: 5px 16px;
|
|
min-width: 60px;
|
|
}}
|
|
QPushButton:hover {{
|
|
background-color: {ACCENT};
|
|
color: white;
|
|
}}
|
|
QPushButton:pressed {{
|
|
background-color: #1a4db5;
|
|
}}
|
|
QPushButton:disabled {{
|
|
color: {TEXT_DIM};
|
|
border-color: {BORDER};
|
|
background-color: {PANEL};
|
|
}}
|
|
QPushButton#primary {{
|
|
background-color: {ACCENT};
|
|
color: white;
|
|
font-weight: bold;
|
|
}}
|
|
QPushButton#primary:hover {{
|
|
background-color: {GLOW};
|
|
color: {NAVY};
|
|
}}
|
|
|
|
/* ── Inputs ────────────────────────────────────────────────────────────── */
|
|
QLineEdit, QTextEdit, QPlainTextEdit {{
|
|
background-color: {PANEL};
|
|
color: {TEXT_MAIN};
|
|
border: 1px solid {BORDER};
|
|
border-radius: 3px;
|
|
padding: 4px 7px;
|
|
selection-background-color: {ACCENT};
|
|
}}
|
|
QLineEdit:focus, QTextEdit:focus, QPlainTextEdit:focus {{
|
|
border-color: {ACCENT_MID};
|
|
}}
|
|
QComboBox {{
|
|
background-color: {PANEL};
|
|
color: {TEXT_MAIN};
|
|
border: 1px solid {BORDER};
|
|
border-radius: 3px;
|
|
padding: 4px 7px;
|
|
selection-background-color: {ACCENT};
|
|
}}
|
|
QComboBox:focus {{
|
|
border-color: {ACCENT_MID};
|
|
}}
|
|
QComboBox::drop-down {{
|
|
border-left: 1px solid {BORDER};
|
|
width: 20px;
|
|
}}
|
|
QComboBox QAbstractItemView {{
|
|
background-color: {PANEL};
|
|
color: {TEXT_MAIN};
|
|
selection-background-color: {ACCENT};
|
|
outline: none;
|
|
}}
|
|
QSpinBox, QDoubleSpinBox {{
|
|
background-color: {PANEL};
|
|
color: {TEXT_MAIN};
|
|
border: 1px solid {BORDER};
|
|
border-radius: 3px;
|
|
padding: 3px 6px;
|
|
selection-background-color: {ACCENT};
|
|
}}
|
|
QSpinBox:focus, QDoubleSpinBox:focus {{
|
|
border-color: {ACCENT_MID};
|
|
}}
|
|
|
|
/* ── Lists ─────────────────────────────────────────────────────────────── */
|
|
QListWidget {{
|
|
background-color: {PANEL};
|
|
color: {TEXT_MAIN};
|
|
border: 1px solid {BORDER};
|
|
border-radius: 3px;
|
|
outline: none;
|
|
}}
|
|
QListWidget::item:selected {{
|
|
background-color: {ACCENT};
|
|
}}
|
|
|
|
/* ── Scrollbars ────────────────────────────────────────────────────────── */
|
|
QScrollBar:vertical {{
|
|
background: {NAVY};
|
|
width: 8px;
|
|
border-radius: 4px;
|
|
}}
|
|
QScrollBar::handle:vertical {{
|
|
background: {BORDER};
|
|
border-radius: 4px;
|
|
min-height: 20px;
|
|
}}
|
|
QScrollBar::handle:vertical:hover {{
|
|
background: {ACCENT_MID};
|
|
}}
|
|
QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {{
|
|
height: 0;
|
|
}}
|
|
QScrollBar:horizontal {{
|
|
background: {NAVY};
|
|
height: 8px;
|
|
}}
|
|
QScrollBar::handle:horizontal {{
|
|
background: {BORDER};
|
|
border-radius: 4px;
|
|
}}
|
|
|
|
/* ── Splitter ──────────────────────────────────────────────────────────── */
|
|
QSplitter::handle {{
|
|
background: {BORDER};
|
|
}}
|
|
|
|
/* ── Status bar ────────────────────────────────────────────────────────── */
|
|
QStatusBar {{
|
|
background-color: #0A1520;
|
|
color: {ACCENT_MID};
|
|
font-size: 11px;
|
|
}}
|
|
|
|
/* ── Checkboxes ────────────────────────────────────────────────────────── */
|
|
QCheckBox {{
|
|
color: {TEXT_MAIN};
|
|
spacing: 6px;
|
|
}}
|
|
QCheckBox::indicator {{
|
|
width: 14px;
|
|
height: 14px;
|
|
border: 1px solid {ACCENT};
|
|
border-radius: 3px;
|
|
background: {PANEL};
|
|
}}
|
|
QCheckBox::indicator:checked {{
|
|
background: {ACCENT};
|
|
image: none;
|
|
}}
|
|
|
|
/* ── Labels ────────────────────────────────────────────────────────────── */
|
|
QLabel {{
|
|
color: {TEXT_MAIN};
|
|
}}
|
|
QLabel[role="muted"] {{
|
|
color: {TEXT_MUTED};
|
|
font-size: 11px;
|
|
}}
|
|
QLabel[role="heading"] {{
|
|
color: {ACCENT_MID};
|
|
font-weight: bold;
|
|
font-size: 14px;
|
|
letter-spacing: 1px;
|
|
}}
|
|
|
|
/* ── Message boxes ─────────────────────────────────────────────────────── */
|
|
QMessageBox {{
|
|
background-color: {PANEL};
|
|
}}
|
|
QMessageBox QLabel {{
|
|
color: {TEXT_MAIN};
|
|
}}
|
|
"""
|
|
|
|
|
|
def apply_ar_style(app: QApplication) -> None:
|
|
"""Apply the AR Electronics brand stylesheet + dark palette to *app*."""
|
|
app.setStyleSheet(AR_QSS)
|
|
|
|
pal = QPalette()
|
|
pal.setColor(QPalette.ColorRole.Window, QColor(NAVY))
|
|
pal.setColor(QPalette.ColorRole.WindowText, QColor(TEXT_MAIN))
|
|
pal.setColor(QPalette.ColorRole.Base, QColor(PANEL))
|
|
pal.setColor(QPalette.ColorRole.AlternateBase, QColor(PANEL_LIGHT))
|
|
pal.setColor(QPalette.ColorRole.Text, QColor(TEXT_MAIN))
|
|
pal.setColor(QPalette.ColorRole.BrightText, QColor(GLOW))
|
|
pal.setColor(QPalette.ColorRole.Button, QColor(PANEL))
|
|
pal.setColor(QPalette.ColorRole.ButtonText, QColor(TEXT_MAIN))
|
|
pal.setColor(QPalette.ColorRole.Highlight, QColor(ACCENT))
|
|
pal.setColor(QPalette.ColorRole.HighlightedText, QColor("#FFFFFF"))
|
|
pal.setColor(QPalette.ColorRole.Link, QColor(ACCENT_MID))
|
|
pal.setColor(QPalette.ColorRole.Midlight, QColor(BORDER))
|
|
pal.setColor(QPalette.ColorRole.Dark, QColor("#0A1520"))
|
|
app.setPalette(pal)
|