Eliminación de archivos innecesarios (icon.png y MaVCalcv2.lnk). Actualización del historial de cálculos con nuevos formatos de salario y tarifas. Refactorización del motor algebraico para mejorar la salida de resultados y la gestión de ecuaciones, implementando un nuevo formato de salida que incluye aproximaciones numéricas. Mejora en la generación de contenido LaTeX para ecuaciones y asignaciones.

This commit is contained in:
Miguel 2025-06-12 10:22:02 +02:00
parent e51e6bf1ae
commit 639081c0d0
6 changed files with 119 additions and 58 deletions

View File

@ -1,12 +1,10 @@
ex = (t * 8) / w salario=horas*tarifa
salario=8000
tarifa=36
horas=?
var1 = 2
var2 = 4
vatt1 = 4
vatvatt1()

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

View File

@ -361,10 +361,8 @@ class PureAlgebraicEngine:
var_symbol = sp.Symbol(var_content) var_symbol = sp.Symbol(var_content)
solution_result_obj = self._smart_solve(var_symbol) solution_result_obj = self._smart_solve(var_symbol)
output = str(solution_result_obj) # Usar nuevo formato de salida
numeric = self._get_numeric_approximation(solution_result_obj) output = self._format_output_with_approximation(solution_result_obj)
if numeric and str(solution_result_obj) != str(numeric):
output += f"{numeric}"
current_algebraic_type = type(solution_result_obj).__name__ current_algebraic_type = type(solution_result_obj).__name__
# Los resultados de solve() generalmente no son plots # Los resultados de solve() generalmente no son plots
@ -383,22 +381,16 @@ class PureAlgebraicEngine:
# Para casos más complejos de solve() o si el regex no coincide, # Para casos más complejos de solve() o si el regex no coincide,
# usar _evaluate_expression que ya maneja 'last' y los tipos. # usar _evaluate_expression que ya maneja 'last' y los tipos.
# _evaluate_expression se encargará de self.last_result_object. # _evaluate_expression se encargará de self.last_result_object.
result = self._evaluate_expression(line) # Llama a la versión ya modificada result = self._evaluate_expression(line)
result.is_solve_query = True # Mantener esta bandera
return result return result
# Si no es solve(), podría ser otro tipo de atajo (si se añaden más tarde)
# Por ahora, si no empieza con solve(, lo tratamos como una expresión normal.
return self._evaluate_expression(line)
except Exception as e: except Exception as e:
error_msg = f"Error en atajo de resolución '{line}': {type(e).__name__}: {str(e)}" error_msg = f"Error en solve(): {type(e).__name__}: {str(e)}"
self.logger.error(error_msg) self.logger.error(error_msg)
# No actualizar last_result_object en caso de error
return EvaluationResult(line, error_msg, "error", False, str(e), is_solve_query=True) return EvaluationResult(line, error_msg, "error", False, str(e), is_solve_query=True)
def _evaluate_assignment(self, line: str) -> EvaluationResult: def _evaluate_assignment(self, line: str) -> EvaluationResult:
"""Evalúa una asignación CON FUNCIONALIDAD DUAL: asignación al contexto + ecuación a sympy""" """Evalúa una asignación y gestiona 'last'"""
try: try:
var_name, expression_str = line.split('=', 1) var_name, expression_str = line.split('=', 1)
var_name = var_name.strip() var_name = var_name.strip()
@ -439,7 +431,8 @@ class PureAlgebraicEngine:
# Si falla como ecuación, continuar solo con la asignación # Si falla como ecuación, continuar solo con la asignación
self.logger.warning(f"No se pudo agregar '{line}' como ecuación: {eq_error}") self.logger.warning(f"No se pudo agregar '{line}' como ecuación: {eq_error}")
output = f"{var_name} = {result_obj}" # Usar nuevo formato de salida consistente
output = self._format_output_with_approximation(Eq(sp.Symbol(var_name), result_obj))
# Devolver el resultado de la asignación # Devolver el resultado de la asignación
return EvaluationResult( return EvaluationResult(
@ -489,7 +482,9 @@ class PureAlgebraicEngine:
for free_symbol in equation_obj.free_symbols: for free_symbol in equation_obj.free_symbols:
self.variables.add(str(free_symbol)) self.variables.add(str(free_symbol))
output = str(equation_obj) # Usar nuevo formato de salida
output = self._format_equation_output(equation_obj)
# No actualizar last_result_object para ecuaciones # No actualizar last_result_object para ecuaciones
return EvaluationResult( return EvaluationResult(
input_line=line, input_line=line,
@ -518,20 +513,12 @@ class PureAlgebraicEngine:
if isinstance(result_obj, PlotResult) and not result_obj.original_expression: if isinstance(result_obj, PlotResult) and not result_obj.original_expression:
result_obj.original_expression = line result_obj.original_expression = line
output = str(result_obj) # Usar nuevo formato de salida que maneja Eq() automáticamente
output = self._format_output_with_approximation(result_obj)
result_type_str = "symbolic" # Tipo por defecto result_type_str = "symbolic" # Tipo por defecto
current_algebraic_type = type(result_obj).__name__ current_algebraic_type = type(result_obj).__name__
# Manejo especial para tipos de sympy que pueden necesitar aproximación numérica
if hasattr(result_obj, 'evalf') and not isinstance(result_obj, (sp.Matrix, sp.Basic, sp.Expr)):
# Evitar evalf en matrices directamente o en tipos ya específicos como Integer, Float
pass
numeric_approximation = self._get_numeric_approximation(result_obj)
if numeric_approximation and output != numeric_approximation:
output += f"{numeric_approximation}"
# Considerar si esto cambia el result_type a "numeric_approx"
# Determinar si es un objeto de plotting (para no asignarlo a 'last') # Determinar si es un objeto de plotting (para no asignarlo a 'last')
is_plot_object = False is_plot_object = False
if isinstance(result_obj, PlotResult): if isinstance(result_obj, PlotResult):
@ -825,10 +812,21 @@ class PureAlgebraicEngine:
try: try:
float_val = float(numeric_val) float_val = float(numeric_val)
if abs(float_val) > 1e-10: if abs(float_val) > 1e-10:
return f"{float_val:.6f}".rstrip('0').rstrip('.') # Formatear número eliminando ceros innecesarios
if float_val == int(float_val):
# Es un entero, mostrarlo como tal
return str(int(float_val))
else:
# Es decimal, formatear con precisión limitada
formatted = f"{float_val:.10f}".rstrip('0').rstrip('.')
# Si queda muy largo, usar notación más compacta
if len(formatted) > 12:
formatted = f"{float_val:.6g}"
return formatted
except: except:
pass pass
# Para otros tipos (complejos, expresiones, etc.)
return str(numeric_val) return str(numeric_val)
return None return None
except: except:
@ -872,6 +870,63 @@ class PureAlgebraicEngine:
self._load_tokenization_patterns() self._load_tokenization_patterns()
self.logger.info("Tipos y patrones recargados") self.logger.info("Tipos y patrones recargados")
def _format_equation_output(self, result_obj) -> str:
"""Convierte Eq(var, valor) a formato legible var = valor"""
try:
if hasattr(result_obj, 'func') and result_obj.func.__name__ == 'Equality':
# Es una ecuación Eq(lhs, rhs)
lhs = result_obj.lhs
rhs = result_obj.rhs
return f"{lhs} = {rhs}"
else:
# No es una ecuación, devolver string normal
return str(result_obj)
except Exception:
return str(result_obj)
def _format_output_with_approximation(self, result_obj) -> str:
"""Formatea salida con aproximación numérica, convirtiendo Eq() a formato legible"""
main_output = self._format_equation_output(result_obj)
# Obtener aproximación numérica
numeric_approximation = self._get_numeric_approximation(result_obj)
if numeric_approximation and main_output != numeric_approximation:
# Para ecuaciones, formatear también la aproximación
if hasattr(result_obj, 'func') and result_obj.func.__name__ == 'Equality':
# Crear aproximación en formato var = valor_aproximado
try:
lhs = result_obj.lhs
rhs_numeric = result_obj.rhs.evalf() if hasattr(result_obj.rhs, 'evalf') else result_obj.rhs
# Formatear la parte numérica con el mismo sistema
rhs_numeric_str = self._get_numeric_approximation(result_obj.rhs)
if rhs_numeric_str is None:
rhs_numeric_str = str(rhs_numeric)
numeric_output = f"{lhs} = {rhs_numeric_str}"
# Comparar si son realmente diferentes
# Extraer solo las partes de valor para comparar
main_value = str(result_obj.rhs)
if main_value != rhs_numeric_str:
return f"{main_output}{numeric_output}"
else:
# Son iguales, no mostrar aproximación
return main_output
except:
return f"{main_output}{numeric_approximation}"
else:
# Para expresiones no-ecuación, comparar directamente
if str(result_obj) != numeric_approximation:
return f"{main_output}{numeric_approximation}"
else:
# Son iguales, no mostrar aproximación
return main_output
return main_output
# ========== FUNCIÓN DE EVALUACIÓN DIRECTA ========== # ========== FUNCIÓN DE EVALUACIÓN DIRECTA ==========

View File

@ -417,17 +417,22 @@ class LatexProcessor:
main_content = "" main_content = ""
if result.actual_result_object is not None: if result.actual_result_object is not None:
try: try:
# Intentar generar LaTeX de SymPy para el objeto matemático # Verificar si es una ecuación Eq()
main_content = sympy.latex(result.actual_result_object) if hasattr(result.actual_result_object, 'func') and result.actual_result_object.func.__name__ == 'Equality':
# Es una ecuación, convertir a formato var = valor
lhs = result.actual_result_object.lhs
rhs = result.actual_result_object.rhs
lhs_latex = sympy.latex(lhs)
rhs_latex = sympy.latex(rhs)
main_content = f"{lhs_latex} = {rhs_latex}"
else:
# Intentar generar LaTeX de SymPy para el objeto matemático
main_content = sympy.latex(result.actual_result_object)
# Para asignaciones, necesitamos agregar el lado izquierdo # Para asignaciones, el resultado ya viene formateado
if result.is_assignment and result.input_line: if result.is_assignment and not main_content:
# Extraer la variable del lado izquierdo # Extraer del output ya formateado
if '=' in result.input_line: main_content = result.output.split("")[0] if "" in result.output else result.output
left_side = result.input_line.split('=')[0].strip()
# Limpiar posibles símbolos LaTeX del lado izquierdo
left_side = left_side.replace('$$', '').strip()
main_content = f"{left_side} = {main_content}"
except Exception: except Exception:
# Si falla el LaTeX de SymPy, usar el output textual # Si falla el LaTeX de SymPy, usar el output textual
@ -446,19 +451,8 @@ class LatexProcessor:
if ";" in approx_value: if ";" in approx_value:
approx_value = approx_value.split(";")[0].strip() approx_value = approx_value.split(";")[0].strip()
# Intentar convertir la aproximación a LaTeX si es una ecuación # La aproximación ya viene en formato var = valor, mantenerla así
try: latex_parts.append(f"\\approx {approx_value}")
if "Eq(" in approx_value:
# Es una ecuación, intentar parserarla para LaTeX
approx_obj = eval(approx_value, {'Eq': sympy.Eq, 'sqrt': sympy.sqrt})
approx_latex = sympy.latex(approx_obj)
latex_parts.append(f"\\approx {approx_latex}")
else:
# Es un valor numérico simple
latex_parts.append(f"\\approx {approx_value}")
except:
# Si falla, usar la aproximación como texto
latex_parts.append(f"\\approx {approx_value}")
# PARTE 3: Indicador de tipo (si está en el output) # PARTE 3: Indicador de tipo (si está en el output)
if result.output and "[" in result.output and "]" in result.output: if result.output and "[" in result.output and "]" in result.output:

View File

@ -3,6 +3,7 @@ Calculadora MAV CAS Híbrida - Aplicación principal PySide6 (Refactorizada)
VERSIÓN MODULAR: Código organizado en módulos especializados VERSIÓN MODULAR: Código organizado en módulos especializados
""" """
import sys import sys
import os
import logging import logging
from typing import Optional, Dict, Any, List from typing import Optional, Dict, Any, List
@ -11,7 +12,7 @@ from PySide6.QtWidgets import (
QSplitter, QStatusBar QSplitter, QStatusBar
) )
from PySide6.QtCore import Qt, QTimer from PySide6.QtCore import Qt, QTimer
from PySide6.QtGui import QKeySequence, QShortcut from PySide6.QtGui import QKeySequence, QShortcut, QPixmap, QIcon
# Importar componentes del CAS híbrido # Importar componentes del CAS híbrido
from .evaluation import PureAlgebraicEngine from .evaluation import PureAlgebraicEngine
@ -41,6 +42,9 @@ class HybridCalculatorPySide6(QMainWindow):
logging.basicConfig(level=logging.DEBUG, format='%(levelname)s: %(message)s') logging.basicConfig(level=logging.DEBUG, format='%(levelname)s: %(message)s')
self.logger = logging.getLogger(__name__) self.logger = logging.getLogger(__name__)
# Configurar icono de la aplicación
self._setup_application_icon()
# Motor y managers # Motor y managers
self.engine = PureAlgebraicEngine() self.engine = PureAlgebraicEngine()
self.interactive_manager = None self.interactive_manager = None
@ -305,6 +309,16 @@ class HybridCalculatorPySide6(QMainWindow):
self.settings_manager.save_all() self.settings_manager.save_all()
event.accept() event.accept()
def _setup_application_icon(self):
"""Configura el icono de la aplicación"""
# Buscar el icono en el directorio raíz del proyecto
icon_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), ".res", "icon.png")
if os.path.exists(icon_path):
self.setWindowIcon(QIcon(icon_path))
self.logger.debug(f"✅ Icono cargado desde: {icon_path}")
else:
self.logger.warning(f"⚠️ Icono no encontrado en: {icon_path}")
def main(): def main():
"""Función principal para ejecutar la aplicación""" """Función principal para ejecutar la aplicación"""