Mejorado de la documentacion

This commit is contained in:
Miguel 2025-06-02 09:43:39 +02:00
parent 1eea99fde2
commit 3aadae3dc6
13 changed files with 361 additions and 1044 deletions

358
.doc/refactoring_guide.md Normal file
View File

@ -0,0 +1,358 @@
# Guía de Desarrollo - Calculadora MAV CAS Híbrido
## Visión General
Este documento describe el estado actual y los objetivos de desarrollo para la **Calculadora MAV**, un **Sistema de Álgebra Computacional (CAS) híbrido**. El proyecto busca combinar la potencia de SymPy como motor algebraico central con capacidades especializadas (IP4, Hex, etc.) integradas de forma nativa.
## Objetivos Principales
### 1. **SymPy como Motor Central**
- Integrar todas las funciones de SymPy directamente
- SymPy maneja toda la evaluación algebraica y ecuaciones
- Cada linea de ingreso corresponde a una sola linea de resultados
- La interfaz de usuario se divide en 2 columnas, a la izquierda el area de ingreso de datos y equaciones a ser evaluadas, a la derecha el area de resultados que se colorean segun cada tipo de respuesta.
### 2. **Sintaxis Simplificada con Corchetes (Única)**
- Usar **exclusivamente** `Class[args]` en lugar de `Class("args")`
- Los corchetes indican parsing especial (agregar comillas automáticamente)
- Mantiene sintaxis limpia y reduce errores de tipeo
### 3. **Detección Automática y Manejo de Ecuaciones**
- Ecuaciones ingresadas directamente (ej. `3+b=5+c`) se detectan y añaden al sistema interno de SymPy.
- Soporte para comparaciones SymPy (ej. `x > 5`, `a == b`, `x <= 10`) como expresiones evaluables.
- Sintaxis de atajo para solve: `variable=?` equivale a `solve(variable)`
### 4. **Evaluación Híbrida con Variables SymPy Puras**
- Resultado simbólico (siempre disponible)
- Evaluación numérica con `.evalf()` (cuando sea aplicable y las variables estén definidas). Esta evaluacion se muestra a la derecha del resultado simbolico.
- Las asignaciones son siempre simbolicas, solo los resultados se muestra la evaluacion numerica.
- **Todas las variables son símbolos SymPy**: Sin variables Python tradicionales
- **Sin eval/exec por defecto**: Solo SymPy, a menos que se indique sintaxis especial
### 4. **Resultados Interactivos**
- **Tags clickeables** en el widget Text para resultados complejos que necesitan mas de una linea de resultado como Plot, Matrices y Listas.
- **Detección automática** de tipos que requieren visualización especial:
- Plots de SymPy → "📊 Ver Plot" (abre ventana matplotlib).
- Matrices → "📋 Ver Matriz" (muestra matriz formateada).
- Listas largas → "📋 Ver Lista" (muestra contenido expandido).
- Objetos complejos → "🔍 Ver Detalles" (representación completa).
- **Implementación simple**: Usar `text.tag_bind()` en widget existente
- **Ventanas popup** para contenido que no cabe en línea.
- **Actualización automática**: Las ventanas de plot, listas y matrices deben tener la capacidad de actualizarse automáticamente si los datos subyacentes cambian y la ventana permanece abierta.
- **Posicionamiento inteligente**:
- Cuando no hay otras ventanas de resultados (plot, lista, matriz) abiertas, la primera que se abra debe tener su altura igual a la de la ventana principal de la aplicación.
- Esta primera ventana se posicionará automáticamente a la derecha de la ventana principal de la aplicación.
### 5. **Clases Especializadas como Objetos SymPy**
- Todas las clases (IP4, Hex, Bin, etc.) heredan de `sympy.Basic`
- Participan en manipulación algebraica: `solve()`, `diff()`, `integrate()`
- Mantienen funcionalidad especializada (métodos `.NetworkAddress()`, `.toHex()`, etc.)
## Arquitectura Propuesta
### Estructura de archivos
- Subcarperta de log "/.log"
- Subcarpeta de documentos md de ayuda y documentacion tecnica "/.doc"
- Aplicacion principal launcher: main_calc.py
- Aplicacion principal app: main_calc_app.py
- Motor de evaluacion: main_evaluation.py
- Motor de ventanas emergentes: tl_popup.py
- Objetos especiales: [nombre objeto]_type.py . Ejemplo ip4_type.py
- Tools del motor: tl_[nombre tool].py . Ejemplo: tl_bracket_parser.py
### Flujo de Procesamiento
```
Entrada Usuario → [Bracket Parser] → [SymPy Evaluation] → {
1. Resultado Simbólico (siempre)
2. evalf() (si variables definidas)
3. Métodos especializados disponibles
}
```
### Transformación de Sintaxis
```python
# Clases con corchetes:
IP4[192.168.1.1/24].NetworkAddress[] → IP4("192.168.1.1/24").NetworkAddress()
# Detección contextual de ecuaciones:
3+b=5+c → Se detecta como ecuación standalone (agregar al sistema SymPy)
"x > 5" → Se procesa como desigualdad SymPy
x**2 + 2*x == 0 → Se detecta como igualdad lógica standalone
# NO se detectan como ecuaciones (parsing contextual):
solve(x + 2 = 5, x) → "=" es argumento de función, no ecuación
result = solve(a + b = 10, a) → "=" izquierdo es asignación Python
IP4[192.168.1.1].Mask() == "255.255.255.0" → Expresión de comparación
# Sintaxis de atajo para solve:
x=? → solve(x)
```
## Componentes Técnicos
### 1. **Bracket Parser y Detección Contextual de Ecuaciones**
- Preprocesador que convierte `Class[args]``Class("args")`
- Soporte para clases: `IP4`, `Hex`, `Bin`, `Date`, `Dec`, `Chr`
- **Detección contextual inteligente de ecuaciones**:
- Ecuaciones standalone: `3+b=5+c` (detectar como ecuación)
- Strings con ecuaciones: `"expresión = expresión"` (detectar como ecuación)
- **NO detectar** cuando `=`, `==`, `>`, etc. están dentro de llamadas a funciones: `solve(x=5, y)`
- **NO detectar** asignaciones Python: `result = solve(...)`
- Conversión de atajo: `variable=?``solve(variable)`
- Usa AST parsing para distinguir contexto sintáctico
- Extensible para futuras transformaciones más complejas
### 2. **Clases Híbridas**
```python
class IP4(BaseCalcType, sympy.Basic):
def __new__(cls, address, mask=None):
# Crear objeto SymPy válido
obj = sympy.Basic.__new__(cls)
# Inicializar funcionalidad IP4
obj._init_ip4(address, mask)
return obj
def _sympystr(self, printer):
return f"IP4({self.address})"
```
### 3. **Sistema de Evaluación y Detección Contextual**
```python
def detect_equation_context(code_line):
"""
Usa AST para determinar si una línea contiene una ecuación standalone
vs. comparaciones dentro de funciones o asignaciones Python
"""
tree = ast.parse(code_line)
# Casos que NO son ecuaciones:
# - Asignaciones: result = solve(...)
# - Argumentos de función: solve(x = 5, y)
# - Expresiones dentro de llamadas: func(a == b)
# Casos que SÍ son ecuaciones:
# - Comparaciones standalone: x + 2 == 5
# - Strings con ecuaciones: "a = b"
return is_standalone_equation
def evaluate_expression(expr):
# 1. Detección contextual de ecuaciones
if detect_equation_context(expr):
add_to_equation_system(expr)
return "Ecuación agregada al sistema"
# 2. Evaluación simbólica (SymPy)
symbolic_result = sympy.sympify(expr)
# 3. Intentar evaluación numérica
try:
if all_variables_defined(symbolic_result):
numeric_result = symbolic_result.evalf()
else:
numeric_result = None
except:
numeric_result = None
return {
'symbolic': symbolic_result,
'numeric': numeric_result
}
```
## Capacidades Expandidas
### 1. **Funciones SymPy Completas**
```python
# Todas disponibles directamente:
diff(IP4[192.168.x.1] + offset, x)
integrate(Hex[x] * sin(x), x)
solve([IP4[192.168.x.y] == IP4[192.168.1.10]], [x, y])
limit(Dec[x]/x, x, 0)
series(exp(Hex[x]), x, 0, 5)
```
### 2. **Plotting Integrado con Interface Interactiva**
```python
# SymPy plotting nativo:
plot(sin(Hex[x]), (x, 0, 255)) # Muestra: "📊 Ver Plot" (clickeable)
plot3d(IP4[192.168.x.y].to_decimal(), (x, 1, 254), (y, 1, 254))
# Resultados complejos interactivos:
Matrix([[1, 2], [3, 4]]) # Muestra: "📋 Ver Matriz" (clickeable)
solve(x**2 + 2*x + 1, x) # Lista: "📋 Ver Soluciones" (clickeable)
```
### 3. **Manipulación Algebraica de Objetos Especializados**
```python
# Objetos especializados en expresiones algebraicas:
network = IP4[192.168.x.0/24]
solve(network.NetworkAddress() == IP4[192.168.5.0/24], x)
# Resultado: x = 5
```
## Ejemplos de Uso Objetivo
```python
# Evaluación directa con SymPy:
solve(x + 10 - 15, x) # Resultado: [5]
# Detección automática de ecuaciones:
3+b=5+c # Se agrega automáticamente al sistema
x > 5 # Desigualdad procesada por SymPy
a == b # Igualdad lógica procesada por SymPy
# Sintaxis de atajo:
x=? # Equivale a solve(x)
# Solo sintaxis con corchetes:
IP4[192.168.1.1/24].NetworkAddress[] # Resultado: 192.168.1.0/24
# Variables puras SymPy:
x = 5 # x es Symbol('x') con valor 5, no variable Python
y = x + 2 # y es expresión simbólica: Symbol('x') + 2
# Álgebra con objetos especializados:
base_ip = IP4[10.0.x.1]
solve(base_ip + 255 == IP4[10.0.5.0], x) # Resultado: x = 4
# Funciones avanzadas con resultados interactivos:
diff(Hex[x**2], x) # Derivada: 2*x*log(16)
plot(sin(2*pi*Hex[x]/256), (x, 0, 255)) # Resultado: "📊 Ver Plot"
# Matrices y estructuras complejas:
Matrix([[1, 2, 3], [4, 5, 6]]) # Resultado: "📋 Ver Matriz"
solve([x + y == 10, x - y == 2], [x, y]) # Resultado: "📋 Ver Soluciones"
# eval/exec opcional con sintaxis especial:
@eval: import os; os.getcwd() # Solo cuando se necesite Python puro
```
## Beneficios Esperados
### 1. **Usabilidad**
- Sintaxis más limpia (sin comillas constantes)
- Acceso completo a capacidades de CAS
- Evaluación automática simbólica + numérica
### 2. **Potencia Matemática**
- Todas las funciones de SymPy disponibles
- Objetos especializados participan en álgebra
- Capacidades de plotting integradas
### 3. **Extensibilidad**
- Parser extensible para futuras sintaxis
- Fácil agregar nuevas clases especializadas
- Compatible con ecosystem SymPy completo
### 4. **Consistencia**
- Un solo motor de evaluación (SymPy)
- Reglas algebraicas consistentes
- Manejo unificado de errores
## Fases de Implementación
### Fase 1: Foundation
- Implementar bracket parser básico
- Convertir clases existentes a `sympy.Basic`
- Sistema de evaluación híbrida
### Fase 2: Integration
- Remover clase `solve` separada
- Integrar todas las funciones SymPy
- Testing de manipulación algebraica
### Fase 3: Enhancement
- Capacidades de plotting
- Sintaxis extendida del parser
- Optimizaciones de performance
### Fase 4: Polish
- Documentación completa
- Ejemplos avanzados
- UI/UX improvements
## Consideraciones Adicionales
### **Performance y Caching (Crítico)**
- **Caching de líneas precedentes**: Las líneas anteriores no se recalculan
- **Incremental evaluation**: Solo evaluar líneas nuevas/modificadas
- **Caching de parsing**: Bracket parsing se cachea para expresiones repetidas
- **Lazy evalf()**: Solo evaluar numéricamente cuando se solicite mostrar
- **Optimización SymPy**: Configurar para performance en contexto interactivo
### **Manejo Robusto de Errores y Logging**
- **Logging centralizado enfocado en errores**: El sistema de logging (`main_launcher.py`) debe registrar principalmente errores para facilitar la depuración, evitando logs informativos excesivos en producción. Los logs detallados (DEBUG) pueden activarse con un flag. Solo crear un archivo de log bajo la carpeta "/.logs"
- **Errores de SymPy y del Motor CAS**: Priorizar mensajes de error claros provenientes de SymPy o del motor de evaluación.
- **Errores contextuales**: Mensajes específicos para errores de parsing vs evaluación
- **Sugerencias automáticas**: "¿Quisiste decir...?" para funciones mal escritas
- **eval/exec explícito**: Solo disponible con sintaxis especial cuando sea necesario
### **Extensibilidad del Parser**
- **Arquitectura modular**: Fácil agregar nuevas transformaciones sintácticas
- **Plugin system**: Permitir extensiones de terceros
- **Configurabilidad**: Usuario puede habilitar/deshabilitar transformaciones
### **Sistema de Ayuda Avanzado**
- **Autocompletado SymPy**: Funciones y métodos disponibles según contexto o cuando se presiona el "."
- **Help integrado**: `help(diff)` muestra documentación SymPy completa
- **Ejemplos interactivos**: Templates para operaciones comunes
- **Cheat sheet**: Referencia rápida de sintaxis nueva vs antigua
### **Gestión de Variables Puras SymPy**
- **Solo símbolos SymPy**: Todas las variables son `Symbol()` automáticamente
- **Sin variables Python**: Eliminación de `eval()` como mecanismo por defecto
- **Evaluación consistente**: `x = 5` crea `Symbol('x')` con valor 5 en contexto
- **eval/exec opcional**: Solo disponible con sintaxis especial (ej: `@eval: código_python`)
- **Contexto unificado**: Un solo namespace SymPy para toda la sesión
## Métricas de Éxito
- [X] Sintaxis `Class[args]` funciona consistentemente (`bracket_parser.py`, `hybrid_base_types.py`).
- [+] **Detección contextual inteligente de ecuaciones (Motor CAS/Parser):**
- [+] Ecuaciones standalone: `3+b=5+c`, `x**2 == 0` se detectan y añaden al sistema (`bracket_parser.py`, `hybrid_evaluation_engine.py`). `x**2==0` se evalúa como expresión booleana, no se añade como ecuación al sistema automáticamente.
- [ ] _(Parser)_ Strings con ecuaciones: `"a=b"`, `"x>5"` se procesan como ecuaciones (actualmente el parser maneja `a=b` directo, no como string literal).
- [+] **NO detectar** comparaciones en argumentos: `solve(x=5, y)`, `func(a==b)` (AST en `bracket_parser.py` ayuda, `EquationDetector` es un prototipo).
- [X] **NO detectar** asignaciones Python: `result = solve(...)` (`bracket_parser.py` lo maneja).
- [X] Sintaxis de atajo `variable=?` funciona como `solve(variable)` (`bracket_parser.py`).
- [+] Todas las funciones SymPy disponibles en interfaz (`hybrid_evaluation_engine.py` tiene contexto base amplio; `hybrid_calc_app.py` expone algunas en menús).
- [+] Objetos especializados participan en `solve()`, `diff()`, etc. (`hybrid_base_types.py` heredan de `sympy.Basic`; la profundidad de integración en operaciones aritméticas necesita revisión para preservar el tipo).
- [X] Evaluación híbrida (simbólica + numérica) automática (`hybrid_evaluation_engine.py`).
- [+] Plotting funcional con objetos especializados (Infraestructura en `hybrid_evaluation_engine.py` y `interactive_results.py`; depende de la convertibilidad del objeto a datos ploteables).
- [+] **Resultados interactivos funcionando:**
- [X] Implementación de ventanas popup (Toplevel) para mostrar información detallada (ej. diálogo de error, plots, matrices, listas en `main_launcher.py`, `interactive_results.py`).
- [X] Tags clickeables en el widget Text para resultados complejos (`hybrid_calc_app.py`, `interactive_results.py`).
- [+] Detección automática de tipos para visualización especial:
- [X] Plots de SymPy → "📊 Ver Plot" (`interactive_results.py`).
- [X] Matrices → "📋 Ver Matriz" (`interactive_results.py`).
- [X] Listas largas → "📋 Ver Lista" (`interactive_results.py`).
- [ ] _(UI/Engine)_ Objetos complejos → "🔍 Ver Detalles" (Infraestructura parcial, no implementado genéricamente).
- [ ] _(UI)_ Ventanas de plot/lista/matriz con actualización automática si se mantienen abiertas.
- [+] _(UI)_ Posicionamiento y dimensionamiento inteligente de la primera ventana de plot/lista/matriz (`interactive_results.py` tiene base, necesita lógica de "solo si no hay otras abiertas" y ajuste de altura).
- [+] Performance aceptable para uso interactivo (Debounce en `hybrid_calc_app.py` ayuda).
- [+] **Sistema de ayuda avanzado:**
- [X] Ayuda básica por línea de comandos (`main_launcher.py`).
- [ ] _(UI)_ Autocompletado para funciones SymPy y métodos de objetos (`hybrid_calc_app.py` tiene TODO).
- [+] _(Engine/UI)_ `help()` integrado con documentación SymPy (`hybrid_evaluation_engine.py` tiene base, `hybrid_calc_app.py` tiene menús estáticos).
- [+] _(UI)_ Ejemplos y templates interactivos (`hybrid_calc_app.py` tiene `insert_example`).
- [X] **Manejo robusto de errores y logging (Launcher):**
- [X] Logging centralizado de errores (`main_launcher.py`, configurable para ser error-only).
- [X] Presentación de errores al usuario en ventana dedicada (`main_launcher.py`).
- [X] Verificación de dependencias y archivos de la aplicación al inicio (`main_launcher.py`).
- [X] Intento de instalación automática de dependencias (`main_launcher.py`).
- [X] Provisión de comandos para troubleshooting (`main_launcher.py`).
- [+] **Manejo robusto de errores (Motor CAS):**
- [+] Solo errores de SymPy, sin fallbacks automáticos a eval/exec (Motor (`hybrid_evaluation_engine.py`) usa `eval` y `sympify`; se busca minimizar `eval` directo de input de usuario).
- [+] Errores contextuales: mensajes específicos para errores de parsing vs. evaluación (`EvaluationResult` tiene campo de error).
- [ ] _(Engine)_ Sugerencias automáticas: "¿Quisiste decir...?" para funciones mal escritas.
- [ ] _(Engine/Parser)_ `eval/exec` explícito: solo disponible con sintaxis especial (ej. `@eval:`) (Planeado, no implementado).
- [ ] **Performance optimizada (Motor CAS):**
- [ ] Caching de expresiones repetidas
- [ ] Evaluación lazy de `evalf()`
- [ ] Tiempo de respuesta < 200ms para operaciones comunes
- [+] **Parser extensible** con arquitectura modular (`bracket_parser.py` es un buen inicio).
- [+] **Documentación y ejemplos completos:**
- [X] Docstrings en el código (`main_launcher.py`, `hybrid_evaluation_engine.py`, etc.).
- [X] Guía de Desarrollo (este documento).
- [X] Splash screen eliminado (`main_launcher.py`).
- [+] Documentación de usuario final completa (`comprehensive_documentation.md` es una base sólida).
- [+] Ejemplos de uso avanzados en la documentación (`comprehensive_documentation.md`).

