feat(gui): GUI V6 G3 — câblage moteur, Configuration, licence UI, build-prep
G3-A câblage moteur réel (engine_bridge.py) : EngineSettings + NerManagers à chargement paresseux (aucun manager à l'import), kwargs alignés CLI/V5 (make_vector_redaction=False, also_make_raster_burn=True, config_path, use_hf, ner/gliner/camembert_manager, ogc_label) ; make_process_fn engine injectable ; état managers not_loaded/loading/ready/unavailable, échecs optionnels tolérés. G3-B Configuration (config_state.py + tabs/tab_config.py) : ConfigState → EngineSettings, profils via profile_defaults (path injectable), options raster/NER local/profil/sortie, état managers, sections admin-only via admin_mode. G3-C Licence UI (machine_id.py + tab_about) : activation par clef (LicenseClient.activate), bouton vérifier (check), affichage statut, aucun token loggé, aucun appel réseau au démarrage (local_status seul). Intégration : tab_usage exécute via le moteur réel selon ConfigState (make_process_fn), anti double-lancement UI. app.py câble Config↔Usage↔licence. G3-D build-prep : anonymisation_gui_v6_onefile.spec (entry V6, customtkinter + modules gui_v6 en hiddenimports). Installateur Anonymisation.iss produit déjà la cible Anonymisation-Setup.exe. Aucun artefact .exe commité ; build Windows à part. Tests +14 (engine_bridge 8, config_state 6). self-test exit 0, 46 tests gui_v6, 193 tests/unit (0 régression). Moteur/V5/specs CLI intacts. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -21,18 +21,44 @@ from gui_v6.processing_runner import ProcessingRunner, default_output_dir
|
||||
|
||||
|
||||
class UsageTab(ctk.CTkFrame):
|
||||
def __init__(self, master, runner: ProcessingRunner | None = None, **kwargs):
|
||||
def __init__(
|
||||
self,
|
||||
master,
|
||||
runner: ProcessingRunner | None = None,
|
||||
config_provider=None,
|
||||
config_path: Path | None = None,
|
||||
**kwargs,
|
||||
):
|
||||
super().__init__(master, **kwargs)
|
||||
self._runner = runner or ProcessingRunner()
|
||||
self._config_provider = config_provider
|
||||
self._config_path = config_path
|
||||
self._input_path: Path | None = None
|
||||
self._output_dir: Path | None = None
|
||||
self._stop_event: threading.Event | None = None
|
||||
self._worker: threading.Thread | None = None
|
||||
self._is_running = False
|
||||
self._events: "queue.Queue[tuple]" = queue.Queue()
|
||||
|
||||
self._build()
|
||||
self.after(120, self._drain_events)
|
||||
|
||||
def _build_run_runner(self) -> tuple[ProcessingRunner, Path | None]:
|
||||
"""Runner d'exécution + dossier sortie selon la configuration courante.
|
||||
|
||||
Si une configuration est fournie, câble le moteur réel via engine_bridge ;
|
||||
sinon retombe sur le runner par défaut (process_document direct).
|
||||
"""
|
||||
if self._config_provider is None:
|
||||
return self._runner, self._output_dir
|
||||
from gui_v6.engine_bridge import make_process_fn
|
||||
|
||||
cfg = self._config_provider()
|
||||
settings = cfg.to_engine_settings(self._config_path)
|
||||
runner = ProcessingRunner(process_fn=make_process_fn(settings))
|
||||
output_dir = self._output_dir or getattr(cfg, "output_dir", None)
|
||||
return runner, output_dir
|
||||
|
||||
# -- construction UI --------------------------------------------------
|
||||
|
||||
def _build(self) -> None:
|
||||
@@ -105,8 +131,10 @@ class UsageTab(ctk.CTkFrame):
|
||||
# -- exécution --------------------------------------------------------
|
||||
|
||||
def _start(self) -> None:
|
||||
if self._input_path is None or self._runner.is_running:
|
||||
if self._input_path is None or self._is_running:
|
||||
return
|
||||
self._is_running = True
|
||||
run_runner, run_output_dir = self._build_run_runner()
|
||||
self._stop_event = threading.Event()
|
||||
self._run_btn.configure(state="disabled")
|
||||
self._stop_btn.configure(state="normal")
|
||||
@@ -114,11 +142,11 @@ class UsageTab(ctk.CTkFrame):
|
||||
self._clear_log()
|
||||
self._set_status("Traitement en cours…")
|
||||
|
||||
input_path, output_dir, stop = self._input_path, self._output_dir, self._stop_event
|
||||
input_path, output_dir, stop = self._input_path, run_output_dir, self._stop_event
|
||||
|
||||
def work() -> None:
|
||||
try:
|
||||
summary = self._runner.run(
|
||||
summary = run_runner.run(
|
||||
input_path,
|
||||
output_dir,
|
||||
on_progress=lambda done, total, name: self._events.put(("progress", done, total, name)),
|
||||
@@ -164,6 +192,7 @@ class UsageTab(ctk.CTkFrame):
|
||||
self._finish(None)
|
||||
|
||||
def _finish(self, summary) -> None:
|
||||
self._is_running = False
|
||||
self._stop_btn.configure(state="disabled")
|
||||
self._run_btn.configure(state="normal")
|
||||
if summary is None:
|
||||
|
||||
Reference in New Issue
Block a user