From 085e99632f4f708479c2c939a8d1e616a5ceeda9 Mon Sep 17 00:00:00 2001 From: Miguel Date: Sun, 1 Jun 2025 17:20:22 +0200 Subject: [PATCH] Version con Plot funcionando --- MaVCalc.lnk | Bin 0 -> 1722 bytes hybrid_calc_settings.json | 4 +- hybrid_evaluation_engine.py | 43 +++-- icon.ico | Bin 0 -> 4866 bytes interactive_results.py | 356 +++++++++++++++++++++--------------- plots_interactive_readme.md | 147 +++++++++++++++ test_interactive_plots.py | 177 ++++++++++++++++++ 7 files changed, 566 insertions(+), 161 deletions(-) create mode 100644 MaVCalc.lnk create mode 100644 icon.ico create mode 100644 plots_interactive_readme.md create mode 100644 test_interactive_plots.py diff --git a/MaVCalc.lnk b/MaVCalc.lnk new file mode 100644 index 0000000000000000000000000000000000000000..244d20b06a1b71eb6e392fb505a356ca43572b89 GIT binary patch literal 1722 zcmbVMZ)j6j6hE)+WKNk8JMFryW_6YbOGuk;Z8ORmUPzN!tMXQ@*~1vqx0h$lYu6+i znY1vrL5tnyr2hL*D>yTUl?l_b=|(?TWUz|3KTtam2KB>+M(Gz3ar>S7n${0?8{Wt7 z-t*2q_jk@c=ib{yL{f19dZJO$Q(&5&COhQSt_6dezL}IxI@F=`_@tx{3`Bm+OiH%> zlCZRTYZ3R?B`>WEHI^1`>3TEk?;UFyc-Zc~T|p96Q0@meEV5Hyc2DFcq?={h>FP_9 z(p3FBg1MObC`Dx?2b4ewRSHUH>t};DS{G^#$?Lm?Dpk5=>%kj82mAFEo{P5~-;Ism zzi~|8cb2&w%nre9e}nh`%p9qZ&OCTk(!jTiu#AeqYwhMxc%x~f zw?n>&BJ*yVW*85J*O~cPJ$=(lQzaXT8Yqax~5v8TsIGUXFB6fprgf<02os$weAv0HRXh0PrGE zI5RRw0}umB0M^G7uh?xE6~!OfY?vf_)t02zl~kcy1@O*_0jm{|s>+J5xka|DC}HSn zO-&f45$iD9q8^oVucnM>BpK^6I#Y(JtB-E(c@)b_Ds4bYIuN>sU{a8h&?%23ivMM# zZzpuCsQd|5saR>PcZnpGKOsy0u(@WA9VhE4qM^UhypTD;i}1g9$(3m-^wV1(2Y>x# zZBg!1p4$c>$wRFexy6!EGaa*J?lYJ6rZ@L|6*}J^IWE`v$M3ILo{iVtS$d@K>)&(7 z)c5~uei+qR2>$)xs#G>8x7^=fm#Mcgd++M96UleW)6loEqm@7vF#nmflLHCo(zKu$ zX{3(NtW_Akbvkq!W*>J0Rm@noXAL_;>jgNVKl)k&7n^G33lEkZs5qRsl7PO54LBe^ z*a|su*_`wmP$i@)s=?=`*KxHyRE^Jrxmxs<(D8pA#u{Po#$PRDwy6;kv$^qE7no=m zJ~$Bf-PI$x#!}lCcMG=d8~p1u2P&Qc4rDPf-#vN`bK)gxfIt_{h{u(cyogxHzpP9- zyEmK&@_}@xI5kn6MT4f1ByPOuSr!_A{QJ!R#{mxZ bool: """Determina si el resultado requiere interactividad""" + # 🔧 CORRECCIÓN: Importar PlotResult desde el lugar correcto + from interactive_results import PlotResult + return isinstance(self.result, (PlotResult, sympy.Matrix)) or \ (isinstance(self.result, list) and len(self.result) > 3) @@ -469,21 +486,6 @@ class EvaluationResult: return "" -class PlotResult: - """Placeholder para resultados de plotting""" - - def __init__(self, plot_type: str, args: tuple, kwargs: dict): - self.plot_type = plot_type - self.args = args - self.kwargs = kwargs - - def __str__(self): - return f"📊 Ver {self.plot_type.title()}" - - def __repr__(self): - return f"PlotResult('{self.plot_type}', {self.args}, {self.kwargs})" - - # Funciones de testing def test_evaluation_engine(): """Test del motor de evaluación""" @@ -500,6 +502,10 @@ def test_evaluation_engine(): "Hex[FF]", "IP4[192.168.1.1/24]", + # 🧪 PLOTS - Casos específicos para testing + "plot(sin(x), (x, -pi, pi))", + "plot(x**2, (x, -5, 5))", + # Ecuaciones "x + 2 = 5", "y**2 = 16", @@ -520,6 +526,13 @@ def test_evaluation_engine(): for test in test_cases: result = engine.evaluate_line(test) print(f"'{test}' → {result} (type: {result.result_type})") + + # 🔍 Información adicional para plots + if 'plot' in test: + print(f" 🎯 Es interactivo: {result.is_interactive}") + if isinstance(result.result, PlotResult): + print(f" 📊 PlotResult confirmado: {result.result.plot_type}") + if result.info: print(f" Info: {result.info}") diff --git a/icon.ico b/icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..737fada3ca18efdf27a64fbeebf379267550791b GIT binary patch literal 4866 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 Optional[str]: + def create_interactive_tag(self, result: Any, text_widget: tk.Text, index: str) -> Optional[Tuple[str, str]]: """ Crea un tag interactivo para un resultado si es necesario Returns: - Tag name si se creó, None si no es necesario + (tag_name, display_text) si se creó tag, None si no es necesario """ tag_name = None display_text = "" + # 🔧 CORRECCIÓN: Verificar con isinstance correcto if isinstance(result, PlotResult): tag_name = f"plot_{id(result)}" display_text = f"📊 Ver {result.plot_type.title()}" @@ -48,37 +64,43 @@ class InteractiveResultManager: tag_name = f"object_{id(result)}" display_text = f"🔍 Ver Detalles ({type(result).__name__})" - if tag_name: - # Configurar tag - text_widget.tag_configure( - tag_name, - foreground="#4fc3f7", - underline=True, - font=("Consolas", 11, "underline") - ) - - # Bind click event - text_widget.tag_bind( - tag_name, - "", - lambda e, r=result: self._handle_interactive_click(r) - ) - - text_widget.tag_bind( - tag_name, - "", - lambda e: text_widget.config(cursor="hand2") - ) - - text_widget.tag_bind( - tag_name, - "", - lambda e: text_widget.config(cursor="") - ) - - return tag_name, display_text + # 🔧 CORRECCIÓN: Solo crear tag si se encontró un tipo interactivo + if tag_name and display_text: + try: + # Configurar tag + text_widget.tag_configure( + tag_name, + foreground="#4fc3f7", + underline=True, + font=("Consolas", 11, "underline") + ) + + # Bind click event + text_widget.tag_bind( + tag_name, + "", + lambda e, r=result: self._handle_interactive_click(r) + ) + + text_widget.tag_bind( + tag_name, + "", + lambda e: text_widget.config(cursor="hand2") + ) + + text_widget.tag_bind( + tag_name, + "", + lambda e: text_widget.config(cursor="") + ) + + return (tag_name, display_text) + + except Exception as e: + print(f"⚠️ Error creando tag interactivo: {e}") + return None - return None, str(result) + return None def _handle_interactive_click(self, result: Any): """Maneja clicks en elementos interactivos""" @@ -87,26 +109,32 @@ class InteractiveResultManager: # Si ya existe la ventana, enfocarla if window_key in self.open_windows: window = self.open_windows[window_key] - if window.winfo_exists(): - window.lift() - window.focus_set() - return - else: + try: + if window.winfo_exists(): + window.lift() + window.focus_set() + return + else: + del self.open_windows[window_key] + except tk.TclError: del self.open_windows[window_key] # Crear nueva ventana - if isinstance(result, PlotResult): - self._show_plot_window(result, window_key) - elif isinstance(result, sympy.Matrix): - self._show_matrix_window(result, window_key) - elif isinstance(result, list): - self._show_list_window(result, window_key) - elif isinstance(result, dict): - self._show_dict_window(result, window_key) - else: - self._show_object_window(result, window_key) + try: + if isinstance(result, PlotResult): + self._show_plot_window(result, window_key) + elif isinstance(result, sympy.Matrix): + self._show_matrix_window(result, window_key) + elif isinstance(result, list): + self._show_list_window(result, window_key) + elif isinstance(result, dict): + self._show_dict_window(result, window_key) + else: + self._show_object_window(result, window_key) + except Exception as e: + print(f"❌ Error abriendo ventana interactiva: {e}") - def _show_plot_window(self, plot_result: 'PlotResult', window_key: str): + def _show_plot_window(self, plot_result: PlotResult, window_key: str): """Muestra ventana con plot matplotlib""" window = self._create_base_window(f"Plot - {plot_result.plot_type}", "800x600") self.open_windows[window_key] = window @@ -124,76 +152,97 @@ class InteractiveResultManager: canvas.draw() canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True) + # Toolbar para interactividad + try: + from matplotlib.backends.backend_tkagg import NavigationToolbar2Tk + toolbar = NavigationToolbar2Tk(canvas, window) + toolbar.update() + except ImportError: + pass # Si no está disponible, continuar sin toolbar + except Exception as e: error_label = tk.Label( window, text=f"Error generando plot: {e}", fg="red", - bg="#2b2b2b" + bg="#2b2b2b", + font=("Consolas", 12) ) error_label.pack(pady=20) + + print(f"❌ Error en plot: {e}") def _create_2d_plot(self, fig, ax, args, kwargs): """Crea plot 2D usando SymPy""" if len(args) >= 1: expr = args[0] - if len(args) >= 2: - # Rango especificado: (variable, start, end) - var_range = args[1] - if isinstance(var_range, tuple) and len(var_range) == 3: - var, start, end = var_range - x_vals = np.linspace(float(start), float(end), 1000) - - # Evaluar expresión - f = sympy.lambdify(var, expr, 'numpy') - y_vals = f(x_vals) - - ax.plot(x_vals, y_vals, **kwargs) - ax.set_xlabel(str(var)) - ax.set_ylabel(str(expr)) - ax.grid(True) - ax.set_title(f"Plot: {expr}") - else: - # Rango por defecto - free_symbols = list(expr.free_symbols) - if free_symbols: - var = free_symbols[0] - x_vals = np.linspace(-10, 10, 1000) - f = sympy.lambdify(var, expr, 'numpy') - y_vals = f(x_vals) - - ax.plot(x_vals, y_vals, **kwargs) - ax.set_xlabel(str(var)) - ax.set_ylabel(str(expr)) - ax.grid(True) - ax.set_title(f"Plot: {expr}") + try: + if len(args) >= 2: + # Rango especificado: (variable, start, end) + var_range = args[1] + if isinstance(var_range, tuple) and len(var_range) == 3: + var, start, end = var_range + x_vals = np.linspace(float(start), float(end), 1000) + + # Evaluar expresión + f = sympy.lambdify(var, expr, 'numpy') + y_vals = f(x_vals) + + ax.plot(x_vals, y_vals, **kwargs) + ax.set_xlabel(str(var)) + ax.set_ylabel(str(expr)) + ax.grid(True) + ax.set_title(f"Plot: {expr}") + else: + # Rango por defecto + free_symbols = list(expr.free_symbols) + if free_symbols: + var = free_symbols[0] + x_vals = np.linspace(-10, 10, 1000) + f = sympy.lambdify(var, expr, 'numpy') + y_vals = f(x_vals) + + ax.plot(x_vals, y_vals, **kwargs) + ax.set_xlabel(str(var)) + ax.set_ylabel(str(expr)) + ax.grid(True) + ax.set_title(f"Plot: {expr}") + + except Exception as e: + ax.text(0.5, 0.5, f"Error: {e}", + transform=ax.transAxes, ha='center', va='center') + ax.set_title("Error en Plot") def _create_3d_plot(self, fig, args, kwargs): """Crea plot 3D""" - ax = fig.add_subplot(111, projection='3d') - - if len(args) >= 3: - expr = args[0] - x_range = args[1] # (x, x_start, x_end) - y_range = args[2] # (y, y_start, y_end) + try: + ax = fig.add_subplot(111, projection='3d') - if isinstance(x_range, tuple) and isinstance(y_range, tuple): - x_var, x_start, x_end = x_range - y_var, y_start, y_end = y_range + if len(args) >= 3: + expr = args[0] + x_range = args[1] # (x, x_start, x_end) + y_range = args[2] # (y, y_start, y_end) - x_vals = np.linspace(float(x_start), float(x_end), 50) - y_vals = np.linspace(float(y_start), float(y_end), 50) - X, Y = np.meshgrid(x_vals, y_vals) - - f = sympy.lambdify([x_var, y_var], expr, 'numpy') - Z = f(X, Y) - - ax.plot_surface(X, Y, Z, **kwargs) - ax.set_xlabel(str(x_var)) - ax.set_ylabel(str(y_var)) - ax.set_zlabel(str(expr)) - ax.set_title(f"3D Plot: {expr}") + if isinstance(x_range, tuple) and isinstance(y_range, tuple): + x_var, x_start, x_end = x_range + y_var, y_start, y_end = y_range + + x_vals = np.linspace(float(x_start), float(x_end), 50) + y_vals = np.linspace(float(y_start), float(y_end), 50) + X, Y = np.meshgrid(x_vals, y_vals) + + f = sympy.lambdify([x_var, y_var], expr, 'numpy') + Z = f(X, Y) + + ax.plot_surface(X, Y, Z, **kwargs) + ax.set_xlabel(str(x_var)) + ax.set_ylabel(str(y_var)) + ax.set_zlabel(str(expr)) + ax.set_title(f"3D Plot: {expr}") + + except Exception as e: + ax.text2D(0.5, 0.5, f"Error: {e}", transform=ax.transAxes) def _show_matrix_window(self, matrix: sympy.Matrix, window_key: str): """Muestra ventana con matriz formateada""" @@ -224,24 +273,30 @@ class InteractiveResultManager: button_frame = tk.Frame(window, bg="#2b2b2b") button_frame.pack(fill=tk.X, padx=10, pady=5) - det_btn = tk.Button( - button_frame, - text="Determinante", - command=lambda: self._show_matrix_property(matrix, "determinante", matrix.det()), - bg="#3c3c3c", - fg="white" - ) - det_btn.pack(side=tk.LEFT, padx=5) - - if matrix.is_square: - inv_btn = tk.Button( + try: + det_btn = tk.Button( button_frame, - text="Inversa", - command=lambda: self._show_matrix_property(matrix, "inversa", matrix.inv()), + text="Determinante", + command=lambda: self._show_matrix_property(matrix, "determinante", matrix.det()), bg="#3c3c3c", fg="white" ) - inv_btn.pack(side=tk.LEFT, padx=5) + det_btn.pack(side=tk.LEFT, padx=5) + except: + pass # Skip si la matriz no es cuadrada + + if matrix.is_square: + try: + inv_btn = tk.Button( + button_frame, + text="Inversa", + command=lambda: self._show_matrix_property(matrix, "inversa", matrix.inv()), + bg="#3c3c3c", + fg="white" + ) + inv_btn.pack(side=tk.LEFT, padx=5) + except: + pass # Skip si no es invertible def _format_matrix(self, matrix: sympy.Matrix) -> str: """Formatea una matriz para display""" @@ -254,6 +309,8 @@ class InteractiveResultManager: element_str = str(matrix[i, j]) max_width = max(max_width, len(element_str)) + max_width = max(max_width, 8) # Mínimo 8 caracteres + # Construir representación lines = [] lines.append("┌" + " " * (max_width * cols + cols - 1) + "┐") @@ -385,54 +442,65 @@ class InteractiveResultManager: def close_all_windows(self): """Cierra todas las ventanas interactivas""" - for window in self.open_windows.values(): - if window.winfo_exists(): - window.destroy() + for window in list(self.open_windows.values()): + try: + if window.winfo_exists(): + window.destroy() + except tk.TclError: + pass self.open_windows.clear() -# Importar PlotResult desde el motor de evaluación -class PlotResult: - """Placeholder para resultados de plotting""" - - def __init__(self, plot_type: str, args: tuple, kwargs: dict): - self.plot_type = plot_type - self.args = args - self.kwargs = kwargs - - def __str__(self): - return f"📊 Ver {self.plot_type.title()}" - - def __repr__(self): - return f"PlotResult('{self.plot_type}', {self.args}, {self.kwargs})" - - # Función de testing def test_interactive_results(): """Test del sistema de resultados interactivos""" + print("🧪 Test Interactive Results - Versión Corregida") + print("=" * 50) + root = tk.Tk() root.title("Test Interactive Results") manager = InteractiveResultManager(root) # Crear widget de texto de prueba - text_widget = tk.Text(root, height=20, width=80) + text_widget = tk.Text(root, height=20, width=80, bg="#1e1e1e", fg="#d4d4d4") text_widget.pack(padx=10, pady=10) + # Test con PlotResult + print("📊 Testing PlotResult...") + plot_result = PlotResult("plot", (sympy.sin(sympy.Symbol('x')), (sympy.Symbol('x'), -10, 10)), {}) + tag_info = manager.create_interactive_tag(plot_result, text_widget, "1.0") + if tag_info: + tag, display = tag_info + text_widget.insert("end", f"Plot test: {display}\n", tag) + print(f" ✅ PlotResult tag creado: {display}") + else: + print(f" ❌ PlotResult tag NO creado") + # Test con matriz + print("📋 Testing Matrix...") matrix = sympy.Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) - tag, display = manager.create_interactive_tag(matrix, text_widget, "1.0") - text_widget.insert("end", f"Matriz test: {display}\n", tag) + tag_info = manager.create_interactive_tag(matrix, text_widget, "2.0") + if tag_info: + tag, display = tag_info + text_widget.insert("end", f"Matrix test: {display}\n", tag) + print(f" ✅ Matrix tag creado: {display}") + else: + print(f" ❌ Matrix tag NO creado") # Test con lista + print("📋 Testing List...") long_list = list(range(20)) - tag, display = manager.create_interactive_tag(long_list, text_widget, "2.0") - text_widget.insert("end", f"Lista test: {display}\n", tag) + tag_info = manager.create_interactive_tag(long_list, text_widget, "3.0") + if tag_info: + tag, display = tag_info + text_widget.insert("end", f"List test: {display}\n", tag) + print(f" ✅ List tag creado: {display}") + else: + print(f" ❌ List tag NO creado") - # Test con plot - plot_result = PlotResult("plot", (sympy.sin(sympy.Symbol('x')), (sympy.Symbol('x'), -10, 10)), {}) - tag, display = manager.create_interactive_tag(plot_result, text_widget, "3.0") - text_widget.insert("end", f"Plot test: {display}\n", tag) + print("\n✅ Test completado. Ventana interactiva abierta.") + print("🔍 Haz click en los elementos para probar funcionalidad.") root.mainloop() diff --git a/plots_interactive_readme.md b/plots_interactive_readme.md new file mode 100644 index 0000000..7acf119 --- /dev/null +++ b/plots_interactive_readme.md @@ -0,0 +1,147 @@ +# Plots Interactivos - Correcciones Implementadas + +## 🎯 **Estado Actual** + +✅ **Operaciones aritméticas corregidas**: `Hex[FF] + 1`, `Bin[1010] * 2` +✅ **Asignaciones funcionando**: `z = 5`, `w = z + 3` +🔧 **Plots interactivos**: En proceso de corrección + +## 🔧 **Correcciones Implementadas para Plots** + +### **1. Reorganización de PlotResult** +- ✅ Movido `PlotResult` a `interactive_results.py` para evitar importaciones circulares +- ✅ Importación correcta en motor de evaluación y aplicación principal + +### **2. Detección de Resultados Interactivos** +- ✅ Método `is_interactive` corregido en `EvaluationResult` +- ✅ Detección de `PlotResult`, `Matrix`, y listas largas + +### **3. Manejo de Tags Clickeables** +- ✅ Método `create_interactive_tag()` retorna tupla `(tag, display_text)` +- ✅ Manejo robusto de casos donde no se puede crear tag + +## 🧪 **Scripts de Verificación** + +### **Test Operaciones Básicas** (YA FUNCIONA ✅) +```bash +python debug_and_test.py +``` +**Resultados esperados:** +``` +✅ Hex[FF] + 1 → 256 +✅ Bin[1010] * 2 → 20 +✅ IP4[...].NetworkAddress[] → 192.168.1.0/24 +✅ z = 5 → z = 5 +✅ w = z + 3 → w = 8 +``` + +### **Test Plots Interactivos** (NUEVO) +```bash +python test_interactive_plots.py +``` +**Verificará:** +- Creación de objetos `PlotResult` +- Evaluación de expresiones `plot()` +- Funcionalidad del `InteractiveResultManager` +- Flujo completo: evaluación → tag clickeable + +### **Test Ejemplos Completos** (NUEVO) +```bash +python final_examples_test.py +``` +**Ejecutará todos los ejemplos originales** que antes fallaban + +## 🎯 **Lo que Debería Funcionar Ahora** + +| Expresión | Estado | Resultado Esperado | +|-----------|--------|--------------------| +| `Hex[FF] + 1` | ✅ FUNCIONA | `256` | +| `Bin[1010] * 2` | ✅ FUNCIONA | `20` | +| `IP4[...].NetworkAddress[]` | ✅ FUNCIONA | `192.168.1.0/24` | +| `z = 5` | ✅ FUNCIONA | `z = 5` | +| `w = z + 3` | ✅ FUNCIONA | `w = 8` | +| `plot(sin(x), (x, -pi, pi))` | 🔧 EN TEST | `📊 Ver Plot` (clickeable) | +| `Matrix([[1, 2], [3, 4]])` | 🔧 EN TEST | `📋 Ver Matriz 2×2` (clickeable) | + +## 🚀 **Próximos Pasos** + +### **1. Verificar Plots** +```bash +# Ejecutar test específico +python test_interactive_plots.py + +# Si hay errores, revisar logs +python log_viewer.py +``` + +### **2. Test en Aplicación Completa** +```bash +# Iniciar aplicación +python launcher.py + +# Probar en interfaz: +plot(sin(x), (x, -2*pi, 2*pi)) +Matrix([[1, 2], [3, 4]]) +``` + +### **3. Verificar Funcionalidad Clickeable** +- Los resultados de `plot()` deberían mostrar `📊 Ver Plot` +- Al hacer click debería abrir ventana matplotlib +- Las matrices deberían mostrar `📋 Ver Matriz NxM` +- Al hacer click debería mostrar matriz formateada + +## 🔍 **Debugging si Plots No Funcionan** + +### **Verificar Creación de PlotResult** +```python +from interactive_results import PlotResult +plot_obj = PlotResult("plot", (sin(x), (x, -pi, pi)), {}) +print(plot_obj) # Debería mostrar: 📊 Ver Plot +``` + +### **Verificar Evaluación** +```python +from hybrid_evaluation_engine import HybridEvaluationEngine +engine = HybridEvaluationEngine() +result = engine.evaluate_line("plot(sin(x), (x, -pi, pi))") +print(f"Resultado: {result.result}") +print(f"Tipo: {type(result.result)}") +print(f"Es interactivo: {result.is_interactive}") +``` + +### **Verificar Manager** +```python +import tkinter as tk +from interactive_results import InteractiveResultManager +root = tk.Tk() +manager = InteractiveResultManager(root) +# Test con plot_obj... +``` + +## 📊 **Logs para Debugging** + +Todos los errores se registran automáticamente en: +``` +logs/mav_calc_YYYYMMDD_HHMMSS.log +``` + +Para ver logs en tiempo real: +```bash +tail -f logs/mav_calc_*.log | grep -A 5 "plot\|Plot\|interactive" +``` + +## ✅ **Checklist de Verificación** + +- [ ] `python debug_and_test.py` pasa todos los tests ✅ (YA FUNCIONA) +- [ ] `python test_interactive_plots.py` pasa todos los tests +- [ ] `python final_examples_test.py` muestra 100% éxito +- [ ] `plot(sin(x), (x, -pi, pi))` retorna `📊 Ver Plot` +- [ ] `Matrix([[1, 2], [3, 4]])` retorna `📋 Ver Matriz 2×2` +- [ ] La aplicación inicia sin errores: `python launcher.py` +- [ ] Los elementos clickeables funcionan en la interfaz + +--- + +**Estado**: ✅ Operaciones básicas corregidas, 🔧 plots interactivos en verificación + +**Siguiente paso**: Ejecutar `python test_interactive_plots.py` y reportar resultados \ No newline at end of file diff --git a/test_interactive_plots.py b/test_interactive_plots.py new file mode 100644 index 0000000..74f637c --- /dev/null +++ b/test_interactive_plots.py @@ -0,0 +1,177 @@ +#!/usr/bin/env python3 +""" +Test específico para plots interactivos y resultados clickeables +""" +import sys +from pathlib import Path + +# Agregar directorio actual al path +sys.path.insert(0, str(Path(__file__).parent)) + +from hybrid_evaluation_engine import HybridEvaluationEngine +from interactive_results import PlotResult, InteractiveResultManager +import sympy +import tkinter as tk + + +def test_plot_creation(): + """Test de creación de objetos PlotResult""" + print("=== Test Creación de Plots ===") + + try: + # Test creación básica de PlotResult + plot_obj = PlotResult("plot", (sympy.sin(sympy.Symbol('x')), (sympy.Symbol('x'), -10, 10)), {}) + print(f"✅ PlotResult creado: {plot_obj}") + print(f" Tipo: {type(plot_obj)}") + print(f" String: {str(plot_obj)}") + print(f" Plot type: {plot_obj.plot_type}") + + # Test detección como interactivo + from hybrid_evaluation_engine import EvaluationResult + result = EvaluationResult(plot_obj, "expression") + print(f"✅ is_interactive: {result.is_interactive}") + + except Exception as e: + print(f"❌ Error en creación de plots: {e}") + import traceback + traceback.print_exc() + + +def test_plot_evaluation(): + """Test de evaluación de expresiones plot""" + print("\n=== Test Evaluación de Plots ===") + + engine = HybridEvaluationEngine() + + plot_expressions = [ + "plot(sin(x), (x, -2*pi, 2*pi))", + "plot(x**2, (x, -5, 5))", + "plot(cos(x), (x, 0, 2*pi))", + "Matrix([[1, 2], [3, 4]])", # También debería ser interactivo + ] + + for expr in plot_expressions: + try: + print(f"\nEvaluando: '{expr}'") + result = engine.evaluate_line(expr) + + if result.is_error: + print(f" ❌ Error: {result.error}") + else: + print(f" ✅ Resultado: {result.result}") + print(f" Tipo: {type(result.result)}") + print(f" Es interactivo: {result.is_interactive}") + + # Verificar si es PlotResult + if isinstance(result.result, PlotResult): + print(f" ✅ Es PlotResult!") + print(f" Plot type: {result.result.plot_type}") + print(f" Args: {result.result.args}") + + except Exception as e: + print(f" ❌ Excepción: {e}") + import traceback + traceback.print_exc() + + +def test_interactive_manager(): + """Test del manager de resultados interactivos""" + print("\n=== Test Interactive Manager ===") + + # Necesitamos una ventana tk para el manager + try: + root = tk.Tk() + root.withdraw() # Ocultar ventana + + manager = InteractiveResultManager(root) + + # Crear widget de texto simulado + text_widget = tk.Text(root) + + # Test con PlotResult + plot_obj = PlotResult("plot", (sympy.sin(sympy.Symbol('x')), (sympy.Symbol('x'), -10, 10)), {}) + + interactive_info = manager.create_interactive_tag(plot_obj, text_widget, "1.0") + + if interactive_info and len(interactive_info) == 2: + tag, display_text = interactive_info + print(f"✅ Tag interactivo creado: '{tag}'") + print(f" Display text: '{display_text}'") + else: + print(f"❌ No se creó tag interactivo: {interactive_info}") + + # Test con Matrix + matrix = sympy.Matrix([[1, 2], [3, 4]]) + interactive_info2 = manager.create_interactive_tag(matrix, text_widget, "2.0") + + if interactive_info2 and len(interactive_info2) == 2: + tag2, display_text2 = interactive_info2 + print(f"✅ Tag para matriz creado: '{tag2}'") + print(f" Display text: '{display_text2}'") + else: + print(f"❌ No se creó tag para matriz: {interactive_info2}") + + root.destroy() + + except Exception as e: + print(f"❌ Error en test de manager: {e}") + import traceback + traceback.print_exc() + + +def test_full_workflow(): + """Test del flujo completo: evaluación → resultado interactivo""" + print("\n=== Test Flujo Completo ===") + + try: + root = tk.Tk() + root.withdraw() + + # Simular el flujo de la aplicación + engine = HybridEvaluationEngine() + manager = InteractiveResultManager(root) + text_widget = tk.Text(root) + + # Evaluar plot + result = engine.evaluate_line("plot(sin(x), (x, -pi, pi))") + + print(f"Resultado evaluación: {result.result} (tipo: {type(result.result)})") + print(f"Es interactivo: {result.is_interactive}") + + if result.is_interactive: + interactive_info = manager.create_interactive_tag( + result.result, text_widget, "1.0" + ) + + if interactive_info and len(interactive_info) == 2: + tag, display_text = interactive_info + print(f"✅ ÉXITO: Tag '{tag}' con texto '{display_text}'") + else: + print(f"❌ FALLO: No se creó tag interactivo") + else: + print("❌ FALLO: Resultado no marcado como interactivo") + + root.destroy() + + except Exception as e: + print(f"❌ Error en flujo completo: {e}") + import traceback + traceback.print_exc() + + +def main(): + """Función principal de testing""" + print("Test de Plots Interactivos - Calculadora MAV") + print("=" * 50) + + test_plot_creation() + test_plot_evaluation() + test_interactive_manager() + test_full_workflow() + + print("\n" + "=" * 50) + print("Testing completado.") + + +if __name__ == "__main__": + main()