# 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("", 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()