SXXXXXXX_CppPythonDebug/cpp_python_debug/gui/dialogs.py
2025-05-26 07:43:57 +02:00

215 lines
8.8 KiB
Python

# File: cpp_python_debug/gui/dialogs.py
import tkinter as tk
from tkinter import ttk, messagebox, scrolledtext
import logging
from typing import List, Optional
logger = logging.getLogger(__name__)
class SymbolAnalysisProgressDialog(tk.Toplevel):
"""
Dialog to show progress of symbol analysis.
"""
def __init__(self, parent: tk.Widget):
super().__init__(parent)
self.title("Symbol Analysis")
# Centra la finestra rispetto al genitore
parent_x = parent.winfo_x()
parent_y = parent.winfo_y()
parent_width = parent.winfo_width()
parent_height = parent.winfo_height()
width = 600
height = 400
x = parent_x + (parent_width // 2) - (width // 2)
y = parent_y + (parent_height // 2) - (height // 2)
self.geometry(f'{width}x{height}+{x}+{y}')
self.transient(parent)
self.grab_set() # Rendi modale
self.protocol("WM_DELETE_WINDOW", self._on_attempt_close)
self.analysis_can_be_closed = False
main_frame = ttk.Frame(self, padding="10")
main_frame.pack(expand=True, fill=tk.BOTH)
self.log_text_widget = scrolledtext.ScrolledText(main_frame, wrap=tk.WORD, height=15, state=tk.DISABLED, font=("Consolas", 9))
self.log_text_widget.pack(pady=5, padx=5, fill=tk.BOTH, expand=True)
self.status_label_var = tk.StringVar(value="Initializing symbol analysis...")
ttk.Label(main_frame, textvariable=self.status_label_var, justify=tk.LEFT).pack(pady=(5,2), fill=tk.X)
self.progressbar = ttk.Progressbar(main_frame, mode='indeterminate', length=300)
self.progressbar.pack(pady=(0,10), fill=tk.X, padx=5)
self.progressbar.start(15)
self.close_button = ttk.Button(main_frame, text="Close", command=self._on_close_button_click, state=tk.DISABLED)
self.close_button.pack(pady=5)
self.update_idletasks() # Assicura che la finestra sia disegnata
def _on_attempt_close(self):
if not self.analysis_can_be_closed:
messagebox.showwarning("Analysis in Progress",
"Symbol analysis is still in progress. Please wait.",
parent=self)
else:
self.destroy()
def _on_close_button_click(self):
self.destroy()
def log_message(self, message: str):
if not self.winfo_exists(): return
self.log_text_widget.config(state=tk.NORMAL)
self.log_text_widget.insert(tk.END, message + "\n")
self.log_text_widget.see(tk.END)
self.log_text_widget.config(state=tk.DISABLED)
try:
self.update_idletasks()
except tk.TclError: # Può accadere se la finestra viene distrutta mentre si logga
pass
def set_status(self, status_message: str):
if not self.winfo_exists(): return
self.status_label_var.set(status_message)
try:
self.update_idletasks()
except tk.TclError:
pass
def analysis_complete_or_failed(self, success: bool):
if not self.winfo_exists(): return
self.progressbar.stop()
self.progressbar.config(mode='determinate')
self.progressbar['value'] = 100 if success else 0
self.analysis_can_be_closed = True
self.close_button.config(state=tk.NORMAL)
if success:
self.set_status("Analysis complete. You can close this window.")
else:
self.set_status("Analysis failed or was aborted. Check log. You can close this window.")
class SymbolListViewerDialog(tk.Toplevel):
"""A simple dialog to view a list of symbols with a filter."""
def __init__(self, parent: tk.Widget, symbols: List[str], title: str = "Symbol List"):
super().__init__(parent)
self.title(title)
# Centra la finestra rispetto al genitore
parent_x = parent.winfo_x()
parent_y = parent.winfo_y()
parent_width = parent.winfo_width()
parent_height = parent.winfo_height()
width = 500
height = 450
x = parent_x + (parent_width // 2) - (width // 2)
y = parent_y + (parent_height // 2) - (height // 2)
self.geometry(f'{width}x{height}+{x}+{y}')
self.transient(parent)
self.grab_set()
main_frame = ttk.Frame(self, padding="10")
main_frame.pack(expand=True, fill=tk.BOTH)
main_frame.rowconfigure(1, weight=1) # La Listbox si espande
main_frame.columnconfigure(0, weight=1) # La Listbox si espande
# Campo per il filtro
filter_frame = ttk.Frame(main_frame)
filter_frame.grid(row=0, column=0, sticky="ew", pady=(0, 5))
ttk.Label(filter_frame, text="Filter:").pack(side=tk.LEFT, padx=(0,5))
self.filter_var = tk.StringVar()
self.filter_var.trace_add("write", self._apply_filter)
ttk.Entry(filter_frame, textvariable=self.filter_var, width=40).pack(side=tk.LEFT, expand=True, fill=tk.X)
# Listbox per visualizzare i simboli
self.listbox = tk.Listbox(main_frame, selectmode=tk.SINGLE)
self.listbox.grid(row=1, column=0, sticky="nsew")
# Scrollbars
scrollbar_y = ttk.Scrollbar(main_frame, orient=tk.VERTICAL, command=self.listbox.yview)
scrollbar_y.grid(row=1, column=1, sticky="ns")
self.listbox.configure(yscrollcommand=scrollbar_y.set)
scrollbar_x = ttk.Scrollbar(main_frame, orient=tk.HORIZONTAL, command=self.listbox.xview)
scrollbar_x.grid(row=2, column=0, sticky="ew")
self.listbox.configure(xscrollcommand=scrollbar_x.set)
self._original_symbols = sorted(symbols) # Salva e ordina la lista originale
self._populate_listbox(self._original_symbols)
# Bottone Close
button_frame = ttk.Frame(main_frame, padding=(0, 10, 0, 0))
button_frame.grid(row=3, column=0, columnspan=2, sticky="e")
ttk.Button(button_frame, text="Close", command=self.destroy).pack()
self.protocol("WM_DELETE_WINDOW", self.destroy)
def _populate_listbox(self, symbols_to_show: List[str]):
"""Popola la listbox con i simboli forniti."""
self.listbox.delete(0, tk.END)
for item in symbols_to_show:
self.listbox.insert(tk.END, item)
def _apply_filter(self, *args):
"""Applica il filtro alla lista di simboli visualizzata."""
filter_text = self.filter_var.get().lower()
if not filter_text:
self._populate_listbox(self._original_symbols)
else:
filtered_list = [s for s in self._original_symbols if filter_text in s.lower()]
self._populate_listbox(filtered_list)
# FunctionSelectorDialog è utilizzata da action_editor_window.py
# La definiamo qui per spostare tutto ciò che è dialogo in questo file.
# È simile a SymbolListViewerDialog ma gestisce una selezione.
class FunctionSelectorDialog(tk.Toplevel):
"""Dialog to select a function from a list."""
def __init__(self, parent: tk.Widget, functions_list: List[str], title: str = "Select Function"):
super().__init__(parent)
self.title(title)
self.geometry("500x400") # Puoi aggiustare la dimensione
self.transient(parent)
self.grab_set()
self.result: Optional[str] = None # Per memorizzare la funzione selezionata
main_frame = ttk.Frame(self, padding="10")
main_frame.pack(expand=True, fill=tk.BOTH)
main_frame.rowconfigure(0, weight=1)
main_frame.columnconfigure(0, weight=1)
self.listbox = tk.Listbox(main_frame, selectmode=tk.SINGLE)
self.listbox.grid(row=0, column=0, sticky="nsew")
scrollbar = ttk.Scrollbar(main_frame, orient=tk.VERTICAL, command=self.listbox.yview)
scrollbar.grid(row=0, column=1, sticky="ns")
self.listbox.configure(yscrollcommand=scrollbar.set)
for func_name in functions_list:
self.listbox.insert(tk.END, func_name)
self.listbox.bind("<Double-Button-1>", self._on_ok) # Doppioclick per selezionare e chiudere
button_frame = ttk.Frame(main_frame, padding=(0,10,0,0))
button_frame.grid(row=1, column=0, columnspan=2, sticky="e") # Posiziona sotto la listbox
ttk.Button(button_frame, text="OK", command=self._on_ok).pack(side=tk.RIGHT, padx=5)
ttk.Button(button_frame, text="Cancel", command=self._on_cancel).pack(side=tk.RIGHT)
if functions_list:
self.listbox.selection_set(0) # Seleziona il primo elemento
self.listbox.focus_set()
self.protocol("WM_DELETE_WINDOW", self._on_cancel)
self.wait_window() # Rende la dialog modale
def _on_ok(self, event=None):
selection = self.listbox.curselection()
if selection:
self.result = self.listbox.get(selection[0])
self.destroy()
def _on_cancel(self):
self.result = None
self.destroy()