Files
anonymisation/gui_v6/terms_table_window.py
Domi31tls a9e8b2c2e6 feat(gui): addenda Dom GUI V6 — sous-onglet Profils, libellés, aide, bêta
Suite des retours Dom sur la GUI V6 (par-dessus 6a0a581).

Addendum Profils / Réglages :
- Nouveau sous-onglet Administration « 👤 Profils » : le profil actif devient
  un objet lisible (nom, description, masque requis, template, listes locales
  avec compteurs) — données réelles lues depuis profile_defaults.
- Fenêtre « Tableau des termes » (terms_table_window.py) : table scrollable
  avec recherche/filtre, colonnes Type/Terme/Source ; reste lisible à 50+
  termes. Ajouter/éditer/supprimer désactivés « (à venir) » (écriture par
  profil non câblée).
- Réglages : « Profil métier » → « Profil d'anonymisation », « Sortie… » →
  « Dossier de sortie… » (+ infobulle), hints moteurs (standard/optionnel/
  plus lent), bouton « Voir le profil », « Ouvrir le tableau des termes ».
- Aide « ? » + infobulles (ui_kit.attach_tooltip) près des éléments ambigus.
- profile_view.py : logique pure (résumé profil + lignes du tableau),
  testable sans display.

Addendum bêta : en-tête « aivanonym » + badge « bêta », titre fenêtre
« … — bêta ». Détail version conservé dans À propos.

tests/unit/test_gui_v6_profiles.py + ajouts shell. 237 tests unit OK
(228 → 237, 0 régression), self-test GUI V6 OK, navigation des 5 sous-onglets
+ thème OK. V5/moteur/app_aivanov/profile_defaults non touchés, 0 dépendance.
Aucun build/push sans GO Dom — validation visuelle Dom attendue.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 17:02:54 +02:00

115 lines
4.6 KiB
Python

"""Fenêtre « Tableau des termes » d'un profil (lisible même avec 50+ termes).
Table scrollable avec recherche/filtre — colonnes Type / Terme / Source (profil).
Lecture seule pour l'instant : ajouter/supprimer/éditer sont désactivés et
marqués « (à venir) » (l'écriture par profil n'est pas encore câblée).
"""
from __future__ import annotations
import tkinter as tk
from typing import Optional
import customtkinter as ctk
from gui_v6 import ui_kit
from gui_v6.profile_view import filter_term_rows
_TYPE_COLORS = {
"À conserver": "success",
"À masquer": "primary",
"À ignorer": "text_muted",
}
class TermsTableWindow(ctk.CTkToplevel):
def __init__(
self,
master,
palette: dict,
rows,
*,
title: str = "Tableau des termes",
profile_label: str = "",
) -> None:
super().__init__(master)
self._palette = palette
self._rows = list(rows)
self._profile_label = profile_label
self._visible = list(self._rows)
self.title(title)
self.geometry("740x560")
self.minsize(520, 360)
try:
self.configure(fg_color=palette["bg"])
except Exception:
pass
self._query = tk.StringVar()
self._count_text = tk.StringVar(value="")
self._build()
self._refresh()
try:
self.transient(master)
self.after(120, lambda: (self.lift(), self.focus_force()))
except Exception:
pass
# -- coutures testables --------------------------------------------------
def set_query(self, query: str) -> None:
self._query.set(query)
self._refresh()
def visible_count(self) -> int:
return len(self._visible)
def add_is_disabled(self) -> bool:
return str(self._add_btn.cget("state")) == "disabled"
# -- UI ------------------------------------------------------------------
def _build(self) -> None:
p = self._palette
head = ctk.CTkFrame(self, fg_color="transparent")
head.pack(fill="x", padx=12, pady=(12, 4))
title = "Termes du profil"
if self._profile_label:
title += f" « {self._profile_label} »"
ctk.CTkLabel(head, text=title, text_color=p["text"], font=ui_kit.font(15, "bold")).pack(side="left")
bar = ctk.CTkFrame(self, fg_color="transparent")
bar.pack(fill="x", padx=12, pady=(0, 6))
ctk.CTkLabel(bar, text="🔎 Rechercher :", text_color=p["text_dim"], font=ui_kit.font(12)).pack(side="left")
entry = ctk.CTkEntry(bar, textvariable=self._query, width=240)
entry.pack(side="left", padx=6)
self._query.trace_add("write", lambda *_: self._refresh())
self._add_btn = ui_kit.secondary_button(bar, p, "+ Ajouter (à venir)", command=lambda: None)
self._add_btn.configure(state="disabled")
self._add_btn.pack(side="right")
header = ctk.CTkFrame(self, fg_color=p["card"], corner_radius=6)
header.pack(fill="x", padx=12)
for text, width in [("TYPE", 130), ("TERME", 360), ("SOURCE (PROFIL)", 180)]:
ctk.CTkLabel(header, text=text, width=width, anchor="w", text_color=p["text_muted"], font=ui_kit.font(10, "bold")).pack(side="left", padx=8, pady=4)
self._table = ctk.CTkScrollableFrame(self, fg_color=p["bg"])
self._table.pack(fill="both", expand=True, padx=12, pady=(2, 4))
ctk.CTkLabel(self, textvariable=self._count_text, text_color=p["text_muted"], font=ui_kit.font(11), anchor="w").pack(fill="x", padx=14, pady=(0, 10))
def _refresh(self) -> None:
p = self._palette
self._visible = filter_term_rows(self._rows, self._query.get())
for child in self._table.winfo_children():
child.destroy()
if not self._visible:
ctk.CTkLabel(self._table, text="Aucun terme.", text_color=p["text_muted"], font=ui_kit.font(12)).pack(anchor="w", padx=8, pady=8)
for type_label, term, source in self._visible:
row = ctk.CTkFrame(self._table, fg_color="transparent")
row.pack(fill="x", pady=1)
color = p[_TYPE_COLORS.get(type_label, "text")]
ctk.CTkLabel(row, text=type_label, width=130, anchor="w", text_color=color, font=ui_kit.font(11, "bold")).pack(side="left", padx=8)
ctk.CTkLabel(row, text=term, width=360, anchor="w", text_color=p["text"], font=ui_kit.font(12)).pack(side="left", padx=8)
ctk.CTkLabel(row, text=source, width=180, anchor="w", text_color=p["text_muted"], font=ui_kit.font(11)).pack(side="left", padx=8)
self._count_text.set(f"{len(self._visible)} terme(s) affiché(s) sur {len(self._rows)}.")