From 20d04361bca5fbe352c23c6e6f45eead1d8c2da6 Mon Sep 17 00:00:00 2001 From: marwin Date: Sat, 15 Aug 2026 20:36:54 +0200 Subject: [PATCH] TUI: Statusleiste mit Akkustand und Uhrzeit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Neues diora_tui/statusbar.py: StatusBar-Widget, unten rechts auf Bibliotheks- und Reader-Screen, zeigt Akkustand (psutil.sensors_battery(), degradiert sauber zu reiner Uhrzeit auf Geräten ohne Akku) + aktuelle Uhrzeit, sekündlich aktualisiert. Getestet: Anzeige und Aktualisierung auf beiden Screens verifiziert. --- tui/README.md | 3 +++ tui/diora_tui/app.py | 2 ++ tui/diora_tui/app.tcss | 9 +++++++ tui/diora_tui/reader_screen.py | 2 ++ tui/diora_tui/statusbar.py | 44 ++++++++++++++++++++++++++++++++++ tui/pyproject.toml | 1 + 6 files changed, 61 insertions(+) create mode 100644 tui/diora_tui/statusbar.py diff --git a/tui/README.md b/tui/README.md index 960dc6d..4af7ad6 100644 --- a/tui/README.md +++ b/tui/README.md @@ -44,6 +44,9 @@ Tastenkürzel: Der Fließtext ist auf 120 Zeichen Breite begrenzt und horizontal zentriert (lesbarer als volle Terminalbreite bei breiten Fenstern). +Unten rechts zeigt eine Statusleiste Akkustand (falls vorhanden, via `psutil`) und Uhrzeit, +sekündlich aktualisiert, auf Bibliotheks- und Reader-Ansicht. + Die Bibliotheksansicht ist nach zuletzt geöffnetem Buch sortiert (neueste zuerst; anhand des Zeitstempels der zuletzt gespeicherten Position), und blendet gelesene Bücher standardmäßig aus (`EBook.is_read` aus dem Sync-Snapshot, lokal in `library.json` diff --git a/tui/diora_tui/app.py b/tui/diora_tui/app.py index 350de9b..883df81 100644 --- a/tui/diora_tui/app.py +++ b/tui/diora_tui/app.py @@ -17,6 +17,7 @@ from textual.widgets import Footer, Header, Label, ListItem, ListView from . import config as config_mod from . import crypto, epub, library_meta, progress, remote from .reader_screen import ReaderScreen +from .statusbar import StatusBar DEFAULT_LIBRARY = Path.home() / "Books" @@ -38,6 +39,7 @@ class LibraryScreen(Screen): def compose(self) -> ComposeResult: yield Header() yield ListView(id="library-list") + yield StatusBar() yield Footer() def on_mount(self) -> None: diff --git a/tui/diora_tui/app.tcss b/tui/diora_tui/app.tcss index d707750..06172cd 100644 --- a/tui/diora_tui/app.tcss +++ b/tui/diora_tui/app.tcss @@ -2,6 +2,15 @@ height: 1fr; } +StatusBar { + dock: bottom; + height: 1; + padding: 0 1; + background: $panel; + color: $text-muted; + text-align: right; +} + #reader-scroll { height: 1fr; padding: 1 2; diff --git a/tui/diora_tui/reader_screen.py b/tui/diora_tui/reader_screen.py index 9dbd17f..f820a0f 100644 --- a/tui/diora_tui/reader_screen.py +++ b/tui/diora_tui/reader_screen.py @@ -25,6 +25,7 @@ from textual.widgets import Footer, Header, Label, LoadingIndicator, Static from . import api, blocks, cache, config as config_mod, layout, progress from .book_view import ContinuousBookView +from .statusbar import StatusBar MAX_LINE_WIDTH = 120 AUTOSAVE_INTERVAL = 5.0 @@ -88,6 +89,7 @@ class ReaderScreen(Screen): # nothing to clamp against and silently resets to 0. self._book_view = ContinuousBookView(_EMPTY_LAYOUT) yield self._book_view + yield StatusBar() yield Footer() def on_mount(self) -> None: diff --git a/tui/diora_tui/statusbar.py b/tui/diora_tui/statusbar.py new file mode 100644 index 0000000..e38675a --- /dev/null +++ b/tui/diora_tui/statusbar.py @@ -0,0 +1,44 @@ +"""Bottom status bar: battery level + current time, refreshed every second. +Battery is read via psutil (cross-platform); on a desktop machine with no +battery, psutil.sensors_battery() returns None and the bar just shows time. +""" + +from __future__ import annotations + +from datetime import datetime + +from textual.widgets import Static + +try: + import psutil +except ImportError: + psutil = None # battery display degrades gracefully to time-only + + +class StatusBar(Static): + def on_mount(self) -> None: + self._refresh() + self.set_interval(1.0, self._refresh) + + def _refresh(self) -> None: + self.update(self._render_text()) + + def _render_text(self) -> str: + parts = [] + battery = self._battery_text() + if battery: + parts.append(battery) + parts.append(datetime.now().strftime("%H:%M:%S")) + return " ".join(parts) + + def _battery_text(self) -> str | None: + if psutil is None: + return None + try: + battery = psutil.sensors_battery() + except Exception: + return None + if battery is None: + return None + state = "lädt" if battery.power_plugged else "Akku" + return f"{state} {round(battery.percent)}%" diff --git a/tui/pyproject.toml b/tui/pyproject.toml index 80d5a24..969d473 100644 --- a/tui/pyproject.toml +++ b/tui/pyproject.toml @@ -11,6 +11,7 @@ dependencies = [ "platformdirs>=4.0", "requests>=2.31", "cryptography>=42.0", + "psutil>=5.9", ] [project.scripts]