# firmware_test: TUI app screens styling & app_logo
This commit is contained in:
parent
86df36fab7
commit
0132d8056d
6 changed files with 102 additions and 20 deletions
|
|
@ -15,8 +15,8 @@ Screen {
|
||||||
AppFrame {
|
AppFrame {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
max-width: 160;
|
max-width: 112;
|
||||||
max-height: 50;
|
max-height: 35;
|
||||||
border: heavy $primary;
|
border: heavy $primary;
|
||||||
background: $surface;
|
background: $surface;
|
||||||
padding: 1 2;
|
padding: 1 2;
|
||||||
|
|
@ -25,19 +25,31 @@ AppFrame {
|
||||||
/* ── WaitingScreen ──────────────────────────────────────── */
|
/* ── WaitingScreen ──────────────────────────────────────── */
|
||||||
|
|
||||||
#waiting-frame {
|
#waiting-frame {
|
||||||
align: center middle;
|
align: center top;
|
||||||
}
|
}
|
||||||
|
|
||||||
#waiting-logo {
|
#waiting-logo-row {
|
||||||
width: 52;
|
width: 100%;
|
||||||
height: 7;
|
height: auto;
|
||||||
border: round $primary;
|
margin-top: 1;
|
||||||
padding: 1 2;
|
}
|
||||||
content-align: center middle;
|
|
||||||
color: $primary;
|
#waiting-logo-art {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#waiting-version-row {
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
margin-top: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#waiting-version {
|
||||||
|
color: $warning;
|
||||||
text-style: bold;
|
text-style: bold;
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#waiting-reason {
|
#waiting-reason {
|
||||||
margin-top: 1;
|
margin-top: 1;
|
||||||
padding: 1 2;
|
padding: 1 2;
|
||||||
|
|
@ -163,6 +175,11 @@ AppFrame {
|
||||||
margin-right: 2;
|
margin-right: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#post-flash-title-row,
|
||||||
|
#post-flash-instruction-row {
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
/* ── DiagScreen ─────────────────────────────────────────── */
|
/* ── DiagScreen ─────────────────────────────────────────── */
|
||||||
|
|
||||||
#diag-frame {
|
#diag-frame {
|
||||||
|
|
|
||||||
33
tools/production/app/boot_art.py
Normal file
33
tools/production/app/boot_art.py
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
"""
|
||||||
|
boot_art.py — ASCII-логотип компании для WaitingScreen.
|
||||||
|
|
||||||
|
Сгенерирован программно (coverage-based растеризация logo.png в
|
||||||
|
символы плотности + разделение по цвету blue/dark), затем зафиксирован
|
||||||
|
как статичная строка — рантайм-зависимости на Pillow/etc. не требуется.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
LOGO_ART = """
|
||||||
|
[grey37]........[/grey37][#3ca0dc].**:+*::*+.[/#3ca0dc][grey37]..........[/grey37]
|
||||||
|
[grey37]........[/grey37][#3ca0dc].##:*#++#*.[/#3ca0dc][grey37]..........[/grey37]
|
||||||
|
[grey37].........[/grey37][#3ca0dc]*#[/#3ca0dc][grey37].[/grey37][#3ca0dc]:#..#+[/#3ca0dc][grey37]...........[/grey37]
|
||||||
|
[grey37].......[/grey37][#3ca0dc].+#:[/#3ca0dc][grey37].[/grey37][#3ca0dc]:#..#+[/#3ca0dc][grey37]...........[/grey37]
|
||||||
|
[grey37].....[/grey37][#3ca0dc].+#+..:#*..#+[/#3ca0dc][grey37]...........[/grey37]
|
||||||
|
[grey37]...[/grey37][#3ca0dc].+#+..:**:..*#.[/#3ca0dc][white].:.[/white][grey37]........[/grey37]
|
||||||
|
[grey37].[/grey37][#3ca0dc].+#+..:#*:..*#+.[/#3ca0dc][white]+@@+[/white][grey37]........[/grey37]
|
||||||
|
[grey37].[/grey37][#3ca0dc]##..:#*.[/#3ca0dc][grey37].[/grey37][#3ca0dc].*#+.[/#3ca0dc][white]%@@@@%[/white][grey37].[/grey37][white]::.[/white][grey37]....[/grey37]
|
||||||
|
[grey37].[/grey37][#3ca0dc]#*[/#3ca0dc][grey37].[/grey37][#3ca0dc]:#:[/#3ca0dc][grey37].[/grey37][#3ca0dc].*#+.[/#3ca0dc][white]+@@@@@@%[/white][grey37].[/grey37][white]+@...[/white][grey37]..[/grey37]
|
||||||
|
[grey37].[/grey37][#3ca0dc]#*[/#3ca0dc][grey37].[/grey37][#3ca0dc]+#..#+.[/#3ca0dc][grey37]........[/grey37][white]%%[/white][grey37].[/grey37][white]+@..%.[/white][grey37].[/grey37]
|
||||||
|
[grey37].[/grey37][#3ca0dc]#*[/#3ca0dc][grey37].[/grey37][#3ca0dc]+#.:#:[/#3ca0dc][grey37].........[/grey37][white]%%[/white][grey37].[/grey37][white]+@..@:[/white][grey37].[/grey37]
|
||||||
|
[grey37].[/grey37][#3ca0dc]**[/#3ca0dc][grey37].[/grey37][#3ca0dc]+#.:#:[/#3ca0dc][grey37]........[/grey37][white].%%[/white][grey37].[/grey37][white]+@..@:[/white][grey37].[/grey37]
|
||||||
|
[grey37].[/grey37][#3ca0dc]..[/#3ca0dc][grey37].[/grey37][#3ca0dc]+#.:#*+++++.[/#3ca0dc][white].:@%.[/white][grey37].[/grey37][white]+@..@:[/white][grey37].[/grey37]
|
||||||
|
[grey37]....[/grey37][#3ca0dc].*.:#####+.[/#3ca0dc][white]+@%.[/white][grey37].[/grey37][white]:%%:[/white][grey37].[/grey37][white]:@:[/white][grey37].[/grey37]
|
||||||
|
[grey37]......[/grey37][#3ca0dc].:###+.[/#3ca0dc][white]+@%.[/white][grey37].[/grey37][white].%%:..+@+.[/white][grey37].[/grey37]
|
||||||
|
[grey37].......[/grey37][#3ca0dc].:+.[/#3ca0dc][white]:@%.[/white][grey37].[/grey37][white].%@:..+@+.[/white][grey37]...[/grey37]
|
||||||
|
[grey37]...........[/grey37][white]@%[/white][grey37].[/grey37][white].%@:..+@+.[/white][grey37].....[/grey37]
|
||||||
|
[grey37]...........[/grey37][white]@%[/white][grey37].[/grey37][white]%@..+@+.[/white][grey37].......[/grey37]
|
||||||
|
[grey37]...........[/grey37][white]@%[/white][grey37].[/grey37][white]%@[/white][grey37].[/grey37][white]:@:[/white][grey37].........[/grey37]
|
||||||
|
[grey37]..........[/grey37][white].@%.%@.+@+[/white][grey37].........[/grey37]
|
||||||
|
[grey37]..........[/grey37][white]:@@:@@+%@%.[/white][grey37]........[/grey37]
|
||||||
|
""".strip("\n")
|
||||||
|
|
@ -19,7 +19,7 @@ post_flash.py — экран-промпт после успешной проши
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from textual.app import ComposeResult
|
from textual.app import ComposeResult
|
||||||
from textual.containers import Horizontal
|
from textual.containers import Center, Horizontal
|
||||||
from textual.message import Message
|
from textual.message import Message
|
||||||
from textual.screen import Screen
|
from textual.screen import Screen
|
||||||
from textual.timer import Timer
|
from textual.timer import Timer
|
||||||
|
|
@ -49,8 +49,10 @@ class PostFlashScreen(Screen):
|
||||||
|
|
||||||
def compose(self) -> ComposeResult:
|
def compose(self) -> ComposeResult:
|
||||||
with AppFrame(id="post-flash-frame"):
|
with AppFrame(id="post-flash-frame"):
|
||||||
|
with Center(id="post-flash-title-row"):
|
||||||
yield Label("✅ firmware_test успешно записан", id="post-flash-title")
|
yield Label("✅ firmware_test успешно записан", id="post-flash-title")
|
||||||
|
|
||||||
|
with Center(id="post-flash-instruction-row"):
|
||||||
yield Static(
|
yield Static(
|
||||||
"Переведите плату в нормальный режим:\nBOOT_MOD_1 → GND → Reset",
|
"Переведите плату в нормальный режим:\nBOOT_MOD_1 → GND → Reset",
|
||||||
id="post-flash-instruction",
|
id="post-flash-instruction",
|
||||||
|
|
|
||||||
|
|
@ -14,25 +14,53 @@ waiting.py — экран ожидания подключения платы.
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import tomllib
|
||||||
|
from pathlib import Path
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from textual.app import ComposeResult
|
from textual.app import ComposeResult
|
||||||
|
from textual.containers import Center
|
||||||
from textual.css.query import NoMatches
|
from textual.css.query import NoMatches
|
||||||
from textual.message import Message
|
from textual.message import Message
|
||||||
from textual.screen import Screen
|
from textual.screen import Screen
|
||||||
from textual.timer import Timer
|
from textual.timer import Timer
|
||||||
from textual.widgets import Static
|
from textual.widgets import Static
|
||||||
|
|
||||||
|
from ..boot_art import LOGO_ART
|
||||||
from ..flasher import Flasher
|
from ..flasher import Flasher
|
||||||
from ..models import AppMode
|
from ..models import AppMode
|
||||||
from ..widgets import AppFrame
|
from ..widgets import AppFrame
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
_DETECT_INTERVAL_S = 1.5
|
_DETECT_INTERVAL_S = 1.5
|
||||||
_SPIN_INTERVAL_S = 0.1
|
_SPIN_INTERVAL_S = 0.1
|
||||||
_REASON_DISPLAY_S = 4.0
|
_REASON_DISPLAY_S = 4.0
|
||||||
_SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]
|
_SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]
|
||||||
|
|
||||||
|
|
||||||
|
def _read_app_version() -> str:
|
||||||
|
"""
|
||||||
|
Прочитать версию service-tui из pyproject.toml.
|
||||||
|
|
||||||
|
Не используем importlib.metadata — проект не устанавливается как пакет
|
||||||
|
(tool.uv.package = false), метаданные могут отсутствовать. Читаем файл
|
||||||
|
напрямую через tomllib (stdlib, requires-python >= 3.11 уже задан).
|
||||||
|
"""
|
||||||
|
pyproject_path = Path(__file__).resolve().parents[2] / "pyproject.toml"
|
||||||
|
try:
|
||||||
|
with pyproject_path.open("rb") as f:
|
||||||
|
data = tomllib.load(f)
|
||||||
|
return data.get("project", {}).get("version", "0.0.0")
|
||||||
|
except Exception as exc:
|
||||||
|
logger.warning("Не удалось прочитать версию из %s: %s", pyproject_path, exc)
|
||||||
|
return "0.0.0"
|
||||||
|
|
||||||
|
|
||||||
|
_APP_VERSION = _read_app_version()
|
||||||
|
|
||||||
|
|
||||||
class WaitingScreen(Screen):
|
class WaitingScreen(Screen):
|
||||||
"""
|
"""
|
||||||
Экран ожидания.
|
Экран ожидания.
|
||||||
|
|
@ -58,7 +86,9 @@ class WaitingScreen(Screen):
|
||||||
|
|
||||||
def compose(self) -> ComposeResult:
|
def compose(self) -> ComposeResult:
|
||||||
with AppFrame(id="waiting-frame"):
|
with AppFrame(id="waiting-frame"):
|
||||||
yield Static("TFT Indicator Board\nService Tool", id="waiting-logo")
|
yield Static(f"service_tool v{_APP_VERSION}", id="waiting-version")
|
||||||
|
with Center(id="waiting-logo-row"):
|
||||||
|
yield Static(LOGO_ART, id="waiting-logo-art")
|
||||||
yield Static("", id="waiting-reason", classes="hidden")
|
yield Static("", id="waiting-reason", classes="hidden")
|
||||||
yield Static("Подключите плату индикатора к USB...", id="waiting-hint")
|
yield Static("Подключите плату индикатора к USB...", id="waiting-hint")
|
||||||
yield Static(_SPINNER_FRAMES[0], id="waiting-spinner")
|
yield Static(_SPINNER_FRAMES[0], id="waiting-spinner")
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[project]
|
[project]
|
||||||
name = "service-tui"
|
name = "service-tui"
|
||||||
version = "0.1.0"
|
version = "0.1.1"
|
||||||
description = "TUI сервисного инженера для диагностики платы MIMXRT1052"
|
description = "TUI сервисного инженера для диагностики платы MIMXRT1052"
|
||||||
requires-python = ">=3.11"
|
requires-python = ">=3.11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
|
|
||||||
|
|
@ -200,7 +200,7 @@ wheels = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "service-tui"
|
name = "service-tui"
|
||||||
version = "0.1.0"
|
version = "0.1.1"
|
||||||
source = { virtual = "." }
|
source = { virtual = "." }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "pyinstaller" },
|
{ name = "pyinstaller" },
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue