feat(gui): add GUI V6 G1 foundation (license client/store, shell, About tab)
Socle de la refonte GUI V6 (couche présentation uniquement, aucune logique de détection) : - license_store: stockage licence hors dépôt (%LOCALAPPDATA%/Aivanov | XDG), read/write atomique/delete, ne journalise aucun token - license_client: LicenseStatus + activate/check/local_status, session HTTP injectable, serveur indisponible géré sans crash, aucune clé privée - theme: 4 thèmes + couleurs de statut licence - app + tab_about: shell customtkinter minimal (header, bandeau licence, 3 onglets), onglet À propos étoffé - Pseudonymisation_Gui_V6.py: point d'entrée + --self-test (exit 0 sans fenêtre) - requirements.txt: customtkinter==5.2.2 Tests: 20 nouveaux (store sur vrais fichiers, client sur session injectée). Suite tests/unit: 167 passed, 0 régression. V5/moteur/managers/specs intacts. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
1
gui_v6/tabs/__init__.py
Normal file
1
gui_v6/tabs/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""Onglets de la GUI V6."""
|
||||
76
gui_v6/tabs/tab_about.py
Normal file
76
gui_v6/tabs/tab_about.py
Normal file
@@ -0,0 +1,76 @@
|
||||
"""Onglet « À propos » : version, et état de la licence.
|
||||
|
||||
Affiche le statut licence fourni par le client/stub. Dégrade proprement si la
|
||||
licence est indisponible (bandeau d'information, pas d'erreur bloquante).
|
||||
Les widgets ne sont créés qu'à l'instanciation (import sûr pour ``--self-test``).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Optional
|
||||
|
||||
import customtkinter as ctk
|
||||
|
||||
from gui_v6 import __version__ as GUI_VERSION
|
||||
from gui_v6 import theme as theme_mod
|
||||
from gui_v6.license_client import LicenseStatus
|
||||
|
||||
_STATUS_LABELS = {
|
||||
"active": "Licence active",
|
||||
"grace": "Licence en période de grâce",
|
||||
"expired": "Licence expirée",
|
||||
"revoked": "Poste révoqué",
|
||||
"invalid": "Licence invalide",
|
||||
"unavailable": "Serveur de licence indisponible",
|
||||
"none": "Aucune licence",
|
||||
}
|
||||
|
||||
|
||||
def _build_info() -> str:
|
||||
"""Version / commit du build, si disponible. Best-effort, sans casser l'UI."""
|
||||
try:
|
||||
import build_info # type: ignore
|
||||
|
||||
version = getattr(build_info, "VERSION", "?")
|
||||
commit = getattr(build_info, "COMMIT", "?")
|
||||
return f"Moteur {version} ({commit})"
|
||||
except Exception:
|
||||
return "Moteur : information de build indisponible"
|
||||
|
||||
|
||||
class AboutTab(ctk.CTkFrame):
|
||||
def __init__(
|
||||
self,
|
||||
master,
|
||||
status: Optional[LicenseStatus] = None,
|
||||
theme_name: str = theme_mod.DEFAULT_THEME,
|
||||
**kwargs,
|
||||
) -> None:
|
||||
super().__init__(master, **kwargs)
|
||||
self._theme_name = theme_name
|
||||
|
||||
ctk.CTkLabel(
|
||||
self,
|
||||
text="Pseudonymisation de vos documents",
|
||||
font=ctk.CTkFont(size=18, weight="bold"),
|
||||
).pack(anchor="w", padx=16, pady=(16, 4))
|
||||
|
||||
ctk.CTkLabel(self, text=f"Interface V6 — {GUI_VERSION}").pack(
|
||||
anchor="w", padx=16
|
||||
)
|
||||
ctk.CTkLabel(self, text=_build_info()).pack(anchor="w", padx=16, pady=(0, 12))
|
||||
|
||||
self._status_label = ctk.CTkLabel(self, text="", anchor="w")
|
||||
self._status_label.pack(anchor="w", padx=16, pady=(0, 8))
|
||||
|
||||
self.set_status(status or LicenseStatus.none())
|
||||
|
||||
def set_status(self, status: LicenseStatus) -> None:
|
||||
label = _STATUS_LABELS.get(status.status, status.status)
|
||||
text = f"État licence : {label}"
|
||||
if status.expires_at:
|
||||
text += f" · expire le {status.expires_at}"
|
||||
if status.message:
|
||||
text += f"\n{status.message}"
|
||||
color = theme_mod.status_color(self._theme_name, status.status)
|
||||
self._status_label.configure(text=text, text_color=color)
|
||||
Reference in New Issue
Block a user