feat: configure minimum splash duration

This commit is contained in:
Marcel Peterkau
2026-06-21 18:48:08 +02:00
parent dadcdb8b4a
commit e1d2b87ca1
7 changed files with 66 additions and 1 deletions
+1
View File
@@ -55,6 +55,7 @@ class CCMAApp(tk.Tk):
run_housekeeper=self.config_obj.run_housekeeper_on_startup,
monitor=self.startup_monitor,
housekeeper_settings=self.config_obj.housekeeper_settings(),
minimum_display_seconds=self.config_obj.splash_minimum_seconds,
)
except Exception as exc:
self._startup_failed(exc)
+1
View File
@@ -27,6 +27,7 @@
"Dropdowns zeigen deutsche Begriffe bei weiterhin englischen Speicher-Keys; der Hausmeisterstatus liegt einheitlich in housekeeper.json.",
"Mehrstufiger Mahnworkflow mit Hausmeister-Regel, Entwurf, Versandbestätigung, Zahlungsfrist, optionaler Gebühr und Mahnsperre ergänzt.",
"Splash-Screen auf das eingebettete CCMA-Hintergrundmotiv umgestellt und redundante Titeltexte entfernt.",
"Konfigurierbare Mindestanzeigezeit des Splash-Screens mit fünf Sekunden Standardwert ergänzt.",
"Hausmeister um konfigurierbare Geburtstags- und Mitgliedsjubiläumsmeldungen erweitert.",
"Statusänderungen werden mit altem und neuem Klartextwert in der Mitgliederchronik protokolliert.",
"Fensterposition, normaler Fensterzustand und Maximierung werden gespeichert; der Splash startet auf dem zuletzt verwendeten Monitor.",
+12
View File
@@ -1,6 +1,7 @@
from __future__ import annotations
import json
import math
import os
from dataclasses import dataclass
from pathlib import Path
@@ -18,6 +19,7 @@ class AppConfig:
gnucash_path: str = ""
theme_mode: str = "dark"
run_housekeeper_on_startup: bool = True
splash_minimum_seconds: float = 5.0
birthday_days_before: int = 7
birthday_days_after: int = 2
anniversary_days_before: int = 14
@@ -40,6 +42,7 @@ class AppConfig:
"gnucash_path": self.gnucash_path,
"theme_mode": self.theme_mode,
"run_housekeeper_on_startup": self.run_housekeeper_on_startup,
"splash_minimum_seconds": _non_negative_float(self.splash_minimum_seconds, 5.0),
"birthday_days_before": self.birthday_days_before,
"birthday_days_after": self.birthday_days_after,
"anniversary_days_before": self.anniversary_days_before,
@@ -96,6 +99,7 @@ def load_config() -> AppConfig:
gnucash_path=str(data.get("gnucash_path", "")),
theme_mode=str(data.get("theme_mode", "dark")),
run_housekeeper_on_startup=bool(data.get("run_housekeeper_on_startup", True)),
splash_minimum_seconds=_non_negative_float(data.get("splash_minimum_seconds", 5.0), 5.0),
birthday_days_before=int(data.get("birthday_days_before", 7)),
birthday_days_after=int(data.get("birthday_days_after", 2)),
anniversary_days_before=int(data.get("anniversary_days_before", 14)),
@@ -113,3 +117,11 @@ def _legacy_config_directory() -> Path:
if os.name == "nt":
return Path(os.environ.get("APPDATA", Path.home())) / "C3MA"
return Path(os.environ.get("XDG_CONFIG_HOME", Path.home() / ".config")) / "c3ma"
def _non_negative_float(value: object, default: float) -> float:
try:
parsed = float(value)
except (TypeError, ValueError):
return default
return max(0.0, parsed) if math.isfinite(parsed) else default
+25 -1
View File
@@ -1,6 +1,8 @@
from __future__ import annotations
import math
import threading
import time
import tkinter as tk
from collections.abc import Callable
from dataclasses import dataclass
@@ -33,6 +35,7 @@ class SplashScreen(tk.Toplevel):
run_housekeeper: bool = True,
monitor: MonitorBounds | None = None,
housekeeper_settings: HousekeeperSettings | None = None,
minimum_display_seconds: float = 5.0,
):
super().__init__(master)
self.repository = repository
@@ -41,6 +44,10 @@ class SplashScreen(tk.Toplevel):
self.run_housekeeper = run_housekeeper
self.monitor = monitor
self.housekeeper_settings = housekeeper_settings
self.minimum_display_seconds = (
max(0.0, float(minimum_display_seconds)) if math.isfinite(float(minimum_display_seconds)) else 5.0
)
self.shown_at = time.monotonic()
self._messages: Queue[tuple[str, object]] = Queue()
self.overrideredirect(True)
self.resizable(False, False)
@@ -145,7 +152,15 @@ class SplashScreen(tk.Toplevel):
def _finish(self, result: StartupResult) -> None:
self.progress.stop()
self._set_status(f"Bereit · {len(result.findings)} Vorgänge benötigen Aufmerksamkeit")
self.after(350, lambda: self._complete(result))
self._after_minimum(lambda: self._complete(result))
def _after_minimum(self, callback: Callable[[], None]) -> None:
remaining_ms = _remaining_minimum_ms(
self.shown_at,
self.minimum_display_seconds,
time.monotonic(),
)
self.after(remaining_ms, callback)
def _set_status(self, text: str) -> None:
self.canvas.itemconfigure(self.status_item, text=text)
@@ -156,6 +171,10 @@ class SplashScreen(tk.Toplevel):
def _fail(self, error: Exception) -> None:
self.progress.stop()
self._set_status("Start fehlgeschlagen")
self._after_minimum(lambda: self._complete_error(error))
def _complete_error(self, error: Exception) -> None:
self.destroy()
self.on_error(error)
@@ -177,3 +196,8 @@ def centered_position(
x = min(max(pointer_x - width // 2, screen_x), maximum_x)
y = min(max(pointer_y - height // 2, screen_y), maximum_y)
return x, y
def _remaining_minimum_ms(started_at: float, minimum_seconds: float, now: float) -> int:
remaining = max(0.0, minimum_seconds - max(0.0, now - started_at))
return int(remaining * 1000 + 0.999)