Binary file not shown.

View File

@ -1,181 +0,0 @@
#!/usr/bin/env python3
"""
Script de debug y testing para verificar correcciones del error as_coeff_Mul
"""
import sys
from pathlib import Path
# Agregar directorio actual al path
sys.path.insert(0, str(Path(__file__).parent))
from bracket_parser import BracketParser
from hybrid_evaluation_engine import HybridEvaluationEngine
from hybrid_base_types import Hex, Bin, IP4, HybridCalcType
import sympy
def test_sympy_integration():
"""Test específico de integración con SymPy"""
print("=== Test Integración SymPy ===")
try:
# Test creación básica
h = Hex("FF")
print(f"✅ Hex[FF] creado: {h}")
print(f" Valor interno: {h._value}")
print(f" Es SymPy Basic: {isinstance(h, sympy.Basic)}")
print(f" Args: {h.args}")
print(f" Func: {h.func}")
# Test métodos requeridos por SymPy
print(f"✅ as_coeff_Mul(): {h.as_coeff_Mul()}")
print(f"✅ as_coeff_Add(): {h.as_coeff_Add()}")
print(f"✅ as_base_exp(): {h.as_base_exp()}")
# Test propiedades
print(f"✅ is_number: {h.is_number}")
print(f"✅ is_real: {h.is_real}")
print(f"✅ is_integer: {h.is_integer}")
except Exception as e:
print(f"❌ Error en test básico: {e}")
import traceback
traceback.print_exc()
def test_arithmetic_operations():
"""Test específico de operaciones aritméticas"""
print("\n=== Test Operaciones Aritméticas ===")
try:
h = Hex("FF")
print(f"Hex[FF] creado: {h} (valor: {h._value})")
# Test suma simple
print("\n1. Probando h + 1...")
result1 = h + 1
print(f" Resultado: {result1} (tipo: {type(result1)})")
# Test con SymPy sympify
print("\n2. Probando sympify(h + 1)...")
try:
expr = sympy.sympify("h + 1", locals={'h': h})
print(f" SymPy expr: {expr} (tipo: {type(expr)})")
except Exception as e:
print(f" Error en sympify: {e}")
# Test multiplicación
print("\n3. Probando h * 2...")
result3 = h * 2
print(f" Resultado: {result3} (tipo: {type(result3)})")
# Test con Bin
print("\n4. Probando Bin[1010] * 2...")
b = Bin("1010")
result4 = b * 2
print(f" Bin[1010]: {b} (valor: {b._value})")
print(f" Resultado: {result4} (tipo: {type(result4)})")
except Exception as e:
print(f"❌ Error en operaciones aritméticas: {e}")
import traceback
traceback.print_exc()
def test_evaluation_engine():
"""Test del motor de evaluación con las correcciones"""
print("\n=== Test Motor de Evaluación ===")
engine = HybridEvaluationEngine()
test_expressions = [
# Casos que fallaban antes
"Hex[FF] + 1",
"Bin[1010] * 2",
"IP4[192.168.1.100/24].NetworkAddress[]",
# Casos básicos
"Hex[FF]",
"Bin[1010]",
# Asignaciones
"z = 5",
"w = z + 3",
# SymPy básico
"x + 2*y",
"diff(x**2, x)",
]
for expr in test_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} (tipo: {type(result.result)})")
print(f" Info: {result.result_type}")
if result.numeric_result:
print(f" Numérico: {result.numeric_result}")
except Exception as e:
print(f" ❌ Excepción: {e}")
import traceback
traceback.print_exc()
def test_problematic_cases():
"""Test de casos específicos que causaban problemas"""
print("\n=== Test Casos Problemáticos ===")
# Test directo de la operación que fallaba
print("1. Test directo Hex + int...")
try:
h = Hex("FF")
result = h.__add__(1)
print(f" h.__add__(1): {result} (tipo: {type(result)})")
# Test usando operador
result2 = h + 1
print(f" h + 1: {result2} (tipo: {type(result2)})")
except Exception as e:
print(f" ❌ Error: {e}")
import traceback
traceback.print_exc()
# Test con SymPy sympify directo
print("\n2. Test SymPy sympify directo...")
try:
h = Hex("FF")
sympified = sympy.sympify(h)
print(f" sympify(Hex[FF]): {sympified} (tipo: {type(sympified)})")
# Test operaciones con sympified
result = sympified + 1
print(f" sympified + 1: {result} (tipo: {type(result)})")
except Exception as e:
print(f" ❌ Error: {e}")
import traceback
traceback.print_exc()
def main():
"""Función principal de testing"""
print("Debug y Testing - Corrección error as_coeff_Mul")
print("=" * 60)
test_sympy_integration()
test_arithmetic_operations()
test_evaluation_engine()
test_problematic_cases()
print("\n" + "=" * 60)
print("Testing completado.")
if __name__ == "__main__":
main()

