Files
AR-Autopilot/arautopilot/studio/ar_style.py
T
alro65 5238bd31f0 feat(studio): AR Electronics branding + Telemetría + Instalar J6412 tabs
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
2026-05-24 11:22:38 -04:00

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)