Implementación de un sistema de logging para mejorar la gestión de advertencias y errores en la aplicación. Se establece un límite para la cantidad de archivos de log generados y se optimiza la configuración de la ventana. Se ajusta el manejo de la evaluación y el autocompletado, mejorando la experiencia del usuario.
This commit is contained in:
parent
589bab03b2
commit
0c7ed33d0d
File diff suppressed because it is too large
Load Diff
15
calc.py
15
calc.py
|
@ -19,6 +19,7 @@ import platform
|
|||
|
||||
def setup_logging():
|
||||
"""Configura el sistema de logging completo"""
|
||||
MAX_LOG_FILES = 10 # Límite de archivos de log
|
||||
log_dir = Path("logs")
|
||||
log_dir.mkdir(exist_ok=True)
|
||||
|
||||
|
@ -26,6 +27,20 @@ def setup_logging():
|
|||
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
log_file = log_dir / f"mav_calc_{timestamp}.log"
|
||||
|
||||
# Eliminar logs antiguos si se supera el límite
|
||||
try:
|
||||
existing_logs = sorted(
|
||||
[f for f in log_dir.glob("mav_calc_*.log") if f.is_file()],
|
||||
key=os.path.getmtime
|
||||
)
|
||||
if len(existing_logs) >= MAX_LOG_FILES:
|
||||
logs_to_delete = existing_logs[:len(existing_logs) - MAX_LOG_FILES + 1]
|
||||
for old_log in logs_to_delete:
|
||||
old_log.unlink()
|
||||
logging.info(f"Eliminado log antiguo: {old_log}")
|
||||
except Exception as e:
|
||||
logging.warning(f"No se pudieron eliminar logs antiguos: {e}")
|
||||
|
||||
# Configurar logging
|
||||
logging.basicConfig(
|
||||
level=logging.DEBUG,
|
||||
|
|
|
@ -1,10 +1,5 @@
|
|||
|
||||
a = 10 + b
|
||||
a=?
|
||||
|
||||
x=12
|
||||
a=x
|
||||
|
||||
x=2
|
||||
b=x
|
||||
|
||||
a
|
||||
b
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"window_geometry": "1020x700+144+161",
|
||||
"sash_pos_x": 355,
|
||||
"window_geometry": "1020x700+467+199",
|
||||
"sash_pos_x": 363,
|
||||
"symbolic_mode": true,
|
||||
"show_numeric_approximation": true,
|
||||
"keep_symbolic_fractions": true,
|
||||
|
|
202
main_calc_app.py
202
main_calc_app.py
|
@ -6,6 +6,7 @@ import tkinter as tk
|
|||
from tkinter import scrolledtext, messagebox, Menu, filedialog
|
||||
import tkinter.font as tkFont
|
||||
import json
|
||||
import logging # <--- AÑADIDO
|
||||
import os
|
||||
from pathlib import Path
|
||||
import threading
|
||||
|
@ -35,10 +36,12 @@ except ImportError:
|
|||
except ImportError:
|
||||
HTML_VIEWER_TYPE = None
|
||||
|
||||
# Usar logging para estas advertencias iniciales
|
||||
module_logger = logging.getLogger(__name__)
|
||||
if not MARKDOWN_AVAILABLE:
|
||||
print("Advertencia: La librería 'markdown' no está instalada. La ayuda se mostrará en texto plano.")
|
||||
module_logger.warning("La librería 'markdown' no está instalada. La ayuda se mostrará en texto plano.")
|
||||
if MARKDOWN_AVAILABLE and HTML_VIEWER_TYPE is None:
|
||||
print("Advertencia: 'markdown' está disponible, pero no se encontró un visor HTML (tkinterweb/tkhtmlview). La ayuda se mostrará en texto plano.")
|
||||
module_logger.warning("'markdown' está disponible, pero no se encontró un visor HTML (tkinterweb/tkhtmlview). La ayuda se mostrará en texto plano.")
|
||||
|
||||
# ========== IMPORTS ADAPTADOS AL NUEVO SISTEMA ==========
|
||||
# Importar componentes del CAS híbrido con nuevo sistema de tipos
|
||||
|
@ -58,6 +61,7 @@ class HybridCalculatorApp:
|
|||
|
||||
def __init__(self, root: tk.Tk):
|
||||
self.root = root
|
||||
self.logger = logging.getLogger(__name__) # <--- AÑADIDO: Logger para la instancia
|
||||
self.root.title("Calculadora MAV - CAS Híbrido")
|
||||
|
||||
# Configuración y estado
|
||||
|
@ -121,34 +125,35 @@ class HybridCalculatorApp:
|
|||
# Obtener helpers registrados dinámicamente
|
||||
self.HELPERS = get_registered_helper_functions()
|
||||
|
||||
# Añadir SympyHelper al final
|
||||
# Añadir SympyHelper.Helper al final
|
||||
self.HELPERS.append(SympyHelper.Helper)
|
||||
|
||||
print(f"🆘 Helpers dinámicos cargados: {len(self.HELPERS)}")
|
||||
# Usar logger en lugar de print, y sin emoji para la consola
|
||||
self.logger.info(f"Helpers dinámicos cargados: {len(self.HELPERS)}") # Original: 🆘
|
||||
|
||||
except Exception as e:
|
||||
print(f"⚠️ Error cargando helpers dinámicos: {e}")
|
||||
# Usar logger en lugar de print, y sin emoji para la consola
|
||||
self.logger.error(f"Error cargando helpers dinámicos: {e}", exc_info=True) # Original: ⚠️
|
||||
# Fallback a helpers básicos
|
||||
self.HELPERS = [SympyHelper.Helper]
|
||||
|
||||
def reload_types(self):
|
||||
"""Recarga el sistema de tipos (útil para desarrollo)"""
|
||||
try:
|
||||
print("🔄 Recargando sistema de tipos...")
|
||||
self.logger.info("Recargando sistema de tipos...") # Original: 🔄
|
||||
|
||||
# Recargar engine
|
||||
self.engine.reload_types()
|
||||
|
||||
# Recargar helpers
|
||||
self._setup_dynamic_helpers()
|
||||
|
||||
# Re-evaluar contenido actual
|
||||
self._evaluate_and_update()
|
||||
|
||||
print("✅ Sistema de tipos recargado")
|
||||
self.logger.info("Sistema de tipos recargado.") # Original: ✅
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Error recargando tipos: {e}")
|
||||
self.logger.error(f"Error recargando tipos: {e}", exc_info=True) # Original: ❌
|
||||
messagebox.showerror("Error", f"Error recargando tipos:\n{e}")
|
||||
|
||||
def show_types_info(self):
|
||||
|
@ -186,15 +191,15 @@ CLASES DISPONIBLES:
|
|||
icon_path = script_dir / "icon.png"
|
||||
|
||||
if not icon_path.is_file():
|
||||
print(f"Advertencia: Archivo de ícono no encontrado en '{icon_path}'.")
|
||||
self.logger.warning(f"Archivo de ícono no encontrado en '{icon_path}'.")
|
||||
return
|
||||
|
||||
self.app_icon = tk.PhotoImage(file=str(icon_path))
|
||||
self.root.iconphoto(True, self.app_icon)
|
||||
except tk.TclError as e:
|
||||
print(f"Advertencia: No se pudo cargar el ícono desde '{icon_path}'. Error de Tkinter: {e}")
|
||||
self.logger.warning(f"No se pudo cargar el ícono desde '{icon_path}'. Error de Tkinter: {e}")
|
||||
except Exception as e:
|
||||
print(f"Advertencia: Ocurrió un error inesperado al cargar el ícono desde '{icon_path}': {e}")
|
||||
self.logger.warning(f"Ocurrió un error inesperado al cargar el ícono desde '{icon_path}': {e}", exc_info=True)
|
||||
|
||||
def _load_settings(self) -> Dict[str, Any]:
|
||||
"""Carga configuración de la aplicación"""
|
||||
|
@ -221,7 +226,7 @@ CLASES DISPONIBLES:
|
|||
json.dump(self.settings, f, indent=4, ensure_ascii=False)
|
||||
except Exception as e:
|
||||
if self.debug:
|
||||
print(f"Error guardando configuración: {e}")
|
||||
self.logger.error(f"Error guardando configuración: {e}", exc_info=True)
|
||||
|
||||
def update_symbolic_settings(self, symbolic_mode=None, show_numeric=None,
|
||||
keep_fractions=None, auto_simplify=None):
|
||||
|
@ -444,8 +449,8 @@ CLASES DISPONIBLES:
|
|||
if event and event.char == '.' and self.input_text.focus_get() == self.input_text:
|
||||
self._handle_dot_autocomplete()
|
||||
|
||||
# Evaluación con debounce
|
||||
self._debounce_job = self.root.after(300, self._evaluate_and_update)
|
||||
# Evaluación con debounce y auto-dimensionado
|
||||
self._debounce_job = self.root.after(300, self._process_input_and_adjust_layout)
|
||||
|
||||
def _handle_dot_autocomplete(self):
|
||||
"""Maneja el autocompletado cuando se escribe un punto - VERSIÓN DINÁMICA"""
|
||||
|
@ -456,7 +461,7 @@ CLASES DISPONIBLES:
|
|||
char_idx_after_dot = int(char_num_str)
|
||||
|
||||
if char_idx_after_dot == 0:
|
||||
print("DEBUG: Autocomplete: Cursor at beginning of line after dot. No action.")
|
||||
self.logger.debug("Autocomplete: Cursor at beginning of line after dot. No action.")
|
||||
return
|
||||
|
||||
dot_char_index_in_line = char_idx_after_dot - 1
|
||||
|
@ -466,7 +471,7 @@ CLASES DISPONIBLES:
|
|||
|
||||
# 1. Determinar si es un popup GLOBAL (usando contexto dinámico)
|
||||
if not stripped_text_before_dot:
|
||||
print("DEBUG: Dot on empty line or after spaces. Offering global suggestions.")
|
||||
self.logger.debug("Dot on empty line or after spaces. Offering global suggestions.")
|
||||
suggestions = []
|
||||
|
||||
# ========== USAR CONTEXTO DINÁMICO DEL REGISTRO ==========
|
||||
|
@ -485,12 +490,12 @@ CLASES DISPONIBLES:
|
|||
if helper_text:
|
||||
hint = helper_text.split('\n')[0]
|
||||
except Exception as e_helper:
|
||||
print(f"DEBUG: Error calling Helper for {name}: {e_helper}")
|
||||
self.logger.debug(f"Error calling Helper for {name}: {e_helper}")
|
||||
pass
|
||||
suggestions.append((name, hint))
|
||||
|
||||
except Exception as e:
|
||||
print(f"DEBUG: Error obteniendo contexto dinámico: {e}")
|
||||
self.logger.debug(f"Error obteniendo contexto dinámico: {e}")
|
||||
# Fallback básico
|
||||
suggestions = [("sin", "Función seno"), ("cos", "Función coseno")]
|
||||
|
||||
|
@ -503,7 +508,7 @@ CLASES DISPONIBLES:
|
|||
if fname not in current_suggestion_names:
|
||||
suggestions.append((fname, fhint))
|
||||
except Exception as e:
|
||||
print(f"DEBUG: Error calling SympyHelper.PopupFunctionList() for global: {e}")
|
||||
self.logger.debug(f"Error calling SympyHelper.PopupFunctionList() for global: {e}")
|
||||
|
||||
if suggestions:
|
||||
suggestions.sort(key=lambda x: x[0])
|
||||
|
@ -522,48 +527,48 @@ CLASES DISPONIBLES:
|
|||
if not obj_expr_str_candidate or \
|
||||
not re.match(r"^[a-zA-Z_0-9\(\)\[\]\.\"\'\+\-\*\/ ]*$", obj_expr_str_candidate) or \
|
||||
obj_expr_str_candidate.endswith(("+", "-", "*", "/", "(", ",")):
|
||||
print(f"DEBUG: Extracted expr '{obj_expr_str_candidate}' from '{stripped_text_before_dot}' not a valid object for dot autocomplete.")
|
||||
self.logger.debug(f"Extracted expr '{obj_expr_str_candidate}' from '{stripped_text_before_dot}' not a valid object for dot autocomplete.")
|
||||
return
|
||||
|
||||
obj_expr_str = obj_expr_str_candidate
|
||||
print(f"DEBUG: Autocomplete for object. Extracted: '{obj_expr_str}' from: '{text_on_line_up_to_dot}'")
|
||||
self.logger.debug(f"Autocomplete for object. Extracted: '{obj_expr_str}' from: '{text_on_line_up_to_dot}'")
|
||||
|
||||
if not obj_expr_str:
|
||||
print("DEBUG: Object expression is empty after extraction. No autocomplete.")
|
||||
self.logger.debug("Object expression is empty after extraction. No autocomplete.")
|
||||
return
|
||||
|
||||
# 3. Caso especial para el módulo sympy
|
||||
if obj_expr_str == "sympy":
|
||||
print(f"DEBUG: Detected 'sympy.', using SympyHelper for suggestions.")
|
||||
self.logger.debug(f"Detected 'sympy.', using SympyHelper for suggestions.")
|
||||
try:
|
||||
methods = SympyHelper.PopupFunctionList()
|
||||
if methods:
|
||||
self._show_autocomplete_popup(methods, is_global_popup=False)
|
||||
else:
|
||||
print(f"DEBUG: SympyHelper.PopupFunctionList returned no methods.")
|
||||
self.logger.debug(f"SympyHelper.PopupFunctionList returned no methods.")
|
||||
except Exception as e:
|
||||
print(f"DEBUG: Error calling SympyHelper.PopupFunctionList(): {e}")
|
||||
self.logger.debug(f"Error calling SympyHelper.PopupFunctionList(): {e}")
|
||||
return
|
||||
|
||||
# 4. Preprocesar con BracketParser
|
||||
if '[' in obj_expr_str:
|
||||
original_for_debug = obj_expr_str
|
||||
obj_expr_str = self.engine.parser._transform_brackets(obj_expr_str)
|
||||
if obj_expr_str != original_for_debug:
|
||||
print(f"DEBUG: Preprocessed by BracketParser: '{original_for_debug}' -> '{obj_expr_str}'")
|
||||
if obj_expr_str != original_for_debug and self.debug: # Solo loguear si self.debug es True
|
||||
self.logger.debug(f"Preprocessed by BracketParser: '{original_for_debug}' -> '{obj_expr_str}'")
|
||||
|
||||
# 5. Evaluar la expresión del objeto (usando contexto dinámico)
|
||||
eval_context = self.engine._get_full_context()
|
||||
obj = None
|
||||
try:
|
||||
if not obj_expr_str.strip():
|
||||
print("DEBUG: Object expression became empty before eval. No action.")
|
||||
self.logger.debug("Object expression became empty before eval. No action.")
|
||||
return
|
||||
print(f"DEBUG: Attempting to eval: '{obj_expr_str}'")
|
||||
self.logger.debug(f"Attempting to eval: '{obj_expr_str}'")
|
||||
obj = eval(obj_expr_str, eval_context)
|
||||
print(f"DEBUG: Eval successful. Object: {type(obj)}, Value: {obj}")
|
||||
self.logger.debug(f"Eval successful. Object: {type(obj)}, Value: {obj}")
|
||||
except Exception as e:
|
||||
print(f"DEBUG: Error evaluating object expression '{obj_expr_str}': {e}")
|
||||
self.logger.debug(f"Error evaluating object expression '{obj_expr_str}': {e}")
|
||||
return
|
||||
|
||||
# 6. Mostrar popup de autocompletado para el objeto
|
||||
|
@ -793,7 +798,7 @@ CLASES DISPONIBLES:
|
|||
|
||||
except Exception as e:
|
||||
if self.debug:
|
||||
print(f"DEBUG: Error en get_result_tag_dynamic: {e}")
|
||||
self.logger.debug(f"Error en get_result_tag_dynamic: {e}")
|
||||
|
||||
# Fallback a tags existentes para tipos no registrados
|
||||
if isinstance(result, sympy.Basic):
|
||||
|
@ -813,7 +818,7 @@ CLASES DISPONIBLES:
|
|||
|
||||
except Exception as e:
|
||||
if self.debug:
|
||||
print(f"DEBUG: Error en get_class_display_name_dynamic: {e}")
|
||||
self.logger.debug(f"Error en get_class_display_name_dynamic: {e}")
|
||||
|
||||
# Fallback a lógica existente para tipos nativos
|
||||
if isinstance(obj, sympy.logic.boolalg.BooleanAtom):
|
||||
|
@ -933,7 +938,7 @@ CLASES DISPONIBLES:
|
|||
|
||||
self.input_text.delete("1.0", tk.END)
|
||||
self.input_text.insert("1.0", content)
|
||||
self._evaluate_and_update()
|
||||
self._process_input_and_adjust_layout()
|
||||
|
||||
except Exception as e:
|
||||
messagebox.showerror("Error", f"No se pudo cargar el archivo:\n{e}")
|
||||
|
@ -1203,9 +1208,9 @@ programación y análisis numérico.
|
|||
self.input_text.insert("1.0", content)
|
||||
# Hacer evaluación inicial para mostrar resultados del historial
|
||||
# Esto mantiene el comportamiento de contexto limpio pero muestra resultados
|
||||
self.root.after_idle(self._evaluate_and_update)
|
||||
self.root.after_idle(self._process_input_and_adjust_layout)
|
||||
except Exception as e:
|
||||
print(f"Error cargando historial: {e}")
|
||||
self.logger.error(f"Error cargando historial: {e}", exc_info=True)
|
||||
|
||||
def save_history(self):
|
||||
"""Guarda historial de entrada"""
|
||||
|
@ -1217,7 +1222,7 @@ programación y análisis numérico.
|
|||
elif os.path.exists(self.HISTORY_FILE):
|
||||
os.remove(self.HISTORY_FILE)
|
||||
except Exception as e:
|
||||
print(f"Error guardando historial: {e}")
|
||||
self.logger.error(f"Error guardando historial: {e}", exc_info=True)
|
||||
|
||||
def on_close(self):
|
||||
"""Maneja cierre de la aplicación"""
|
||||
|
@ -1336,7 +1341,7 @@ programación y análisis numérico.
|
|||
|
||||
html_viewer.pack(padx=0, pady=0, fill=tk.BOTH, expand=True)
|
||||
except Exception as e:
|
||||
print(f"Error al renderizar Markdown a HTML: {e}")
|
||||
self.logger.error(f"Error al renderizar Markdown a HTML: {e}", exc_info=True)
|
||||
# Fallback to text if HTML fails
|
||||
self._show_text_help(help_win, readme_content)
|
||||
else:
|
||||
|
@ -1419,7 +1424,7 @@ Para crear un archivo de ayuda personalizado, cree un archivo `readme.md` en el
|
|||
if ayuda:
|
||||
return ayuda
|
||||
except Exception as e:
|
||||
print(f"DEBUG: Error en helper: {e}")
|
||||
self.logger.debug(f"Error en helper: {e}")
|
||||
continue
|
||||
return None
|
||||
|
||||
|
@ -1460,6 +1465,123 @@ Para crear un archivo de ayuda personalizado, cree un archivo `readme.md` en el
|
|||
|
||||
return f"{mode}{numeric_indicator}{fractions_indicator}"
|
||||
|
||||
def _get_input_font(self):
|
||||
"""Obtiene o crea y cachea el objeto tk.Font para el panel de entrada."""
|
||||
if not self._cached_input_font:
|
||||
# Asume la fuente configurada en create_widgets: ("Consolas", 11)
|
||||
self._cached_input_font = tkFont.Font(family="Consolas", size=11)
|
||||
return self._cached_input_font
|
||||
|
||||
def _adjust_input_pane_width(self):
|
||||
"""Ajusta el ancho del panel de entrada según su contenido."""
|
||||
if not hasattr(self, 'paned_window') or not self.paned_window.winfo_exists():
|
||||
return
|
||||
|
||||
# Esperar a que la ventana tenga un tamaño válido
|
||||
if self.paned_window.winfo_width() <= 1:
|
||||
return # Se reintentará en la siguiente llamada (ej. por KeyRelease)
|
||||
|
||||
# Obtener contenido excluyendo el último newline automático del widget Text
|
||||
input_content = self.input_text.get("1.0", f"{tk.END}-1c")
|
||||
lines = input_content.splitlines()
|
||||
input_font = self._get_input_font()
|
||||
|
||||
max_pixel_width = 0
|
||||
if not input_content.strip(): # Si está vacío o solo espacios en blanco
|
||||
max_pixel_width = 5 # Ancho mínimo para el cursor o como placeholder
|
||||
else:
|
||||
for line in lines:
|
||||
measured_width = input_font.measure(line) if line.strip() else input_font.measure(" ")
|
||||
if measured_width > max_pixel_width:
|
||||
max_pixel_width = measured_width
|
||||
|
||||
padding = 40 # Relleno para barra de desplazamiento, márgenes, etc.
|
||||
width_needed_by_text = max_pixel_width + padding
|
||||
|
||||
# Debugging opcional (descomenta si necesitas depurar)
|
||||
if self.debug:
|
||||
self.logger.debug(f"--- Adjusting Input Pane ---")
|
||||
self.logger.debug(f"Input content: '{input_content[:50]}...'")
|
||||
self.logger.debug(f"Max pixel width of text: {max_pixel_width}")
|
||||
self.logger.debug(f"Width needed by text (max_pixel_width + padding): {width_needed_by_text}")
|
||||
|
||||
min_input_pane_width = 200 # Definido en create_widgets
|
||||
min_output_pane_width = 200 # Definido en create_widgets
|
||||
total_width = self.paned_window.winfo_width()
|
||||
|
||||
current_sash_pos = 0
|
||||
try:
|
||||
sash_coords = self.paned_window.sash_coord(0)
|
||||
if sash_coords:
|
||||
current_sash_pos = sash_coords[0]
|
||||
else:
|
||||
if self.debug:
|
||||
self.logger.debug("Could not get sash_coord.")
|
||||
return
|
||||
except tk.TclError:
|
||||
if self.debug:
|
||||
self.logger.debug("TclError getting sash_coord.")
|
||||
return
|
||||
|
||||
if self.debug:
|
||||
self.logger.debug(f"Current sash position (input pane width): {current_sash_pos}")
|
||||
|
||||
if width_needed_by_text > current_sash_pos:
|
||||
if self.debug:
|
||||
self.logger.debug(f"Condition MET: Text needs more space ({width_needed_by_text} > {current_sash_pos})")
|
||||
new_input_width = width_needed_by_text # Punto de partida
|
||||
|
||||
# Asegurar que el nuevo ancho no sea menor que el mínimo del panel de entrada
|
||||
new_input_width = max(new_input_width, min_input_pane_width)
|
||||
|
||||
# Asegurar que el panel de salida conserve su espacio mínimo
|
||||
if total_width - new_input_width < min_output_pane_width:
|
||||
new_input_width = total_width - min_output_pane_width
|
||||
new_input_width = max(new_input_width, min_input_pane_width) # Re-verificar mínimo del input
|
||||
|
||||
# Aplicar un ratio máximo para el panel de entrada
|
||||
max_input_ratio = 0.75 # Podría ser una constante de clase
|
||||
max_width_by_ratio = int(total_width * max_input_ratio)
|
||||
|
||||
if new_input_width > max_width_by_ratio:
|
||||
if max_width_by_ratio >= min_input_pane_width and \
|
||||
(total_width - max_width_by_ratio) >= min_output_pane_width:
|
||||
new_input_width = max_width_by_ratio
|
||||
|
||||
final_new_input_width = max(0, int(new_input_width)) # No debe ser negativo
|
||||
if self.debug:
|
||||
self.logger.debug(f"Calculated final new input width: {final_new_input_width}")
|
||||
|
||||
# Mover el sash solo si el nuevo ancho es significativamente mayor que el actual (umbral ajustado)
|
||||
sash_adjustment_threshold = 3 # Píxeles
|
||||
if final_new_input_width > current_sash_pos and \
|
||||
(final_new_input_width - current_sash_pos) >= sash_adjustment_threshold:
|
||||
if self.debug:
|
||||
self.logger.debug(f"Condition MET for sash_place: New width {final_new_input_width} is significantly larger (diff >= {sash_adjustment_threshold}).")
|
||||
try:
|
||||
if self.paned_window.winfo_exists() and total_width >= (min_input_pane_width + min_output_pane_width):
|
||||
self.paned_window.sash_place(0, final_new_input_width, 0) # Añadido el argumento y=0
|
||||
if self.debug:
|
||||
self.logger.debug(f"Sash placed at: {final_new_input_width}")
|
||||
elif self.debug:
|
||||
self.logger.debug("Paned window not ready or total width too small for sash_place.")
|
||||
except tk.TclError as e_sash:
|
||||
if self.debug:
|
||||
self.logger.debug(f"TclError during sash_place: {e_sash}")
|
||||
pass
|
||||
elif self.debug:
|
||||
self.logger.debug(f"Condition NOT MET for sash_place: final_new_input_width ({final_new_input_width}) vs current_sash_pos ({current_sash_pos}) or threshold ({sash_adjustment_threshold}).")
|
||||
elif self.debug:
|
||||
self.logger.debug(f"Condition NOT MET: Text does not need more space ({width_needed_by_text} <= {current_sash_pos})")
|
||||
|
||||
if self.debug:
|
||||
self.logger.debug(f"--- End Adjusting Input Pane ---")
|
||||
|
||||
def _process_input_and_adjust_layout(self):
|
||||
"""Evalúa todas las líneas y luego ajusta el ancho del panel de entrada."""
|
||||
self._evaluate_and_update()
|
||||
self._adjust_input_pane_width()
|
||||
|
||||
|
||||
def main():
|
||||
"""Función principal"""
|
||||
|
|
|
@ -343,10 +343,10 @@ class HybridEvaluationEngine:
|
|||
try:
|
||||
numeric_eval = result.evalf()
|
||||
# Solo mostrar evaluación numérica si es diferente del resultado simbólico
|
||||
if numeric_eval != result and not (
|
||||
hasattr(result, 'is_number') and result.is_number and
|
||||
abs(float(numeric_eval) - float(result)) < 1e-15
|
||||
):
|
||||
if (str(numeric_eval) != str(result) and numeric_eval != result and
|
||||
not (isinstance(result, (int, float)) or
|
||||
(hasattr(result, 'is_number') and result.is_number and
|
||||
hasattr(result, 'is_Integer') and result.is_Integer))):
|
||||
numeric_result = numeric_eval
|
||||
except:
|
||||
pass
|
||||
|
|
Loading…
Reference in New Issue