Améliorations GUI
This commit is contained in:
+160
-18
@@ -8,7 +8,7 @@ from tkinter import filedialog, messagebox, ttk
|
||||
from typing import Any
|
||||
|
||||
from copienator import ExitCode
|
||||
from platform_utils import WindowsLabelError, validate_windows_labels
|
||||
from platform_utils import WindowsLabelError, open_path, validate_windows_labels
|
||||
|
||||
from .diagnostics import collect_diagnostics
|
||||
from .runner import ProcessRunner
|
||||
@@ -35,6 +35,65 @@ STATUS_LABELS = {
|
||||
"detected": "Détectée",
|
||||
}
|
||||
|
||||
DEFAULT_HTTPS_PROXY = "http://10.0.0.1:3128"
|
||||
|
||||
|
||||
def copy_pdf_paths(evaluation: Path) -> list[Path]:
|
||||
"""List the most relevant version of each scanned copy."""
|
||||
locations = (evaluation / "Copies", evaluation, evaluation / "Copies Originales")
|
||||
for location in locations:
|
||||
if not location.is_dir():
|
||||
continue
|
||||
copies = sorted(
|
||||
(
|
||||
path
|
||||
for path in location.glob("*.pdf")
|
||||
if path.name.casefold() not in {"enonce.pdf", "énoncé.pdf"}
|
||||
),
|
||||
key=lambda path: path.name.casefold(),
|
||||
)
|
||||
if copies:
|
||||
return copies
|
||||
return []
|
||||
|
||||
|
||||
def plotting_shortcut_lines() -> list[str]:
|
||||
try:
|
||||
from config import PLOTTING_KB
|
||||
except (ImportError, AttributeError):
|
||||
from default_config import PLOTTING_KB
|
||||
|
||||
labels = (
|
||||
("OK", "valider et passer à la suivante"),
|
||||
("previous", "revenir à la précédente"),
|
||||
("edit", "éditer le fichier JSON"),
|
||||
("open pdf", "ouvrir la copie traitée"),
|
||||
("open original pdf", "ouvrir la copie originale"),
|
||||
("open eval", "ouvrir l’énoncé"),
|
||||
)
|
||||
display_names = {"<Return>": "Entrée", "<Escape>": "Échap"}
|
||||
lines = [
|
||||
f"{display_names.get(PLOTTING_KB[action], PLOTTING_KB[action])} : {description}"
|
||||
for action, description in labels
|
||||
]
|
||||
lines.append("Échap : fermer la fenêtre")
|
||||
return lines
|
||||
|
||||
|
||||
def build_runner_environment(
|
||||
base: dict[str, str], api_key: str, proxy: str, use_proxy: bool
|
||||
) -> dict[str, str]:
|
||||
environment = dict(base)
|
||||
environment["PYTHONUNBUFFERED"] = "1"
|
||||
environment["PYTHONIOENCODING"] = "utf-8"
|
||||
if api_key.strip():
|
||||
environment["GEMINI_API_KEY"] = api_key.strip()
|
||||
environment.pop("HTTPS_PROXY", None)
|
||||
environment.pop("https_proxy", None)
|
||||
if use_proxy and proxy.strip():
|
||||
environment["HTTPS_PROXY"] = proxy.strip()
|
||||
return environment
|
||||
|
||||
|
||||
def process_status(return_code: int, interrupted: bool = False) -> str:
|
||||
if interrupted or return_code == ExitCode.INTERRUPTED:
|
||||
@@ -76,6 +135,7 @@ class CopienatorApp(tk.Tk):
|
||||
self.active_step_id: str | None = None
|
||||
self.current_step: StepDefinition | None = None
|
||||
self.arg_vars: dict[str, tk.Variable] = {}
|
||||
self.copy_paths: dict[str, Path] = {}
|
||||
self._rendering = False
|
||||
|
||||
self.title("Copienator — assistant de correction")
|
||||
@@ -85,7 +145,9 @@ class CopienatorApp(tk.Tk):
|
||||
|
||||
self.evaluation_var = tk.StringVar()
|
||||
self.api_key_var = tk.StringVar(value=os.environ.get("GEMINI_API_KEY", ""))
|
||||
self.proxy_var = tk.StringVar(value=os.environ.get("HTTPS_PROXY", ""))
|
||||
self.proxy_var = tk.StringVar(value=DEFAULT_HTTPS_PROXY)
|
||||
self.use_proxy_var = tk.BooleanVar(value=False)
|
||||
self.copy_var = tk.StringVar()
|
||||
self.variant_var = tk.StringVar()
|
||||
self.extra_var = tk.StringVar()
|
||||
self.command_var = tk.StringVar(value="Sélectionnez une étape.")
|
||||
@@ -113,13 +175,17 @@ class CopienatorApp(tk.Tk):
|
||||
|
||||
top = ttk.Frame(self, padding=(10, 8))
|
||||
top.grid(row=0, column=0, sticky="ew")
|
||||
top.columnconfigure(1, weight=1)
|
||||
ttk.Label(top, text="Évaluation").grid(row=0, column=0, sticky="w", padx=(0, 8))
|
||||
top.columnconfigure(2, weight=1)
|
||||
ttk.Button(top, text="Charger", command=self._load_evaluation).grid(
|
||||
row=0, column=0, padx=(0, 8)
|
||||
)
|
||||
ttk.Label(top, text="Évaluation").grid(row=0, column=1, sticky="w", padx=(0, 8))
|
||||
path_entry = ttk.Entry(top, textvariable=self.evaluation_var)
|
||||
path_entry.grid(row=0, column=1, sticky="ew")
|
||||
path_entry.grid(row=0, column=2, sticky="ew")
|
||||
path_entry.bind("<Return>", lambda _event: self._load_evaluation())
|
||||
ttk.Button(top, text="Parcourir…", command=self._browse_evaluation).grid(row=0, column=2, padx=6)
|
||||
ttk.Button(top, text="Charger", command=self._load_evaluation).grid(row=0, column=3)
|
||||
ttk.Button(top, text="Parcourir…", command=self._browse_evaluation).grid(
|
||||
row=0, column=3, padx=6
|
||||
)
|
||||
ttk.Button(top, text="Diagnostic…", command=self._show_diagnostics).grid(row=0, column=4, padx=(6, 0))
|
||||
|
||||
ttk.Label(top, text="Clé Gemini").grid(row=1, column=0, sticky="w", padx=(0, 8), pady=(7, 0))
|
||||
@@ -127,9 +193,15 @@ class CopienatorApp(tk.Tk):
|
||||
row=1, column=1, sticky="w", pady=(7, 0)
|
||||
)
|
||||
environment = ttk.Frame(top)
|
||||
environment.grid(row=1, column=2, columnspan=2, sticky="e", pady=(7, 0))
|
||||
ttk.Label(environment, text="HTTPS_PROXY").pack(side="left", padx=(0, 6))
|
||||
ttk.Entry(environment, textvariable=self.proxy_var, width=28).pack(side="left")
|
||||
environment.grid(row=1, column=2, columnspan=3, sticky="e", pady=(7, 0))
|
||||
ttk.Checkbutton(
|
||||
environment,
|
||||
text="Utiliser le proxy HTTPS",
|
||||
variable=self.use_proxy_var,
|
||||
command=self._toggle_proxy,
|
||||
).pack(side="left", padx=(0, 6))
|
||||
self.proxy_entry = ttk.Entry(environment, textvariable=self.proxy_var, width=28, state="disabled")
|
||||
self.proxy_entry.pack(side="left")
|
||||
profile = "standard + personnel" if show_personal_steps else "standard"
|
||||
ttk.Label(environment, text=f"Profil : {profile}").pack(side="left", padx=(12, 0))
|
||||
|
||||
@@ -269,6 +341,9 @@ class CopienatorApp(tk.Tk):
|
||||
self.evaluation_var.set(selected)
|
||||
self._load_evaluation()
|
||||
|
||||
def _toggle_proxy(self) -> None:
|
||||
self.proxy_entry.configure(state="normal" if self.use_proxy_var.get() else "disabled")
|
||||
|
||||
def _load_evaluation(self) -> None:
|
||||
evaluation = self.evaluation
|
||||
if not evaluation or not evaluation.is_dir():
|
||||
@@ -437,7 +512,7 @@ class CopienatorApp(tk.Tk):
|
||||
description += "\nPrérequis non détectés : " + ", ".join(missing)
|
||||
self.description_label.configure(text=description)
|
||||
|
||||
row = 0
|
||||
row = self._render_context_controls(step, 0)
|
||||
if len(step.variants) > 1:
|
||||
ttk.Label(self.form, text="Mode").grid(row=row, column=0, sticky="w", pady=4, padx=(0, 8))
|
||||
labels = [variant.label for variant in step.variants]
|
||||
@@ -499,6 +574,74 @@ class CopienatorApp(tk.Tk):
|
||||
self._update_command_preview()
|
||||
self._update_controls()
|
||||
|
||||
def _render_context_controls(self, step: StepDefinition, row: int) -> int:
|
||||
if step.id == "review_persp":
|
||||
ttk.Button(
|
||||
self.form,
|
||||
text="Ouvrir le dossier Persp",
|
||||
command=self._open_persp,
|
||||
).grid(row=row, column=0, columnspan=2, sticky="w", pady=(0, 8))
|
||||
row += 1
|
||||
|
||||
if step.section == "Prétraitement des copies":
|
||||
evaluation = self.evaluation
|
||||
paths = copy_pdf_paths(evaluation) if evaluation else []
|
||||
self.copy_paths = {path.name: path for path in paths}
|
||||
names = list(self.copy_paths)
|
||||
copies = ttk.LabelFrame(self.form, text="Copies détectées", padding=7)
|
||||
copies.grid(row=row, column=0, columnspan=2, sticky="ew", pady=(0, 8))
|
||||
copies.columnconfigure(0, weight=1)
|
||||
summary = (
|
||||
f"{len(names)} copie(s) : {', '.join(names)}"
|
||||
if names
|
||||
else "Aucune copie PDF détectée."
|
||||
)
|
||||
ttk.Label(copies, text=summary, wraplength=570, justify="left").grid(
|
||||
row=0, column=0, columnspan=2, sticky="ew"
|
||||
)
|
||||
self.copy_var.set(names[0] if names else "")
|
||||
selector = ttk.Combobox(
|
||||
copies,
|
||||
textvariable=self.copy_var,
|
||||
values=names,
|
||||
state="readonly" if names else "disabled",
|
||||
)
|
||||
selector.grid(row=1, column=0, sticky="ew", pady=(7, 0))
|
||||
ttk.Button(
|
||||
copies,
|
||||
text="Afficher la copie",
|
||||
command=self._open_selected_copy,
|
||||
state="normal" if names else "disabled",
|
||||
).grid(row=1, column=1, padx=(6, 0), pady=(7, 0))
|
||||
row += 1
|
||||
|
||||
if step.id == "plotting":
|
||||
shortcuts = ttk.LabelFrame(self.form, text="Raccourcis clavier", padding=7)
|
||||
shortcuts.grid(row=row, column=0, columnspan=2, sticky="ew", pady=(0, 8))
|
||||
ttk.Label(
|
||||
shortcuts,
|
||||
text="\n".join(plotting_shortcut_lines()),
|
||||
justify="left",
|
||||
).grid(row=0, column=0, sticky="w")
|
||||
row += 1
|
||||
return row
|
||||
|
||||
def _open_persp(self) -> None:
|
||||
evaluation = self.evaluation
|
||||
self._open_desktop_path(evaluation / "Persp" if evaluation else None, "dossier Persp")
|
||||
|
||||
def _open_selected_copy(self) -> None:
|
||||
self._open_desktop_path(self.copy_paths.get(self.copy_var.get()), "copie")
|
||||
|
||||
def _open_desktop_path(self, path: Path | None, label: str) -> None:
|
||||
if path is None or not path.exists():
|
||||
messagebox.showerror("Élément introuvable", f"Le {label} n’existe pas encore.")
|
||||
return
|
||||
try:
|
||||
open_path(path)
|
||||
except (OSError, RuntimeError) as exc:
|
||||
messagebox.showerror("Ouverture impossible", str(exc))
|
||||
|
||||
def _select_variant(self, index: int) -> None:
|
||||
if not self.current_step or index < 0:
|
||||
return
|
||||
@@ -640,13 +783,12 @@ class CopienatorApp(tk.Tk):
|
||||
assert workspace is not None
|
||||
log_path = workspace.log_path(step.id)
|
||||
|
||||
environment = os.environ.copy()
|
||||
environment["PYTHONUNBUFFERED"] = "1"
|
||||
environment["PYTHONIOENCODING"] = "utf-8"
|
||||
if self.api_key_var.get().strip():
|
||||
environment["GEMINI_API_KEY"] = self.api_key_var.get().strip()
|
||||
if self.proxy_var.get().strip():
|
||||
environment["HTTPS_PROXY"] = self.proxy_var.get().strip()
|
||||
environment = build_runner_environment(
|
||||
os.environ,
|
||||
self.api_key_var.get(),
|
||||
self.proxy_var.get(),
|
||||
self.use_proxy_var.get(),
|
||||
)
|
||||
|
||||
self._append_console(f"\n$ {command_display(command)}\n")
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user