From 449d5032137eb3a5d9a477c5c8ce848ae17a55c2 Mon Sep 17 00:00:00 2001 From: Miguel Date: Mon, 2 Jun 2025 23:05:54 +0200 Subject: [PATCH] =?UTF-8?q?Actualizaci=C3=B3n=20de=20la=20configuraci?= =?UTF-8?q?=C3=B3n=20de=20la=20ventana=20y=20ajustes=20en=20la=20posici?= =?UTF-8?q?=C3=B3n=20de=20los=20elementos.=20Se=20a=C3=B1ade=20soporte=20p?= =?UTF-8?q?ara=20la=20conversi=C3=B3n=20a=20LaTeX=20en=20varias=20partes?= =?UTF-8?q?=20del=20c=C3=B3digo,=20incluyendo=20nuevas=20funcionalidades?= =?UTF-8?q?=20en=20el=20motor=20de=20evaluaci=C3=B3n=20y=20en=20la=20clase?= =?UTF-8?q?=20de=20herramientas=20de=20Sympy.=20Se=20mejora=20la=20gesti?= =?UTF-8?q?=C3=B3n=20din=C3=A1mica=20de=20clases=20de=20corchetes,=20elimi?= =?UTF-8?q?nando=20dependencias=20de=20clases=20codificadas=20y=20optimiza?= =?UTF-8?q?ndo=20la=20recarga=20de=20clases.=20Tambi=C3=A9n=20se=20incorpo?= =?UTF-8?q?ra=20la=20opci=C3=B3n=20de=20registrar=20versiones=20en=20min?= =?UTF-8?q?=C3=BAscula=20de=20las=20clases=20en=20el=20registro=20de=20tip?= =?UTF-8?q?os.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- custom_types/latex_type.py | 134 +++++++++++++++++++++++++++++++++++++ hybrid_calc_history.txt | 7 +- hybrid_calc_settings.json | 4 +- main_evaluation.py | 7 +- sympy_helper.py | 1 + tl_bracket_parser.py | 62 +++++++++++------ type_registry.py | 4 ++ 7 files changed, 189 insertions(+), 30 deletions(-) create mode 100644 custom_types/latex_type.py diff --git a/custom_types/latex_type.py b/custom_types/latex_type.py new file mode 100644 index 0000000..444fdfb --- /dev/null +++ b/custom_types/latex_type.py @@ -0,0 +1,134 @@ +""" +Clase híbrida para conversión LaTeX - ADAPTADA AL NUEVO SISTEMA +""" +from sympy_Base import SympyClassBase +import sympy +import re + + +class Class_LaTeX(SympyClassBase): + """Clase híbrida para conversión de expresiones a formato LaTeX""" + + def __new__(cls, value_input): + """Crear objeto SymPy válido""" + obj = SympyClassBase.__new__(cls) + return obj + + def __init__(self, expression): + """Inicialización de LaTeX""" + # Si el input es string, intentar parsearlo como expresión de SymPy + if isinstance(expression, str): + try: + # Parsear la expresión string a objeto SymPy + parsed_expr = sympy.sympify(expression) + self._expression = parsed_expr + except Exception as e: + # Si no se puede parsear, mantener como string + self._expression = expression + else: + # Si ya es un objeto SymPy, usarlo directamente + self._expression = expression + + # Generar código LaTeX + try: + if hasattr(self._expression, '__class__') and hasattr(sympy, 'latex'): + self._latex_code = sympy.latex(self._expression) + else: + self._latex_code = str(self._expression) + except Exception: + self._latex_code = str(self._expression) + + # Llamar al constructor base con el código LaTeX como valor de display + super().__init__(self._expression, self._latex_code) + + def __str__(self): + """Representación string que muestra el código LaTeX""" + return self._latex_code + + def _sympystr(self, printer): + """Representación SymPy string""" + return self._latex_code + + def latex_code(self): + """Retorna el código LaTeX generado""" + return self._latex_code + + def original_expression(self): + """Retorna la expresión original""" + return self._expression + + def to_image(self, filename=None, dpi=150): + """ + Convierte el LaTeX a imagen (requiere matplotlib) + """ + try: + import matplotlib.pyplot as plt + import matplotlib + + # Configurar matplotlib para usar LaTeX + matplotlib.rcParams['text.usetex'] = True + + fig, ax = plt.subplots(figsize=(10, 2)) + ax.text(0.5, 0.5, f'${self._latex_code}$', + horizontalalignment='center', + verticalalignment='center', + fontsize=16, + transform=ax.transAxes) + ax.axis('off') + + if filename: + plt.savefig(filename, dpi=dpi, bbox_inches='tight') + plt.close() + return f"LaTeX guardado como imagen en: {filename}" + else: + plt.show() + return "LaTeX mostrado en pantalla" + + except ImportError: + return "Error: matplotlib no está instalado. Instalar con: pip install matplotlib" + except Exception as e: + return f"Error generando imagen: {e}" + + @staticmethod + def Helper(input_str): + """Ayuda contextual para LaTeX""" + if re.match(r"^\s*[Ll]atex\b", input_str, re.IGNORECASE): + return '''LaTeX - Conversión de expresiones matemáticas a código LaTeX + +Uso: LaTeX[expresion] o latex[expresion] + +Ejemplos: + LaTeX[x**2 + 2*x + 1] → x^{2} + 2 x + 1 + LaTeX[sin(x)/cos(x)] → \\frac{\\sin{\\left(x \\right)}}{\\cos{\\left(x \\right)}} + LaTeX[Integral(x, x)] → \\int x\\,dx + LaTeX[sqrt(x**2 + y**2)] → \\sqrt{x^{2} + y^{2}} + +Métodos disponibles: + .latex_code() - Obtiene el código LaTeX + .original_expression() - Obtiene la expresión original + .to_image() - Convierte a imagen (requiere matplotlib) + +Nota: Las expresiones pueden ser strings o objetos SymPy''' + return None + + @staticmethod + def PopupFunctionList(): + """Lista de métodos sugeridos para autocompletado de LaTeX""" + return [ + ("latex_code", "Obtiene el código LaTeX generado"), + ("original_expression", "Obtiene la expresión matemática original"), + ("to_image", "Convierte el LaTeX a imagen (requiere matplotlib)"), + ] + + +def register_classes_in_module(): + """ + Devuelve una lista de clases definidas en este módulo para ser registradas. + """ + return [ + ("LaTeX", Class_LaTeX, "SympyClassBase", { + "add_lowercase": True, + "supports_brackets": True, + "description": "Conversión de expresiones matemáticas a formato LaTeX" + }), + ] \ No newline at end of file diff --git a/hybrid_calc_history.txt b/hybrid_calc_history.txt index 0028bbb..8c9fd9c 100644 --- a/hybrid_calc_history.txt +++ b/hybrid_calc_history.txt @@ -20,8 +20,7 @@ m=IP4Mask[23] IP4Mask[22].to_hex() -IP4[110.1.30.70;255.255.255.0] +n=IP4[110.1.30.70;255.255.255.0] -a=25/51 - -a*52 \ No newline at end of file +latex[x**z/rr] +latex(x**z/rr) \ No newline at end of file diff --git a/hybrid_calc_settings.json b/hybrid_calc_settings.json index 167ed43..d0c3bf5 100644 --- a/hybrid_calc_settings.json +++ b/hybrid_calc_settings.json @@ -1,6 +1,6 @@ { - "window_geometry": "1020x700+137+49", - "sash_pos_x": 353, + "window_geometry": "1020x700+238+107", + "sash_pos_x": 355, "symbolic_mode": true, "show_numeric_approximation": true, "keep_symbolic_fractions": true, diff --git a/main_evaluation.py b/main_evaluation.py index f97b792..93989c5 100644 --- a/main_evaluation.py +++ b/main_evaluation.py @@ -113,6 +113,8 @@ class HybridEvaluationEngine: 'Matrix': sympy.Matrix, 'det': lambda m: m.det() if hasattr(m, 'det') else sympy.det(m), 'inv': lambda m: m.inv() if hasattr(m, 'inv') else sympy.Matrix(m).inv(), + # Printing + 'latex': sympy.latex, # NUEVO: función latex global # Plotting (será manejado por resultados interactivos) 'plot': self._create_plot_placeholder, 'plot3d': self._create_plot3d_placeholder, @@ -149,9 +151,8 @@ class HybridEvaluationEngine: def _update_bracket_parser(self): """Actualiza el BracketParser con las clases descubiertas""" try: - discovered_bracket_classes = get_registered_bracket_classes() - # Combinar con clases existentes del parser - self.parser.BRACKET_CLASSES = self.parser.BRACKET_CLASSES.union(discovered_bracket_classes) + # NUEVO: Llamar al método reload para actualizar dinámicamente + self.parser.reload_bracket_classes() if self.debug: print(f"🔧 Bracket classes actualizadas: {self.parser.BRACKET_CLASSES}") diff --git a/sympy_helper.py b/sympy_helper.py index 7615c9f..86b7491 100644 --- a/sympy_helper.py +++ b/sympy_helper.py @@ -21,6 +21,7 @@ class SympyTools: "cancel": "Cancelar: cancel(expr). Ej: cancel((x**2 + 2*x + 1)/(x+1))", "apart": "Fracciones parciales: apart(expr). Ej: apart(1/(x*(x+1)))", # Var opcional en apart "together": "Unir fracciones: together(expr). Ej: together(1/x + 1/y)", + "latex": "Convertir a LaTeX: latex(expr). Ej: latex(x**2 + 1) → x^{2} + 1", } @staticmethod diff --git a/tl_bracket_parser.py b/tl_bracket_parser.py index 349ce9f..7cb0137 100644 --- a/tl_bracket_parser.py +++ b/tl_bracket_parser.py @@ -17,8 +17,8 @@ except ImportError: class BracketParser: """Parser que convierte sintaxis con corchetes y detecta ecuaciones contextualmente""" - # Clases base que soportan sintaxis con corchetes (fallback) - DEFAULT_BRACKET_CLASSES = {'IP4', 'Hex', 'Bin', 'Date', 'Dec', 'Chr', 'IP4Mask'} + # Clases de fallback mínimas si falla el registro dinámico + FALLBACK_BRACKET_CLASSES = {'Hex', 'Bin'} # Operadores de comparación que pueden formar ecuaciones EQUATION_OPERATORS = {'==', '!=', '<', '<=', '>', '>=', '='} @@ -27,32 +27,52 @@ class BracketParser: self.debug = False self.use_type_registry = use_type_registry and TYPE_REGISTRY_AVAILABLE - # Inicializar clases de corchetes + # Inicializar clases de corchetes dinámicamente self._update_bracket_classes() - def _update_bracket_classes(self): - """Actualiza las clases que soportan sintaxis con corchetes""" - if self.use_type_registry: - try: - # Obtener clases del registro de tipos - registered_classes = get_registered_bracket_classes() - self.BRACKET_CLASSES = registered_classes.union(self.DEFAULT_BRACKET_CLASSES) - - if self.debug: - print(f"🔧 Bracket classes desde registro: {registered_classes}") - print(f"🔧 Total bracket classes: {self.BRACKET_CLASSES}") + def _get_dynamic_bracket_classes(self) -> Set[str]: + """ + Obtiene las clases de corchetes dinámicamente del registro de tipos + NUEVA FUNCIÓN que reemplaza el hardcoding de DEFAULT_BRACKET_CLASSES + """ + if not self.use_type_registry: + return self.FALLBACK_BRACKET_CLASSES.copy() + + try: + # Obtener TODAS las clases registradas que soportan corchetes + registered_classes = get_registered_bracket_classes() - except Exception as e: + if self.debug: + print(f"🔧 Clases dinámicas obtenidas del registro: {registered_classes}") + + # Si no hay clases registradas, usar fallback + if not registered_classes: if self.debug: - print(f"⚠️ Error obteniendo clases del registro: {e}") - self.BRACKET_CLASSES = self.DEFAULT_BRACKET_CLASSES.copy() - else: - self.BRACKET_CLASSES = self.DEFAULT_BRACKET_CLASSES.copy() + print("⚠️ No se encontraron clases registradas, usando fallback") + return self.FALLBACK_BRACKET_CLASSES.copy() + + return registered_classes + + except Exception as e: + if self.debug: + print(f"⚠️ Error obteniendo clases dinámicas: {e}") + return self.FALLBACK_BRACKET_CLASSES.copy() + + def _update_bracket_classes(self): + """ + Actualiza las clases que soportan sintaxis con corchetes dinámicamente + MODIFICADO: Ya no usa DEFAULT_BRACKET_CLASSES hardcodeadas + """ + # Obtener clases dinámicamente del registro de tipos + self.BRACKET_CLASSES = self._get_dynamic_bracket_classes() + + if self.debug: + print(f"🔧 Bracket classes actualizadas dinámicamente: {self.BRACKET_CLASSES}") def reload_bracket_classes(self): - """Recarga las clases de corchetes desde el registro""" + """Recarga las clases de corchetes desde el registro dinámicamente""" if self.debug: - print("🔄 Recargando bracket classes...") + print("🔄 Recargando bracket classes dinámicamente...") self._update_bracket_classes() def add_bracket_class(self, class_name: str): diff --git a/type_registry.py b/type_registry.py index 0f368f6..463ace5 100644 --- a/type_registry.py +++ b/type_registry.py @@ -116,6 +116,10 @@ class TypeRegistry: # Registrar en bracket_classes si es apropiado if category in ['ClassBase', 'SympyClassBase'] or options.get('supports_brackets', False): self.bracket_classes.add(name) + + # NUEVA FUNCIONALIDAD: También agregar versión en minúscula a bracket_classes + if options.get('add_lowercase', True): + self.bracket_classes.add(name.lower()) # Registrar función Helper si existe if hasattr(class_obj, 'Helper') and callable(class_obj.Helper):