View File

@ -1,30 +1,2 @@
# Calculadora MAV - CAS Híbrido
# Sintaxis nueva con corchetes
# Tipos especializados
Hex[FF] + 1
IP4[192.168.1.100/24].NetworkAddress[]
Bin[1010] * 2
# Matemáticas simbólicas
x + 2*y
diff(x**2 + sin(x), x)
integrate(x**2, x)
# Ecuaciones (detección automática)
x**2 + 2*x - 8 = 0
3*a + b = 10
# Resolver ecuaciones
solve(x**2 + 2*x - 8, x)
a=?
# Variables automáticas
z = 5
w = z**2 + 3
# Plotting interactivo
plot(sin(x), (x, -2*pi, 2*pi))
# Matrices
Matrix([[1, 2], [3, 4]])
Hex[FF]
ip=IP4[192.168.1.100/24]

View File

@ -1,4 +1,4 @@
{
"window_geometry": "1000x700+127+123",
"window_geometry": "1000x700+327+101",
"sash_pos_x": 339
}

View File

@ -1,245 +0,0 @@
#!/usr/bin/env python3
"""
Script de setup e instalación para Calculadora MAV - CAS Híbrido
"""
import sys
import subprocess
import os
from pathlib import Path
import importlib.util
def check_python_version():
"""Verifica que la versión de Python sea compatible"""
if sys.version_info < (3, 8):
print("❌ Error: Se requiere Python 3.8 o superior")
print(f" Versión actual: {sys.version}")
return False
print(f"✅ Python {sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}")
return True
def check_module(module_name, package_name=None, optional=False):
"""Verifica si un módulo está disponible"""
try:
importlib.import_module(module_name)
print(f"{module_name}")
return True
except ImportError:
if optional:
print(f"⚠️ {module_name} (opcional)")
else:
print(f"{module_name} - {'usar: pip install ' + (package_name or module_name)}")
return False
def install_requirements():
"""Instala las dependencias requeridas"""
print("\n=== Instalando dependencias ===")
requirements = [
"sympy>=1.12",
"matplotlib>=3.7.0",
"numpy>=1.24.0"
]
for req in requirements:
try:
print(f"Instalando {req}...")
subprocess.check_call([
sys.executable, "-m", "pip", "install", req
], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
print(f"{req}")
except subprocess.CalledProcessError:
print(f"❌ Error instalando {req}")
return False
return True
def check_tkinter():
"""Verifica que tkinter esté disponible"""
try:
import tkinter
print("✅ tkinter")
return True
except ImportError:
print("❌ tkinter - instalar python3-tk en Linux/Ubuntu")
print(" Ubuntu/Debian: sudo apt-get install python3-tk")
print(" CentOS/RHEL: sudo yum install tkinter")
print(" macOS: tkinter debe estar incluido con Python")
return False
def check_optional_modules():
"""Verifica módulos opcionales"""
optional_modules = [
("markdown", "markdown", True),
("tkinterweb", "tkinterweb", True),
("tkhtmlview", "tkhtmlview", True),
("pytest", "pytest", True)
]
for module_name, package_name, optional in optional_modules:
check_module(module_name, package_name, optional)
def create_desktop_shortcut():
"""Crea acceso directo en el escritorio (Linux/Windows)"""
current_dir = Path.cwd()
main_script = current_dir / "hybrid_calc_app.py"
if not main_script.exists():
print("⚠️ No se encontró hybrid_calc_app.py en el directorio actual")
return False
try:
if sys.platform.startswith('linux'):
# Linux desktop entry
desktop_dir = Path.home() / "Desktop"
if desktop_dir.exists():
shortcut_path = desktop_dir / "CalculadoraMAV.desktop"
shortcut_content = f"""[Desktop Entry]
Version=1.0
Type=Application
Name=Calculadora MAV - CAS Híbrido
Comment=Sistema de álgebra computacional híbrido
Exec={sys.executable} "{main_script}"
Icon=calculator
Terminal=false
Categories=Education;Science;Math;
"""
with open(shortcut_path, 'w') as f:
f.write(shortcut_content)
# Hacer ejecutable
os.chmod(shortcut_path, 0o755)
print(f"✅ Acceso directo creado: {shortcut_path}")
return True
elif sys.platform == 'win32':
# Windows shortcut (requiere pywin32)
try:
import win32com.client
desktop = Path.home() / "Desktop"
shortcut_path = desktop / "Calculadora MAV.lnk"
shell = win32com.client.Dispatch("WScript.Shell")
shortcut = shell.CreateShortCut(str(shortcut_path))
shortcut.Targetpath = sys.executable
shortcut.Arguments = f'"{main_script}"'
shortcut.WorkingDirectory = str(current_dir)
shortcut.IconLocation = sys.executable
shortcut.save()
print(f"✅ Acceso directo creado: {shortcut_path}")
return True
except ImportError:
print("⚠️ Para crear acceso directo en Windows instalar: pip install pywin32")
except Exception as e:
print(f"⚠️ Error creando acceso directo: {e}")
return False
def run_tests():
"""Ejecuta tests básicos"""
print("\n=== Ejecutando tests básicos ===")
try:
# Test de importación
from bracket_parser import BracketParser
from hybrid_base_types import Hex, Bin, IP4
from hybrid_evaluation_engine import HybridEvaluationEngine
print("✅ Importaciones básicas")
# Test de parser
parser = BracketParser()
result, info = parser.parse_line("Hex[FF]")
assert result == 'Hex("FF")', f"Parser test failed: {result}"
print("✅ Bracket parser")
# Test de clases híbridas
h = Hex("FF")
assert str(h) == "0xFF", f"Hex test failed: {h}"
print("✅ Clases híbridas")
# Test de motor de evaluación
engine = HybridEvaluationEngine()
result = engine.evaluate_line("2 + 3")
assert result.result == 5, f"Engine test failed: {result.result}"
print("✅ Motor de evaluación")
print("✅ Todos los tests básicos pasaron")
return True
except Exception as e:
print(f"❌ Error en tests: {e}")
return False
def main():
"""Función principal de setup"""
print("=== Calculadora MAV - CAS Híbrido - Setup ===\n")
# Verificar Python
if not check_python_version():
sys.exit(1)
print("\n=== Verificando dependencias ===")
# Verificar tkinter
if not check_tkinter():
print("\n❌ tkinter es requerido para la interfaz gráfica")
sys.exit(1)
# Verificar dependencias principales
deps_ok = True
deps_ok &= check_module("sympy")
deps_ok &= check_module("matplotlib")
deps_ok &= check_module("numpy")
# Si faltan dependencias, intentar instalar
if not deps_ok:
print("\n=== Faltan dependencias requeridas ===")
response = input("¿Instalar automáticamente? (s/n): ").lower().strip()
if response in ['s', 'si', 'y', 'yes']:
if not install_requirements():
print("❌ Error instalando dependencias")
sys.exit(1)
else:
print("❌ Instale las dependencias manualmente:")
print(" pip install sympy matplotlib numpy")
sys.exit(1)
# Verificar módulos opcionales
print("\n=== Módulos opcionales ===")
check_optional_modules()
# Ejecutar tests
if not run_tests():
print("❌ Tests fallaron")
sys.exit(1)
# Crear acceso directo
print("\n=== Configuración adicional ===")
response = input("¿Crear acceso directo en el escritorio? (s/n): ").lower().strip()
if response in ['s', 'si', 'y', 'yes']:
create_desktop_shortcut()
print("\n✅ ¡Setup completado exitosamente!")
print("\nPara ejecutar la calculadora:")
print(f" python {Path.cwd() / 'hybrid_calc_app.py'}")
print("\nO usa el acceso directo si lo creaste.")
if __name__ == "__main__":
main()

View File

@ -1,177 +0,0 @@
#!/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()

View File

@ -1,410 +0,0 @@
#!/usr/bin/env python3
"""
Suite de tests unitarios para Calculadora MAV - CAS Híbrido
"""
import unittest
import sys
import os
from pathlib import Path
# Agregar directorio actual al path para importar módulos
sys.path.insert(0, str(Path(__file__).parent))
import sympy
from bracket_parser import BracketParser, EquationDetector
from hybrid_base_types import (
HybridHex, HybridBin, HybridDec, HybridIP4, HybridChr,
Hex, Bin, Dec, IP4, Chr
)
from hybrid_evaluation_engine import HybridEvaluationEngine, EvaluationResult
class TestBracketParser(unittest.TestCase):
"""Tests para el bracket parser"""
def setUp(self):
self.parser = BracketParser()
def test_bracket_transformation(self):
"""Test transformación de sintaxis con corchetes"""
test_cases = [
("Hex[FF]", 'Hex("FF")'),
("IP4[192.168.1.1/24]", 'IP4("192.168.1.1/24")'),
("Bin[1010]", 'Bin("1010")'),
("Dec[10.5]", 'Dec("10.5")'),
("Chr[A]", 'Chr("A")'),
]
for input_expr, expected in test_cases:
with self.subTest(input_expr=input_expr):
result, info = self.parser.parse_line(input_expr)
self.assertEqual(result, expected)
self.assertEqual(info, "bracket_transform")
def test_solve_shortcut(self):
"""Test transformación de atajo solve"""
test_cases = [
("x=?", "solve(x)"),
("variable_name=?", "solve(variable_name)"),
("a=?", "solve(a)"),
]
for input_expr, expected in test_cases:
with self.subTest(input_expr=input_expr):
result, info = self.parser.parse_line(input_expr)
self.assertEqual(result, expected)
self.assertEqual(info, "solve_shortcut")
def test_equation_detection(self):
"""Test detección de ecuaciones standalone"""
equation_cases = [
"x + 2 = 5",
"3*a + b = 10",
"x**2 == 4",
"y > 5",
]
for equation in equation_cases:
with self.subTest(equation=equation):
result, info = self.parser.parse_line(equation)
self.assertEqual(info, "equation")
self.assertTrue(result.startswith('_add_equation('))
def test_non_equation_cases(self):
"""Test casos que NO deben detectarse como ecuaciones"""
non_equation_cases = [
"result = solve(x + 2, x)", # Asignación Python
"2 + 3", # Expresión simple
"sin(pi/2)", # Función
]
for expr in non_equation_cases:
with self.subTest(expr=expr):
result, info = self.parser.parse_line(expr)
self.assertNotEqual(info, "equation")
def test_comments(self):
"""Test manejo de comentarios"""
comment_cases = [
"# Esto es un comentario",
" # Comentario con espacios",
"", # Línea vacía
]
for comment in comment_cases:
with self.subTest(comment=comment):
result, info = self.parser.parse_line(comment)
self.assertEqual(info, "comment")
class TestHybridBaseTypes(unittest.TestCase):
"""Tests para las clases base híbridas"""
def test_hybrid_hex(self):
"""Test clase HybridHex"""
# Creación desde string
h1 = Hex("FF")
self.assertEqual(h1.value, 255)
self.assertEqual(str(h1), "0xFF")
# Creación desde entero
h2 = Hex(255)
self.assertEqual(h2.value, 255)
self.assertEqual(str(h2), "0xFF")
# Verificar que es instancia de SymPy Basic
self.assertIsInstance(h1, sympy.Basic)
# Test conversión decimal
self.assertEqual(h1.__dec__(), 255)
def test_hybrid_bin(self):
"""Test clase HybridBin"""
# Creación desde string binario
b1 = Bin("1010")
self.assertEqual(b1.value, 10)
self.assertEqual(str(b1), "0b1010")
# Creación desde entero
b2 = Bin(10)
self.assertEqual(b2.value, 10)
self.assertEqual(str(b2), "0b1010")
# Verificar SymPy Basic
self.assertIsInstance(b1, sympy.Basic)
def test_hybrid_ip4(self):
"""Test clase HybridIP4"""
# IP con CIDR
ip1 = IP4("192.168.1.100/24")
self.assertEqual(ip1.ip_address, "192.168.1.100")
self.assertEqual(ip1.prefix, 24)
self.assertEqual(str(ip1), "192.168.1.100/24")
# IP sin máscara
ip2 = IP4("10.0.0.1")
self.assertEqual(ip2.ip_address, "10.0.0.1")
self.assertIsNone(ip2.prefix)
# Verificar SymPy Basic
self.assertIsInstance(ip1, sympy.Basic)
# Test métodos especializados
network = ip1.NetworkAddress()
self.assertEqual(str(network), "192.168.1.0/24")
broadcast = ip1.BroadcastAddress()
self.assertEqual(str(broadcast), "192.168.1.255/24")
nodes = ip1.Nodes()
self.assertEqual(nodes, 254) # 2^8 - 2
def test_hybrid_chr(self):
"""Test clase HybridChr"""
# Carácter único
c1 = Chr("A")
self.assertEqual(c1.value, 65)
self.assertEqual(str(c1), "A")
# String múltiple
c2 = Chr("Hello")
self.assertEqual(c2.value, [72, 101, 108, 108, 111])
self.assertEqual(str(c2), "Hello")
# Verificar SymPy Basic
self.assertIsInstance(c1, sympy.Basic)
def test_hybrid_dec(self):
"""Test clase HybridDec"""
# Desde string decimal
d1 = Dec("10.5")
self.assertEqual(d1.value, 10.5)
self.assertEqual(str(d1), "10.5")
# Desde entero
d2 = Dec(10)
self.assertEqual(d2.value, 10.0)
self.assertEqual(str(d2), "10")
# Verificar SymPy Basic
self.assertIsInstance(d1, sympy.Basic)
class TestHybridEvaluationEngine(unittest.TestCase):
"""Tests para el motor de evaluación híbrida"""
def setUp(self):
self.engine = HybridEvaluationEngine()
def test_basic_expressions(self):
"""Test expresiones básicas"""
test_cases = [
("2 + 3", 5),
("10 * 2", 20),
("15 / 3", 5),
]
for expr, expected in test_cases:
with self.subTest(expr=expr):
result = self.engine.evaluate_line(expr)
self.assertFalse(result.is_error)
self.assertEqual(result.result, expected)
def test_sympy_functions(self):
"""Test funciones de SymPy"""
# Test sin con pi/2
result = self.engine.evaluate_line("sin(pi/2)")
self.assertFalse(result.is_error)
self.assertEqual(result.result, 1)
# Test diferenciación
result = self.engine.evaluate_line("diff(x**2, x)")
self.assertFalse(result.is_error)
self.assertEqual(str(result.result), "2*x")
def test_bracket_syntax(self):
"""Test sintaxis con corchetes"""
# Test Hex
result = self.engine.evaluate_line("Hex[FF]")
self.assertFalse(result.is_error)
self.assertIsInstance(result.result, Hex)
self.assertEqual(result.result.value, 255)
# Test IP4
result = self.engine.evaluate_line("IP4[192.168.1.1/24]")
self.assertFalse(result.is_error)
self.assertIsInstance(result.result, IP4)
self.assertEqual(result.result.ip_address, "192.168.1.1")
def test_equation_handling(self):
"""Test manejo de ecuaciones"""
# Agregar ecuación
result = self.engine.evaluate_line("x + 2 = 5")
self.assertFalse(result.is_error)
self.assertEqual(result.result_type, "equation_added")
# Verificar que la ecuación se agregó
self.assertEqual(len(self.engine.equations), 1)
def test_variable_creation(self):
"""Test creación automática de variables"""
# Usar variable no definida
result = self.engine.evaluate_line("x + y")
self.assertFalse(result.is_error)
# Verificar que las variables se crearon como símbolos
self.assertIn("x", self.engine.symbol_table)
self.assertIn("y", self.engine.symbol_table)
self.assertIsInstance(self.engine.symbol_table["x"], sympy.Symbol)
def test_solve_shortcut(self):
"""Test atajo de solve"""
# Agregar ecuación
self.engine.evaluate_line("x + 2 = 5")
# Usar atajo solve
result = self.engine.evaluate_line("x=?")
self.assertFalse(result.is_error)
# Verificar que x se resolvió
self.assertIn("x", self.engine.symbol_table)
def test_error_handling(self):
"""Test manejo de errores"""
# División por cero
result = self.engine.evaluate_line("1/0")
self.assertTrue(result.is_error)
# Sintaxis inválida
result = self.engine.evaluate_line("2 +")
self.assertTrue(result.is_error)
def test_clear_operations(self):
"""Test operaciones de limpieza"""
# Agregar datos
self.engine.evaluate_line("x = 5")
self.engine.evaluate_line("y + 2 = 7")
# Verificar que hay datos
self.assertTrue(len(self.engine.symbol_table) > 0)
self.assertTrue(len(self.engine.equations) > 0)
# Limpiar variables
self.engine.clear_variables()
self.assertEqual(len(self.engine.symbol_table), 0)
# Limpiar ecuaciones
self.engine.clear_equations()
self.assertEqual(len(self.engine.equations), 0)
class TestIntegration(unittest.TestCase):
"""Tests de integración del sistema completo"""
def setUp(self):
self.engine = HybridEvaluationEngine()
def test_specialized_class_methods(self):
"""Test métodos de clases especializadas"""
# IP4 NetworkAddress
result = self.engine.evaluate_line("IP4[192.168.1.100/24].NetworkAddress[]")
self.assertFalse(result.is_error)
self.assertIsInstance(result.result, IP4)
self.assertEqual(str(result.result), "192.168.1.0/24")
def test_mixed_operations(self):
"""Test operaciones mixtas"""
# Hex + entero
result = self.engine.evaluate_line("Hex[FF] + 1")
self.assertFalse(result.is_error)
# El resultado depende de cómo implementemos las operaciones
def test_equation_solving_workflow(self):
"""Test flujo completo de resolución de ecuaciones"""
# Agregar ecuaciones
self.engine.evaluate_line("x + y = 10")
self.engine.evaluate_line("x - y = 2")
# Resolver sistema
try:
solutions = self.engine.solve_system()
self.assertIsInstance(solutions, dict)
# Verificar que x = 6, y = 4
self.assertEqual(solutions.get("x"), 6)
self.assertEqual(solutions.get("y"), 4)
except Exception as e:
self.fail(f"Solve system failed: {e}")
def test_sympy_integration(self):
"""Test integración con SymPy"""
# Diferenciación de expresión con variables
result = self.engine.evaluate_line("diff(x**3 + 2*x**2 + x, x)")
self.assertFalse(result.is_error)
expected = "3*x**2 + 4*x + 1"
self.assertEqual(str(result.result), expected)
# Integración
result = self.engine.evaluate_line("integrate(2*x, x)")
self.assertFalse(result.is_error)
self.assertEqual(str(result.result), "x**2")
def run_all_tests():
"""Ejecuta todos los tests"""
print("=== Ejecutando Suite de Tests ===\n")
# Crear suite de tests
loader = unittest.TestLoader()
suite = unittest.TestSuite()
# Agregar test classes
test_classes = [
TestBracketParser,
TestHybridBaseTypes,
TestHybridEvaluationEngine,
TestIntegration
]
for test_class in test_classes:
tests = loader.loadTestsFromTestCase(test_class)
suite.addTests(tests)
# Ejecutar tests
runner = unittest.TextTestRunner(verbosity=2)
result = runner.run(suite)
# Mostrar resumen
print(f"\n=== Resumen ===")
print(f"Tests ejecutados: {result.testsRun}")
print(f"Errores: {len(result.errors)}")
print(f"Fallos: {len(result.failures)}")
if result.errors:
print("\nErrores:")
for test, error in result.errors:
print(f" {test}: {error}")
if result.failures:
print("\nFallos:")
for test, failure in result.failures:
print(f" {test}: {failure}")
success = len(result.errors) == 0 and len(result.failures) == 0
print(f"\n{'✅ Todos los tests pasaron' if success else '❌ Algunos tests fallaron'}")
return success
def main():
"""Función principal"""
if len(sys.argv) > 1 and sys.argv[1] == "--verbose":
# Ejecutar tests individuales con más detalle
unittest.main(verbosity=2)
else:
# Ejecutar suite completa
success = run_all_tests()
sys.exit(0 if success else 1)
if __name__ == "__main__":
main()