diff --git a/MaVCalc.lnk b/MaVCalc.lnk new file mode 100644 index 0000000..244d20b Binary files /dev/null and b/MaVCalc.lnk differ diff --git a/hybrid_calc_settings.json b/hybrid_calc_settings.json index bb264fd..774a72f 100644 --- a/hybrid_calc_settings.json +++ b/hybrid_calc_settings.json @@ -1,4 +1,4 @@ { - "window_geometry": "1000x700+722+83", - "sash_pos_x": 341 + "window_geometry": "1000x700+175+111", + "sash_pos_x": 339 } \ No newline at end of file diff --git a/hybrid_evaluation_engine.py b/hybrid_evaluation_engine.py index 7597da7..8625118 100644 --- a/hybrid_evaluation_engine.py +++ b/hybrid_evaluation_engine.py @@ -9,6 +9,7 @@ import re from contextlib import contextmanager from bracket_parser import BracketParser +from interactive_results import PlotResult # 🔧 IMPORTACIÓN CORREGIDA from hybrid_base_types import ( HybridCalcType, HybridHex, HybridBin, HybridDec, HybridIP4, HybridChr, Hex, Bin, Dec, IP4, Chr @@ -111,10 +112,14 @@ class HybridEvaluationEngine: def _create_plot_placeholder(self, *args, **kwargs): """Crear placeholder para plots que será manejado por resultados interactivos""" + if self.debug: + print(f"🎯 Creando PlotResult con args: {args}, kwargs: {kwargs}") return PlotResult('plot', args, kwargs) def _create_plot3d_placeholder(self, *args, **kwargs): """Crear placeholder para plots 3D""" + if self.debug: + print(f"🎯 Creando PlotResult 3D con args: {args}, kwargs: {kwargs}") return PlotResult('plot3d', args, kwargs) def _help_function(self, obj=None): @@ -206,6 +211,11 @@ class HybridEvaluationEngine: # Evaluar en contexto SymPy result = self._eval_in_context(expression) + if self.debug: + print(f"🔍 Resultado evaluación: {result} (tipo: {type(result)})") + if isinstance(result, PlotResult): + print(f" 📊 Es PlotResult: plot_type={result.plot_type}") + # Actualizar last_result self.last_result = result @@ -304,6 +314,10 @@ class HybridEvaluationEngine: # Si el resultado es un objeto híbrido, integrarlo con SymPy si es necesario if isinstance(result, HybridCalcType): return result + elif isinstance(result, PlotResult): + if self.debug: + print(f" 📊 PlotResult detectado en eval: {result}") + return result elif hasattr(result, '__iter__') and not isinstance(result, str): # Si es una lista/tupla, verificar si contiene objetos híbridos return result @@ -458,6 +472,9 @@ class EvaluationResult: @property def is_interactive(self) -> 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 0000000..737fada Binary files /dev/null and b/icon.ico differ diff --git a/interactive_results.py b/interactive_results.py index 0c351f3..745cb15 100644 --- a/interactive_results.py +++ b/interactive_results.py @@ -1,15 +1,30 @@ """ -Sistema de resultados interactivos con tags clickeables +Sistema de resultados interactivos con tags clickeables - VERSIÓN CORREGIDA """ import tkinter as tk from tkinter import Toplevel, scrolledtext import sympy -from typing import Any, Optional, Dict, List +from typing import Any, Optional, Dict, List, Tuple import matplotlib.pyplot as plt from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg import numpy as np +class PlotResult: + """Placeholder para resultados de plotting - DEFINICIÓN PRINCIPAL""" + + 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})" + + class InteractiveResultManager: """Maneja resultados interactivos con ventanas emergentes""" @@ -17,16 +32,17 @@ class InteractiveResultManager: self.parent = parent_window self.open_windows: Dict[str, Toplevel] = {} - def create_interactive_tag(self, result: Any, text_widget: tk.Text, index: str) -> 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()