Files
anonymisation/gui_v6/editable_list.py
Domi31tls 72841ed7b3 feat(gui): onglet Profils éditable (création/modification/persistance)
Retour Dom : remplacer la page vitrine par un vrai éditeur de profils.

- gui_v6/profile_editor.py : couche logique (build_profile_spec,
  profile_is_editable runtime vs defaut, list_profile_choices, slug_for_copy,
  save/set_default/delete) au-dessus de profile_defaults — persistance dans
  config/profiles.yml.
- gui_v6/editable_list.py : EditableTermList (tableau scrollable de termes,
  ajout/suppression, pas de pastilles) — reste lisible à 50+ termes.
- tab_config : sous-onglet « 👤 Profils » réintroduit comme éditeur — menu
  déroulant « Profil à modifier », boutons Nouveau / Dupliquer / Enregistrer /
  Annuler / Définir par défaut, sections Identité, Masquage (require_manual_mask,
  template), Moteurs (force_disable_vlm), Mots (à masquer/conserver/ignorer
  éditables), Règles « à venir ». Profils défaut = lecture seule (dupliquer
  pour modifier). Confirmation non bloquante (pas de modale).
- Réglages : bouton « ✏️ Modifier le profil… » → ouvre Profils sur le profil
  actif. Pas de pastilles inline.

Persiste : label, description, require_manual_mask, force_disable_vlm,
preferred_manual_mask_template, param_lists (3 listes). 260 tests unit OK
(0 régression), self-test OK, nav 5 sous-onglets + thème OK. Préserve
1bbe70a/d30f7b7. Aucun build/push sans GO Dom.

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

102 lines
4.0 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Liste de termes éditable et scrollable (pas de pastilles).
Utilisée dans l'éditeur de profils pour « mots à masquer / à conserver / à
ignorer ». Reste lisible avec 50+ termes (zone scrollable + ajout/suppression).
"""
from __future__ import annotations
import customtkinter as ctk
from gui_v6 import ui_kit
class EditableTermList(ctk.CTkFrame):
def __init__(self, master, palette: dict, *, title: str, initial=None, height: int = 150, **kwargs):
super().__init__(master, fg_color="transparent", **kwargs)
self._palette = palette
self._title = title
self._terms: list[str] = [str(t) for t in (initial or [])]
self._editable = True
self._build(height)
self._render()
# -- API testable --------------------------------------------------------
def terms(self) -> list[str]:
return list(self._terms)
def set_terms(self, terms) -> None:
self._terms = [str(t) for t in (terms or [])]
self._render()
def set_editable(self, editable: bool) -> None:
self._editable = bool(editable)
state = "normal" if self._editable else "disabled"
self._entry.configure(state=state)
self._add_btn.configure(state=state)
self._render()
def add_term(self, term: str) -> bool:
term = str(term).strip()
if not term or term in self._terms:
return False
self._terms.append(term)
self._render()
return True
def remove_term(self, term: str) -> None:
if term in self._terms:
self._terms.remove(term)
self._render()
# -- UI ------------------------------------------------------------------
def _build(self, height: int) -> None:
p = self._palette
ctk.CTkLabel(self, text=self._title, text_color=p["text"], font=ui_kit.font(12, "bold"), anchor="w").pack(
fill="x", pady=(0, 2)
)
row = ctk.CTkFrame(self, fg_color="transparent")
row.pack(fill="x", pady=(0, 4))
self._entry = ctk.CTkEntry(
row, placeholder_text="Ajouter un terme…", fg_color=p["btn_sec_bg"],
border_color=p["btn_sec_border"], text_color=p["text"], height=28,
)
self._entry.pack(side="left", fill="x", expand=True, padx=(0, 6))
self._entry.bind("<Return>", lambda _e: self._on_add())
self._add_btn = ui_kit.secondary_button(row, p, "+ Ajouter", command=self._on_add)
self._add_btn.pack(side="right")
self._list = ctk.CTkScrollableFrame(self, fg_color=p["divider"], height=height)
self._list.pack(fill="both", expand=True)
self._count = ctk.CTkLabel(self, text="", text_color=p["text_muted"], font=ui_kit.font(10), anchor="w")
self._count.pack(fill="x", pady=(2, 0))
def _on_add(self) -> None:
if not self._editable:
return
if self.add_term(self._entry.get()):
self._entry.delete(0, "end")
def _render(self) -> None:
p = self._palette
for child in self._list.winfo_children():
child.destroy()
if not self._terms:
ctk.CTkLabel(self._list, text="Aucun terme.", text_color=p["text_muted"], font=ui_kit.font(11)).pack(
anchor="w", padx=8, pady=6
)
for term in self._terms:
line = ctk.CTkFrame(self._list, fg_color="transparent")
line.pack(fill="x", pady=1)
ctk.CTkLabel(line, text=term, text_color=p["text"], font=ui_kit.font(12), anchor="w").pack(
side="left", fill="x", expand=True, padx=(6, 4)
)
btn = ctk.CTkButton(
line, text="×", width=26, height=24, corner_radius=6,
fg_color=p["btn_sec_bg"], hover_color=p["card_border"], text_color=p["text"],
command=lambda t=term: self.remove_term(t),
)
if not self._editable:
btn.configure(state="disabled")
btn.pack(side="right", padx=(0, 4))
self._count.configure(text=f"{len(self._terms)} terme(s)")