mirror of
https://git.hiabuto.net/C3MA/CCMA.git
synced 2026-08-25 15:05:18 +02:00
feat: initialize CCMA member administration
This commit is contained in:
+134
@@ -0,0 +1,134 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import tkinter as tk
|
||||
from pathlib import Path
|
||||
from tkinter import filedialog, messagebox
|
||||
|
||||
from ccma import __version__
|
||||
from ccma.config import load_config
|
||||
from ccma.domain.dates import setup_system_locale
|
||||
from ccma.storage.repository import MemberRepository
|
||||
from ccma.ui.main_window import MainWindow
|
||||
from ccma.ui.monitors import (
|
||||
centered_geometry,
|
||||
ensure_visible_geometry,
|
||||
monitor_for_geometry,
|
||||
preferred_monitor,
|
||||
)
|
||||
from ccma.ui.splash import SplashScreen, StartupResult
|
||||
from ccma.ui.theme import load_theme
|
||||
from ccma.ui.window_state import is_maximized, maximize
|
||||
|
||||
|
||||
class CCMAApp(tk.Tk):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
setup_system_locale()
|
||||
self.config_obj = load_config()
|
||||
self.title(f"CCMA · v{__version__}")
|
||||
self.minsize(1050, 650)
|
||||
load_theme(self, self.config_obj.theme_mode)
|
||||
self.startup_monitor = preferred_monitor(self, self.config_obj.monitor_bounds)
|
||||
initial_geometry = self.config_obj.window_geometry or centered_geometry(
|
||||
1500, 860, self.startup_monitor
|
||||
)
|
||||
self.geometry(ensure_visible_geometry(initial_geometry, self.startup_monitor))
|
||||
self._last_normal_geometry = self.geometry()
|
||||
self._geometry_capture_job: str | None = None
|
||||
self.bind("<Configure>", self._remember_window_geometry)
|
||||
self.protocol("WM_DELETE_WINDOW", self.close)
|
||||
self.withdraw()
|
||||
self.after_idle(self._start)
|
||||
|
||||
def _start(self) -> None:
|
||||
store_path = self._resolve_store()
|
||||
if not store_path:
|
||||
self.destroy()
|
||||
return
|
||||
repository = MemberRepository(store_path)
|
||||
try:
|
||||
SplashScreen(
|
||||
self,
|
||||
repository,
|
||||
self._startup_complete,
|
||||
self._startup_failed,
|
||||
run_housekeeper=self.config_obj.run_housekeeper_on_startup,
|
||||
monitor=self.startup_monitor,
|
||||
housekeeper_settings=self.config_obj.housekeeper_settings(),
|
||||
)
|
||||
except Exception as exc:
|
||||
self._startup_failed(exc)
|
||||
|
||||
def _resolve_store(self) -> Path | None:
|
||||
configured = self.config_obj.store_path.strip()
|
||||
if configured:
|
||||
return Path(configured).expanduser()
|
||||
selected = filedialog.askdirectory(
|
||||
parent=self,
|
||||
title="Zentrales CCMA-Mitgliederverzeichnis auswählen oder anlegen",
|
||||
initialdir=str(Path.home()),
|
||||
mustexist=False,
|
||||
)
|
||||
if not selected:
|
||||
return None
|
||||
self.config_obj.store_path = selected
|
||||
self.config_obj.save()
|
||||
return Path(selected)
|
||||
|
||||
def _startup_complete(self, result: StartupResult) -> None:
|
||||
self.deiconify()
|
||||
main = MainWindow(
|
||||
self,
|
||||
result.repository,
|
||||
self.config_obj,
|
||||
result.findings,
|
||||
result.validation_errors,
|
||||
)
|
||||
main.pack(fill="both", expand=True)
|
||||
if self.config_obj.window_state in {"maximized", "zoomed"}:
|
||||
self.after_idle(lambda: maximize(self))
|
||||
|
||||
def _startup_failed(self, error: Exception) -> None:
|
||||
self.deiconify()
|
||||
messagebox.showerror("CCMA konnte nicht gestartet werden", str(error), parent=self)
|
||||
self.destroy()
|
||||
|
||||
def _remember_window_geometry(self, _event: tk.Event | None = None) -> None:
|
||||
if self._geometry_capture_job:
|
||||
try:
|
||||
self.after_cancel(self._geometry_capture_job)
|
||||
except tk.TclError:
|
||||
pass
|
||||
self._geometry_capture_job = self.after(150, self._capture_normal_geometry)
|
||||
|
||||
def _capture_normal_geometry(self) -> None:
|
||||
self._geometry_capture_job = None
|
||||
try:
|
||||
if self.state() == "normal" and not is_maximized(self) and self.winfo_viewable():
|
||||
self._last_normal_geometry = self.winfo_geometry()
|
||||
except tk.TclError:
|
||||
return
|
||||
|
||||
def close(self) -> None:
|
||||
try:
|
||||
maximized = is_maximized(self)
|
||||
current_geometry = self.winfo_geometry()
|
||||
if not maximized:
|
||||
self._last_normal_geometry = current_geometry
|
||||
self.config_obj.window_geometry = self._last_normal_geometry
|
||||
self.config_obj.window_state = "maximized" if maximized else "normal"
|
||||
monitor = monitor_for_geometry(self, current_geometry)
|
||||
self.config_obj.monitor_bounds = monitor.as_tuple()
|
||||
self.config_obj.save()
|
||||
except (OSError, tk.TclError):
|
||||
pass
|
||||
self.destroy()
|
||||
|
||||
|
||||
def main() -> None:
|
||||
app = CCMAApp()
|
||||
app.mainloop()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user