"""Shell minimal de la GUI V6 (lot G1). Header + bandeau de statut licence + navigation 3 onglets (Utilisation, Configuration, À propos). Seul « À propos » est étoffé en G1 ; les deux autres sont des placeholders qui seront remplis en G2/G3. Aucune logique de détection ici : ce module orchestre uniquement. La fenêtre n'est créée qu'à l'instanciation de :class:`AnonymisationApp` (import sûr). """ from __future__ import annotations from typing import Optional import customtkinter as ctk from gui_v6 import theme as theme_mod from gui_v6.license_client import LicenseClient, LicenseStatus from gui_v6.tabs.tab_about import AboutTab _TABS = ("Utilisation", "Configuration", "À propos") class AnonymisationApp(ctk.CTk): """Fenêtre principale (socle G1).""" def __init__( self, license_client: Optional[LicenseClient] = None, theme_name: str = theme_mod.DEFAULT_THEME, ) -> None: super().__init__() self._theme_name = theme_name theme_mod.apply_theme(theme_name) # Client licence : par défaut, lecture du statut local uniquement # (aucun appel réseau au démarrage). Injectable pour les tests. self._license_client = license_client or LicenseClient("http://localhost") status = self._safe_local_status() self.title("Pseudonymisation de vos documents") self.geometry("960x640") self._build_header(status) self._build_tabs(status) # -- statut licence --------------------------------------------------- def _safe_local_status(self) -> LicenseStatus: try: return self._license_client.local_status() except Exception: # Licence indisponible → dégradation silencieuse (mode bêta). return LicenseStatus.unavailable() # -- construction UI -------------------------------------------------- def _build_header(self, status: LicenseStatus) -> None: header = ctk.CTkFrame(self, height=56) header.pack(fill="x", padx=12, pady=(12, 6)) ctk.CTkLabel( header, text="Pseudonymisation", font=ctk.CTkFont(size=16, weight="bold"), ).pack(side="left", padx=12, pady=10) color = theme_mod.status_color(self._theme_name, status.status) self._status_banner = ctk.CTkLabel( header, text=self._banner_text(status), text_color=color ) self._status_banner.pack(side="right", padx=12, pady=10) def _build_tabs(self, status: LicenseStatus) -> None: tabview = ctk.CTkTabview(self) tabview.pack(fill="both", expand=True, padx=12, pady=(6, 12)) for name in _TABS: tabview.add(name) self._about = AboutTab( tabview.tab("À propos"), status=status, theme_name=self._theme_name ) self._about.pack(fill="both", expand=True) # Placeholders G2/G3. ctk.CTkLabel( tabview.tab("Utilisation"), text="Onglet Utilisation — disponible au lot G2.", ).pack(padx=16, pady=16, anchor="w") ctk.CTkLabel( tabview.tab("Configuration"), text="Onglet Configuration — disponible au lot G3.", ).pack(padx=16, pady=16, anchor="w") @staticmethod def _banner_text(status: LicenseStatus) -> str: return f"Licence : {status.status}"