diff --git a/.data/history.txt b/.data/history.txt index 54917f6..c3e68d6 100644 --- a/.data/history.txt +++ b/.data/history.txt @@ -36,6 +36,4 @@ hacer_por_dia = faltan/dias_faltan # Minimo para terminar promedio_ideal = total/(dias_pasados+dias_faltan) -h = Today() -f = Date('23/2/25') + x -x=? + diff --git a/app/gui_menus.py b/app/gui_menus.py index 9d6b556..3d74f10 100644 --- a/app/gui_menus.py +++ b/app/gui_menus.py @@ -1,12 +1,11 @@ """ Sistema de Menús y Diálogos para la Calculadora MAV CAS Híbrida """ + import os import time from pathlib import Path -from PySide6.QtWidgets import ( - QMenuBar, QMenu, QMessageBox, QFileDialog -) +from PySide6.QtWidgets import QMenuBar, QMenu, QMessageBox, QFileDialog from PySide6.QtGui import QAction, QKeySequence from PySide6.QtCore import QTimer from PySide6.QtWidgets import QApplication @@ -14,224 +13,272 @@ from PySide6.QtWidgets import QApplication class MenuManager: """Gestor del sistema de menús""" - + def __init__(self, main_window): self.main_window = main_window self.logger = main_window.logger - + def setup_menu(self): """Configura el menú completo""" menubar = self.main_window.menuBar() - + # Menú Archivo file_menu = menubar.addMenu("Archivo") - + new_action = QAction("Nuevo", self.main_window) new_action.setShortcut(QKeySequence.New) new_action.triggered.connect(self.new_session) file_menu.addAction(new_action) - + file_menu.addSeparator() - + load_action = QAction("Cargar...", self.main_window) load_action.setShortcut(QKeySequence.Open) load_action.triggered.connect(self.load_file) file_menu.addAction(load_action) - + save_action = QAction("Guardar como...", self.main_window) save_action.setShortcut(QKeySequence.Save) save_action.triggered.connect(self.save_file) file_menu.addAction(save_action) - + + # Menú de archivos recientes + self.recent_files_menu = file_menu.addMenu("Archivos recientes") + self._update_recent_files_menu() + file_menu.addSeparator() - + exit_action = QAction("Salir", self.main_window) exit_action.triggered.connect(self.main_window.close) file_menu.addAction(exit_action) - + # Menú Editar edit_menu = menubar.addMenu("Editar") - + clear_input_action = QAction("Limpiar entrada", self.main_window) clear_input_action.triggered.connect(self.clear_input) edit_menu.addAction(clear_input_action) - + clear_output_action = QAction("Limpiar salida", self.main_window) clear_output_action.triggered.connect(self.clear_output) edit_menu.addAction(clear_output_action) - + edit_menu.addSeparator() - + clear_history_action = QAction("Limpiar historial", self.main_window) clear_history_action.triggered.connect(self.clear_history) edit_menu.addAction(clear_history_action) - + # Menú Ver view_menu = menubar.addMenu("Ver") - + toggle_latex_action = QAction("📐 Panel LaTeX", self.main_window) toggle_latex_action.setShortcut(QKeySequence("F12")) toggle_latex_action.triggered.connect(self.main_window._toggle_latex_panel) view_menu.addAction(toggle_latex_action) - + view_menu.addSeparator() - + # Submenu de fuentes font_menu = view_menu.addMenu("Tamaño de fuente") - + font_small_action = QAction("Pequeña (11px)", self.main_window) font_small_action.setCheckable(True) font_small_action.triggered.connect(lambda: self.set_font_size(False)) font_menu.addAction(font_small_action) - + font_large_action = QAction("Grande (14px)", self.main_window) font_large_action.setCheckable(True) font_large_action.triggered.connect(lambda: self.set_font_size(True)) font_menu.addAction(font_large_action) - + # Marcar el tamaño actual - is_large = self.main_window.settings_manager.get_setting("font_size_large", False) + is_large = self.main_window.settings_manager.get_setting( + "font_size_large", False + ) if is_large: font_large_action.setChecked(True) else: font_small_action.setChecked(True) - + self.font_small_action = font_small_action self.font_large_action = font_large_action - + view_menu.addSeparator() - + system_info_action = QAction("Información del sistema", self.main_window) system_info_action.triggered.connect(self.show_types_info) view_menu.addAction(system_info_action) - + # Menú Herramientas tools_menu = menubar.addMenu("Herramientas") - + reload_types_action = QAction("Recargar Tipos Personalizados", self.main_window) reload_types_action.triggered.connect(self.reload_types) tools_menu.addAction(reload_types_action) - + tools_menu.addSeparator() - + # Menú de diagnóstico diag_menu = tools_menu.addMenu("Diagnóstico") - + mathjax_diag_action = QAction("🔍 Diagnóstico MathJax", self.main_window) mathjax_diag_action.triggered.connect(self._diagnose_mathjax) diag_menu.addAction(mathjax_diag_action) - + latex_status_action = QAction("📊 Estado Panel LaTeX", self.main_window) latex_status_action.triggered.connect(self._show_latex_panel_status) diag_menu.addAction(latex_status_action) - + save_html_action = QAction("💾 Guardar HTML del Panel LaTeX", self.main_window) save_html_action.triggered.connect(self._save_latex_html) diag_menu.addAction(save_html_action) - + diag_menu.addSeparator() - + copy_debug_action = QAction("📋 Copiar Debug al Portapapeles", self.main_window) copy_debug_action.setShortcut(QKeySequence("Ctrl+Shift+C")) copy_debug_action.triggered.connect(self._copy_debug_to_clipboard) diag_menu.addAction(copy_debug_action) - + # Menú Tipos types_menu = menubar.addMenu("Tipos") - + types_info_action = QAction("Información de tipos", self.main_window) types_info_action.triggered.connect(self.show_types_info) types_menu.addAction(types_info_action) - + types_menu.addSeparator() - + types_syntax_action = QAction("Sintaxis de tipos", self.main_window) types_syntax_action.triggered.connect(self.show_types_syntax) types_menu.addAction(types_syntax_action) - + # Menú Ayuda help_menu = menubar.addMenu("Ayuda") - + quick_guide_action = QAction("Guía rápida", self.main_window) quick_guide_action.triggered.connect(self.show_quick_guide) help_menu.addAction(quick_guide_action) - + syntax_help_action = QAction("Sintaxis", self.main_window) syntax_help_action.triggered.connect(self.show_syntax_help) help_menu.addAction(syntax_help_action) - + sympy_funcs_action = QAction("Funciones SymPy", self.main_window) sympy_funcs_action.triggered.connect(self.show_sympy_functions) help_menu.addAction(sympy_funcs_action) - + help_menu.addSeparator() - + about_action = QAction("Acerca de", self.main_window) about_action.triggered.connect(self.show_about) help_menu.addAction(about_action) - + # ========== FUNCIONES DE MENÚ ARCHIVO ========== - + def new_session(self): """Inicia una nueva sesión""" self.clear_input() self.clear_output() self.main_window.latex_panel.clear_equations() - if hasattr(self.main_window, '_latex_equations'): + if hasattr(self.main_window, "_latex_equations"): self.main_window._latex_equations.clear() self.main_window._update_status("✨ Nueva sesión iniciada") - + def load_file(self): """Carga archivo en el editor""" filepath, _ = QFileDialog.getOpenFileName( - self.main_window, "Cargar archivo", "", - "Archivos de texto (*.txt);;Archivos Python (*.py);;Todos los archivos (*.*)" + self.main_window, + "Cargar archivo", + "", + "Archivos de texto (*.txt);;Archivos Python (*.py);;Todos los archivos (*.*)", ) - + if filepath: - try: - with open(filepath, "r", encoding="utf-8") as f: - content = f.read() - - self.main_window.input_text.setPlainText(content) - self.main_window._evaluate_and_update() - self.main_window._update_status(f"📁 Archivo cargado: {Path(filepath).name}") - - except Exception as e: - QMessageBox.critical(self.main_window, "Error", f"No se pudo cargar el archivo:\n{e}") - + self._load_file_content(filepath) + + def _load_file_content(self, filepath: str): + """Carga el contenido de un archivo en el editor.""" + try: + with open(filepath, "r", encoding="utf-8") as f: + content = f.read() + + self.main_window.input_text.setPlainText(content) + self.main_window._evaluate_and_update() + self.main_window._update_status( + f"📁 Archivo cargado: {Path(filepath).name}" + ) + + # Añadir a recientes y actualizar menú + self.main_window.settings_manager.add_recent_file(filepath) + self._update_recent_files_menu() + + except Exception as e: + QMessageBox.critical( + self.main_window, "Error", f"No se pudo cargar el archivo:\n{e}" + ) + def save_file(self): """Guarda contenido del editor""" filepath, _ = QFileDialog.getSaveFileName( - self.main_window, "Guardar archivo", "", - "Archivos de texto (*.txt);;Archivos Python (*.py);;Todos los archivos (*.*)" + self.main_window, + "Guardar archivo", + "", + "Archivos de texto (*.txt);;Archivos Python (*.py);;Todos los archivos (*.*)", ) - + if filepath: try: content = self.main_window.input_text.toPlainText() with open(filepath, "w", encoding="utf-8") as f: f.write(content) - - self.main_window._update_status(f"💾 Archivo guardado: {Path(filepath).name}") - + + self.main_window._update_status( + f"💾 Archivo guardado: {Path(filepath).name}" + ) + + # Añadir a recientes y actualizar menú + self.main_window.settings_manager.add_recent_file(filepath) + self._update_recent_files_menu() + except Exception as e: - QMessageBox.critical(self.main_window, "Error", f"No se pudo guardar el archivo:\n{e}") - + QMessageBox.critical( + self.main_window, "Error", f"No se pudo guardar el archivo:\n{e}" + ) + + def _update_recent_files_menu(self): + """Actualiza el menú de archivos recientes""" + self.recent_files_menu.clear() + recent_files = self.main_window.settings_manager.get_setting("recent_files", []) + + if not recent_files: + empty_action = QAction("(Vacío)", self.main_window) + empty_action.setEnabled(False) + self.recent_files_menu.addAction(empty_action) + return + + for filepath in recent_files: + action = QAction(filepath, self.main_window) + action.triggered.connect( + lambda checked=False, path=filepath: self._load_file_content(path) + ) + self.recent_files_menu.addAction(action) + # ========== FUNCIONES DE MENÚ EDITAR ========== - + def clear_input(self): """Limpia panel de entrada""" self.main_window.input_text.clear() self.main_window._clear_output() - + def clear_output(self): """Limpia panel de salida y LaTeX""" self.main_window._clear_output() self.main_window.latex_panel.clear_equations() - if hasattr(self.main_window, '_latex_equations'): + if hasattr(self.main_window, "_latex_equations"): self.main_window._latex_equations.clear() - + def clear_history(self): """Limpia el archivo de historial""" try: @@ -239,24 +286,26 @@ class MenuManager: os.remove(self.main_window.HISTORY_FILE) self.main_window._update_status("✓ Historial limpiado") except Exception as e: - QMessageBox.critical(self.main_window, "Error", f"No se pudo limpiar el historial:\n{e}") - + QMessageBox.critical( + self.main_window, "Error", f"No se pudo limpiar el historial:\n{e}" + ) + def set_font_size(self, is_large: bool): """Cambia el tamaño de fuente de los paneles""" # Guardar configuración self.main_window.settings_manager.set_setting("font_size_large", is_large) - + # Actualizar checkboxes self.font_small_action.setChecked(not is_large) self.font_large_action.setChecked(is_large) - + # Aplicar nuevo tamaño font_size = 14 if is_large else 11 self._apply_font_size_to_panels(font_size) - + status_text = f"🔤 Fuente cambiada a {'grande' if is_large else 'pequeña'} ({font_size}px)" self.main_window._update_status(status_text) - + def _apply_font_size_to_panels(self, font_size: int): """Aplica el tamaño de fuente a los paneles de texto""" style_update = f""" @@ -264,26 +313,26 @@ class MenuManager: font-size: {font_size}px; }} """ - + # Actualizar estilo de los paneles principales current_style = self.main_window.styleSheet() # Reemplazar solo la parte de font-size en QPlainTextEdit y QTextEdit import re - + # Patrón para encontrar y reemplazar font-size en QPlainTextEdit y QTextEdit - pattern = r'(QPlainTextEdit, QTextEdit\s*\{[^}]*?)font-size:\s*\d+px;([^}]*\})' - replacement = rf'\1font-size: {font_size}px;\2' - + pattern = r"(QPlainTextEdit, QTextEdit\s*\{[^}]*?)font-size:\s*\d+px;([^}]*\})" + replacement = rf"\1font-size: {font_size}px;\2" + new_style = re.sub(pattern, replacement, current_style) - + # Si no se encontró el patrón, agregar el estilo if new_style == current_style: new_style += style_update - + self.main_window.setStyleSheet(new_style) - + # ========== FUNCIONES DE MENÚ HERRAMIENTAS ========== - + def reload_types(self): """Recarga el sistema de tipos""" try: @@ -293,13 +342,15 @@ class MenuManager: self.main_window._update_status("✓ Sistema de tipos recargado") except Exception as e: self.logger.error(f"Error recargando tipos: {e}") - QMessageBox.critical(self.main_window, "Error", f"Error recargando tipos:\n{e}") - + QMessageBox.critical( + self.main_window, "Error", f"Error recargando tipos:\n{e}" + ) + def show_types_info(self): """Muestra información sobre tipos disponibles""" try: context_info = self.main_window.engine.get_context_info() - + info_text = f"""INFORMACIÓN DEL SISTEMA ALGEBRAICO PURO Ecuaciones en el sistema: {context_info.get('equations', 0)} @@ -313,39 +364,58 @@ CARACTERÍSTICAS: • Evaluación numérica inteligente • Atajo x=? equivale a solve(x) """ - + self._show_info_dialog("Información del Sistema", info_text) - + except Exception as e: - QMessageBox.critical(self.main_window, "Error", f"Error obteniendo información:\n{e}") - + QMessageBox.critical( + self.main_window, "Error", f"Error obteniendo información:\n{e}" + ) + def show_types_syntax(self): """Muestra sintaxis de tipos disponibles""" try: types_info = self.main_window.engine.get_available_types() syntax_text = "SINTAXIS DE TIPOS DISPONIBLES\n\n" - + # Aquí iría el código para mostrar sintaxis # Similar al original pero adaptado para PySide6 - + self._show_info_dialog("Sintaxis de Tipos", syntax_text) - + except Exception as e: - QMessageBox.critical(self.main_window, "Error", f"Error obteniendo sintaxis:\n{e}") - + QMessageBox.critical( + self.main_window, "Error", f"Error obteniendo sintaxis:\n{e}" + ) + # ========== FUNCIONES DE DIAGNÓSTICO ========== - + def _diagnose_mathjax(self): """Ejecuta diagnóstico de MathJax""" - if not hasattr(self.main_window.latex_panel, '_webview_available') or not self.main_window.latex_panel._webview_available: - QMessageBox.warning(self.main_window, "Diagnóstico", "Panel LaTeX no usa WebEngine (usando fallback)") + if ( + not hasattr(self.main_window.latex_panel, "_webview_available") + or not self.main_window.latex_panel._webview_available + ): + QMessageBox.warning( + self.main_window, + "Diagnóstico", + "Panel LaTeX no usa WebEngine (usando fallback)", + ) return - + # Aquí iría el código de diagnóstico # Por ahora solo mostrar estado - status = "WebEngine disponible" if self.main_window.latex_panel._webview_available else "Usando fallback HTML" - equations = len(self.main_window._latex_equations) if hasattr(self.main_window, '_latex_equations') else 0 - + status = ( + "WebEngine disponible" + if self.main_window.latex_panel._webview_available + else "Usando fallback HTML" + ) + equations = ( + len(self.main_window._latex_equations) + if hasattr(self.main_window, "_latex_equations") + else 0 + ) + info = f"""DIAGNÓSTICO MATHJAX Estado: {status} @@ -355,16 +425,22 @@ Panel visible: {self.main_window.latex_panel_visible} Para depuración completa, revise la consola del navegador en el WebEngineView. """ - + self._show_info_dialog("Diagnóstico MathJax", info) - + def _show_latex_panel_status(self): """Muestra estado del panel LaTeX""" - panel_exists = hasattr(self.main_window, 'latex_panel') + panel_exists = hasattr(self.main_window, "latex_panel") panel_visible = self.main_window.latex_panel_visible if panel_exists else False - webview_available = self.main_window.latex_panel._webview_available if panel_exists else False - equations_count = len(self.main_window._latex_equations) if hasattr(self.main_window, '_latex_equations') else 0 - + webview_available = ( + self.main_window.latex_panel._webview_available if panel_exists else False + ) + equations_count = ( + len(self.main_window._latex_equations) + if hasattr(self.main_window, "_latex_equations") + else 0 + ) + status_message = f"""ESTADO DEL PANEL LATEX COMPONENTES: @@ -381,24 +457,27 @@ PARA SOLUCIONAR: 2. Si WebEngine no está disponible: → Instalar con: pip install PySide6-WebEngine """ - + self._show_info_dialog("Estado Panel LaTeX", status_message) - + def _save_latex_html(self): """Guarda el HTML del panel LaTeX para diagnóstico""" try: # Obtener HTML del panel LaTeX latex_panel = self.main_window.latex_panel - - if hasattr(latex_panel, '_webview_available') and latex_panel._webview_available: + + if ( + hasattr(latex_panel, "_webview_available") + and latex_panel._webview_available + ): # Panel con WebEngine - if hasattr(latex_panel, 'webview') and latex_panel._webview_initialized: + if hasattr(latex_panel, "webview") and latex_panel._webview_initialized: # Obtener HTML actual del WebView def on_html_received(html_content): self._save_html_to_file(html_content, "webview_current") - + latex_panel.webview.page().toHtml(on_html_received) - + # También guardar el HTML base generado base_html = latex_panel._generate_mathjax_html() self._save_html_to_file(base_html, "webview_base") @@ -408,66 +487,71 @@ PARA SOLUCIONAR: self._save_html_to_file(base_html, "webview_not_initialized") else: # Panel con fallback text browser - if hasattr(latex_panel, 'text_browser'): + if hasattr(latex_panel, "text_browser"): fallback_html = latex_panel.text_browser.toHtml() self._save_html_to_file(fallback_html, "fallback_textbrowser") else: - self._save_html_to_file("No hay contenido HTML disponible", "no_content") - + self._save_html_to_file( + "No hay contenido HTML disponible", "no_content" + ) + QMessageBox.information( - self.main_window, - "HTML Guardado", - "El HTML del panel LaTeX ha sido guardado en ./debug_html/ para diagnóstico." + self.main_window, + "HTML Guardado", + "El HTML del panel LaTeX ha sido guardado en ./debug_html/ para diagnóstico.", ) - + except Exception as e: self.logger.error(f"Error guardando HTML del panel LaTeX: {e}") QMessageBox.critical( - self.main_window, - "Error", - f"Error guardando HTML: {e}" + self.main_window, "Error", f"Error guardando HTML: {e}" ) - + def _save_html_to_file(self, html_content, file_suffix): """Guarda contenido HTML a un archivo""" # Crear directorio de debug si no existe debug_dir = Path("./debug_html") debug_dir.mkdir(exist_ok=True) - + # Generar nombre de archivo con timestamp timestamp = time.strftime("%Y%m%d_%H%M%S") filename = f"latex_panel_{file_suffix}_{timestamp}.html" filepath = debug_dir / filename - + try: with open(filepath, "w", encoding="utf-8") as f: f.write(html_content) - + self.logger.info(f"HTML guardado en: {filepath}") - + except Exception as e: self.logger.error(f"Error escribiendo archivo HTML: {e}") - + def _copy_debug_to_clipboard(self): """Copia información de debug completa al portapapeles""" try: # Obtener contenido de entrada input_content = self.main_window.input_text.toPlainText() - + # Obtener contenido de salida (texto plano) output_content = self.main_window.output_text.toPlainText() - + # Obtener información del sistema context_info = self.main_window.engine.get_context_info() - + # Obtener ecuaciones LaTeX si están disponibles latex_equations = "" - if hasattr(self.main_window, '_latex_equations') and self.main_window._latex_equations: - latex_equations = "\\n".join([ - f"[{eq['type']}] {eq['content']}" - for eq in self.main_window._latex_equations - ]) - + if ( + hasattr(self.main_window, "_latex_equations") + and self.main_window._latex_equations + ): + latex_equations = "\\n".join( + [ + f"[{eq['type']}] {eq['content']}" + for eq in self.main_window._latex_equations + ] + ) + # Crear reporte de debug completo debug_report = f"""=== REPORTE DEBUG CALCULADORA MAV === Timestamp: {time.strftime('%Y-%m-%d %H:%M:%S')} @@ -493,20 +577,26 @@ MathJax listo: {getattr(self.main_window.latex_panel, '_mathjax_ready', False)} Panel LaTeX visible: {self.main_window.latex_panel_visible} === FIN REPORTE ===""" - + # Copiar al portapapeles clipboard = QApplication.clipboard() clipboard.setText(debug_report) - + # Mostrar confirmación - self.main_window._update_status("📋 Información de debug copiada al portapapeles", 3000) - + self.main_window._update_status( + "📋 Información de debug copiada al portapapeles", 3000 + ) + except Exception as e: self.logger.error(f"Error copiando debug: {e}") - QMessageBox.critical(self.main_window, "Error", f"Error copiando debug al portapapeles:\\n{e}") - + QMessageBox.critical( + self.main_window, + "Error", + f"Error copiando debug al portapapeles:\\n{e}", + ) + # ========== FUNCIONES DE MENÚ AYUDA ========== - + def show_quick_guide(self): """Muestra guía rápida""" guide = """# Calculadora MAV - CAS Híbrido @@ -543,9 +633,9 @@ El sistema detecta automáticamente tipos disponibles en custom_types/ - Escriba "." después de cualquier objeto para ver métodos - El sistema usa los tipos registrados automáticamente """ - + self._show_info_dialog("Guía Rápida", guide) - + def show_syntax_help(self): """Muestra ayuda de sintaxis""" syntax = """# Sintaxis del CAS Híbrido @@ -575,9 +665,9 @@ variable=? # Atajo para solve(variable) x = valor # Crea Symbol('x') expresión # Evaluación simbólica automática """ - + self._show_info_dialog("Sintaxis", syntax) - + def show_sympy_functions(self): """Muestra funciones SymPy disponibles""" functions = """# Funciones SymPy Disponibles @@ -613,9 +703,9 @@ plot3d(expr, (x, x1, x2), (y, y1, y2)) ## Constantes pi, E, I (imaginario), oo (infinito) """ - + self._show_info_dialog("Funciones SymPy", functions) - + def show_about(self): """Muestra información sobre la aplicación""" about = """Calculadora MAV - CAS Híbrido @@ -638,9 +728,9 @@ Desarrollado para cálculo matemático avanzado con soporte especializado para redes, programación y análisis numérico. """ - + QMessageBox.about(self.main_window, "Acerca de", about) - + def _show_info_dialog(self, title: str, content: str): """Muestra diálogo de información con scroll""" dialog = QMessageBox(self.main_window) @@ -648,4 +738,4 @@ programación y análisis numérico. dialog.setIcon(QMessageBox.Information) dialog.setText(content[:200] + "..." if len(content) > 200 else content) dialog.setDetailedText(content) - dialog.exec() \ No newline at end of file + dialog.exec() diff --git a/app/gui_settings.py b/app/gui_settings.py index ca7781b..abf84e4 100644 --- a/app/gui_settings.py +++ b/app/gui_settings.py @@ -1,6 +1,7 @@ """ Sistema de Configuración y Persistencia para la Calculadora MAV CAS Híbrida """ + import os import json import logging @@ -10,15 +11,15 @@ from PySide6.QtCore import QTimer class SettingsManager: """Gestor de configuración y persistencia""" - + SETTINGS_FILE = "./.data/settings.json" HISTORY_FILE = "./.data/history.txt" - + def __init__(self, main_window): self.main_window = main_window self.logger = logging.getLogger(__name__) self.settings = self._load_settings() - + def _load_settings(self) -> Dict[str, Any]: """Carga configuración de la aplicación""" if os.path.exists(self.SETTINGS_FILE): @@ -33,37 +34,38 @@ class SettingsManager: "debug_mode": False, "latex_panel_visible": False, "latex_panel_width": 300, - "font_size_large": False # False = pequeña (11px), True = grande (14px) + "font_size_large": False, # False = pequeña (11px), True = grande (14px) + "recent_files": [], } - + def _save_settings(self): """Guarda configuraciones""" try: # Crear directorio si no existe os.makedirs(os.path.dirname(self.SETTINGS_FILE), exist_ok=True) - + # Guardar geometría geometry = self.main_window.geometry() self.settings["window_geometry"] = { "x": geometry.x(), "y": geometry.y(), "width": geometry.width(), - "height": geometry.height() + "height": geometry.height(), } - + # Guardar tamaños del splitter - if hasattr(self.main_window, 'main_splitter'): + if hasattr(self.main_window, "main_splitter"): self.settings["splitter_sizes"] = self.main_window.main_splitter.sizes() - + self.settings["latex_panel_visible"] = self.main_window.latex_panel_visible - self.settings["debug_mode"] = getattr(self.main_window, 'debug', False) - + self.settings["debug_mode"] = getattr(self.main_window, "debug", False) + with open(self.SETTINGS_FILE, "w", encoding="utf-8") as f: json.dump(self.settings, f, indent=4, ensure_ascii=False) - + except Exception as e: self.logger.error(f"Error guardando configuración: {e}") - + def _load_history(self): """Carga historial de entrada""" try: @@ -76,13 +78,13 @@ class SettingsManager: QTimer.singleShot(100, self.main_window._evaluate_and_update) except Exception as e: self.logger.error(f"Error cargando historial: {e}") - + def _save_history(self): """Guarda historial de entrada""" try: # Crear directorio si no existe os.makedirs(os.path.dirname(self.HISTORY_FILE), exist_ok=True) - + content = self.main_window.input_text.toPlainText() if content: with open(self.HISTORY_FILE, "w", encoding="utf-8") as f: @@ -91,36 +93,56 @@ class SettingsManager: os.remove(self.HISTORY_FILE) except Exception as e: self.logger.error(f"Error guardando historial: {e}") - + def _restore_geometry(self): """Restaura geometría guardada""" try: geom = self.settings.get("window_geometry") if geom and isinstance(geom, dict): - self.main_window.setGeometry(geom["x"], geom["y"], geom["width"], geom["height"]) - + self.main_window.setGeometry( + geom["x"], geom["y"], geom["width"], geom["height"] + ) + # Restaurar splitter sizes = self.settings.get("splitter_sizes") - if sizes and hasattr(self.main_window, 'main_splitter'): + if sizes and hasattr(self.main_window, "main_splitter"): self.main_window.main_splitter.setSizes(sizes) - + except Exception as e: self.logger.warning(f"No se pudo restaurar geometría: {e}") - + def initialize(self): """Inicializa la configuración cargando datos""" self._load_history() self._restore_geometry() - + def save_all(self): """Guarda toda la configuración""" self._save_settings() self._save_history() - + def get_setting(self, key: str, default=None): """Obtiene un valor de configuración""" return self.settings.get(key, default) - + def set_setting(self, key: str, value): """Establece un valor de configuración""" - self.settings[key] = value \ No newline at end of file + self.settings[key] = value + + def add_recent_file(self, filepath: str): + """Añade un archivo a la lista de recientes y guarda.""" + try: + recent_files = self.get_setting("recent_files", []) + + # Si el archivo ya está, lo eliminamos para volver a añadirlo al principio + if filepath in recent_files: + recent_files.remove(filepath) + + # Añadir al principio de la lista + recent_files.insert(0, filepath) + + # Limitar la lista a 10 elementos + self.set_setting("recent_files", recent_files[:10]) + + except Exception as e: + self.logger.error(f"Error añadiendo archivo reciente: {e}")