diff --git a/.data/history.txt b/.data/history.txt index 77357dd..6ea2f02 100644 --- a/.data/history.txt +++ b/.data/history.txt @@ -1,12 +1,10 @@ -ex = (t * 8) / w +salario=horas*tarifa +salario=8000 +tarifa=36 + +horas=? - -var1 = 2 -var2 = 4 -vatt1 = 4 - -vatvatt1() diff --git a/icon.png b/.res/icon.png similarity index 100% rename from icon.png rename to .res/icon.png diff --git a/MaVCalcv2.lnk b/Calc.lnk similarity index 59% rename from MaVCalcv2.lnk rename to Calc.lnk index 4a564ac..8f3610c 100644 Binary files a/MaVCalcv2.lnk and b/Calc.lnk differ diff --git a/app/evaluation.py b/app/evaluation.py index 78cc997..96b0476 100644 --- a/app/evaluation.py +++ b/app/evaluation.py @@ -361,10 +361,8 @@ class PureAlgebraicEngine: var_symbol = sp.Symbol(var_content) solution_result_obj = self._smart_solve(var_symbol) - output = str(solution_result_obj) - numeric = self._get_numeric_approximation(solution_result_obj) - if numeric and str(solution_result_obj) != str(numeric): - output += f" ≈ {numeric}" + # Usar nuevo formato de salida + output = self._format_output_with_approximation(solution_result_obj) current_algebraic_type = type(solution_result_obj).__name__ # 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, # usar _evaluate_expression que ya maneja 'last' y los tipos. # _evaluate_expression se encargará de self.last_result_object. - result = self._evaluate_expression(line) # Llama a la versión ya modificada - result.is_solve_query = True # Mantener esta bandera + result = self._evaluate_expression(line) 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: - 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) - # No actualizar last_result_object en caso de error return EvaluationResult(line, error_msg, "error", False, str(e), is_solve_query=True) 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: var_name, expression_str = line.split('=', 1) var_name = var_name.strip() @@ -439,7 +431,8 @@ class PureAlgebraicEngine: # 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}") - 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 return EvaluationResult( @@ -489,7 +482,9 @@ class PureAlgebraicEngine: for free_symbol in equation_obj.free_symbols: 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 return EvaluationResult( input_line=line, @@ -518,20 +513,12 @@ class PureAlgebraicEngine: if isinstance(result_obj, PlotResult) and not result_obj.original_expression: 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 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') is_plot_object = False if isinstance(result_obj, PlotResult): @@ -825,10 +812,21 @@ class PureAlgebraicEngine: try: float_val = float(numeric_val) 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: pass + # Para otros tipos (complejos, expresiones, etc.) return str(numeric_val) return None except: @@ -872,6 +870,63 @@ class PureAlgebraicEngine: self._load_tokenization_patterns() 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 ========== diff --git a/app/gui_latex.py b/app/gui_latex.py index b8eb2bc..8802f4d 100644 --- a/app/gui_latex.py +++ b/app/gui_latex.py @@ -417,17 +417,22 @@ class LatexProcessor: main_content = "" if result.actual_result_object is not None: try: - # Intentar generar LaTeX de SymPy para el objeto matemático - main_content = sympy.latex(result.actual_result_object) + # Verificar si es una ecuación Eq() + 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 - if result.is_assignment and result.input_line: - # Extraer la variable del lado izquierdo - if '=' in result.input_line: - 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}" + # Para asignaciones, el resultado ya viene formateado + if result.is_assignment and not main_content: + # Extraer del output ya formateado + main_content = result.output.split(" ≈")[0] if " ≈" in result.output else result.output except Exception: # Si falla el LaTeX de SymPy, usar el output textual @@ -446,19 +451,8 @@ class LatexProcessor: if ";" in approx_value: approx_value = approx_value.split(";")[0].strip() - # Intentar convertir la aproximación a LaTeX si es una ecuación - try: - 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}") + # La aproximación ya viene en formato var = valor, mantenerla así + latex_parts.append(f"\\approx {approx_value}") # PARTE 3: Indicador de tipo (si está en el output) if result.output and "[" in result.output and "]" in result.output: diff --git a/app/gui_main.py b/app/gui_main.py index e9e6b9e..13c51db 100644 --- a/app/gui_main.py +++ b/app/gui_main.py @@ -3,6 +3,7 @@ Calculadora MAV CAS Híbrida - Aplicación principal PySide6 (Refactorizada) VERSIÓN MODULAR: Código organizado en módulos especializados """ import sys +import os import logging from typing import Optional, Dict, Any, List @@ -11,7 +12,7 @@ from PySide6.QtWidgets import ( QSplitter, QStatusBar ) 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 from .evaluation import PureAlgebraicEngine @@ -41,6 +42,9 @@ class HybridCalculatorPySide6(QMainWindow): logging.basicConfig(level=logging.DEBUG, format='%(levelname)s: %(message)s') self.logger = logging.getLogger(__name__) + # Configurar icono de la aplicación + self._setup_application_icon() + # Motor y managers self.engine = PureAlgebraicEngine() self.interactive_manager = None @@ -305,6 +309,16 @@ class HybridCalculatorPySide6(QMainWindow): self.settings_manager.save_all() 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(): """Función principal para ejecutar la aplicación"""