Optimización del sistema de menús en la interfaz gráfica, incluyendo la adición de un menú para archivos recientes y mejoras en la gestión de archivos. Se refactorizan las funciones de carga y guardado de archivos para incluir la actualización de la lista de archivos recientes. Se mejora la legibilidad del código eliminando espacios innecesarios y ajustando la estructura de las funciones.

This commit is contained in:
Miguel 2025-06-28 17:10:00 +02:00
parent 0b5a2579d5
commit 7ce08833e8
3 changed files with 304 additions and 194 deletions

View File

@ -36,6 +36,4 @@ hacer_por_dia = faltan/dias_faltan
# Minimo para terminar # Minimo para terminar
promedio_ideal = total/(dias_pasados+dias_faltan) promedio_ideal = total/(dias_pasados+dias_faltan)
h = Today()
f = Date('23/2/25') + x
x=?

View File

@ -1,12 +1,11 @@
""" """
Sistema de Menús y Diálogos para la Calculadora MAV CAS Híbrida Sistema de Menús y Diálogos para la Calculadora MAV CAS Híbrida
""" """
import os import os
import time import time
from pathlib import Path from pathlib import Path
from PySide6.QtWidgets import ( from PySide6.QtWidgets import QMenuBar, QMenu, QMessageBox, QFileDialog
QMenuBar, QMenu, QMessageBox, QFileDialog
)
from PySide6.QtGui import QAction, QKeySequence from PySide6.QtGui import QAction, QKeySequence
from PySide6.QtCore import QTimer from PySide6.QtCore import QTimer
from PySide6.QtWidgets import QApplication from PySide6.QtWidgets import QApplication
@ -43,6 +42,10 @@ class MenuManager:
save_action.triggered.connect(self.save_file) save_action.triggered.connect(self.save_file)
file_menu.addAction(save_action) 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() file_menu.addSeparator()
exit_action = QAction("Salir", self.main_window) exit_action = QAction("Salir", self.main_window)
@ -90,7 +93,9 @@ class MenuManager:
font_menu.addAction(font_large_action) font_menu.addAction(font_large_action)
# Marcar el tamaño actual # 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: if is_large:
font_large_action.setChecked(True) font_large_action.setChecked(True)
else: else:
@ -177,34 +182,50 @@ class MenuManager:
self.clear_input() self.clear_input()
self.clear_output() self.clear_output()
self.main_window.latex_panel.clear_equations() 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._latex_equations.clear()
self.main_window._update_status("✨ Nueva sesión iniciada") self.main_window._update_status("✨ Nueva sesión iniciada")
def load_file(self): def load_file(self):
"""Carga archivo en el editor""" """Carga archivo en el editor"""
filepath, _ = QFileDialog.getOpenFileName( filepath, _ = QFileDialog.getOpenFileName(
self.main_window, "Cargar archivo", "", self.main_window,
"Archivos de texto (*.txt);;Archivos Python (*.py);;Todos los archivos (*.*)" "Cargar archivo",
"",
"Archivos de texto (*.txt);;Archivos Python (*.py);;Todos los archivos (*.*)",
) )
if filepath: if filepath:
try: self._load_file_content(filepath)
with open(filepath, "r", encoding="utf-8") as f:
content = f.read()
self.main_window.input_text.setPlainText(content) def _load_file_content(self, filepath: str):
self.main_window._evaluate_and_update() """Carga el contenido de un archivo en el editor."""
self.main_window._update_status(f"📁 Archivo cargado: {Path(filepath).name}") try:
with open(filepath, "r", encoding="utf-8") as f:
content = f.read()
except Exception as e: self.main_window.input_text.setPlainText(content)
QMessageBox.critical(self.main_window, "Error", f"No se pudo cargar el archivo:\n{e}") 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): def save_file(self):
"""Guarda contenido del editor""" """Guarda contenido del editor"""
filepath, _ = QFileDialog.getSaveFileName( filepath, _ = QFileDialog.getSaveFileName(
self.main_window, "Guardar archivo", "", self.main_window,
"Archivos de texto (*.txt);;Archivos Python (*.py);;Todos los archivos (*.*)" "Guardar archivo",
"",
"Archivos de texto (*.txt);;Archivos Python (*.py);;Todos los archivos (*.*)",
) )
if filepath: if filepath:
@ -213,10 +234,36 @@ class MenuManager:
with open(filepath, "w", encoding="utf-8") as f: with open(filepath, "w", encoding="utf-8") as f:
f.write(content) 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: 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 ========== # ========== FUNCIONES DE MENÚ EDITAR ==========
@ -229,7 +276,7 @@ class MenuManager:
"""Limpia panel de salida y LaTeX""" """Limpia panel de salida y LaTeX"""
self.main_window._clear_output() self.main_window._clear_output()
self.main_window.latex_panel.clear_equations() 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._latex_equations.clear()
def clear_history(self): def clear_history(self):
@ -239,7 +286,9 @@ class MenuManager:
os.remove(self.main_window.HISTORY_FILE) os.remove(self.main_window.HISTORY_FILE)
self.main_window._update_status("✓ Historial limpiado") self.main_window._update_status("✓ Historial limpiado")
except Exception as e: 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): def set_font_size(self, is_large: bool):
"""Cambia el tamaño de fuente de los paneles""" """Cambia el tamaño de fuente de los paneles"""
@ -271,8 +320,8 @@ class MenuManager:
import re import re
# Patrón para encontrar y reemplazar font-size en QPlainTextEdit y QTextEdit # Patrón para encontrar y reemplazar font-size en QPlainTextEdit y QTextEdit
pattern = r'(QPlainTextEdit, QTextEdit\s*\{[^}]*?)font-size:\s*\d+px;([^}]*\})' pattern = r"(QPlainTextEdit, QTextEdit\s*\{[^}]*?)font-size:\s*\d+px;([^}]*\})"
replacement = rf'\1font-size: {font_size}px;\2' replacement = rf"\1font-size: {font_size}px;\2"
new_style = re.sub(pattern, replacement, current_style) new_style = re.sub(pattern, replacement, current_style)
@ -293,7 +342,9 @@ class MenuManager:
self.main_window._update_status("✓ Sistema de tipos recargado") self.main_window._update_status("✓ Sistema de tipos recargado")
except Exception as e: except Exception as e:
self.logger.error(f"Error recargando tipos: {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): def show_types_info(self):
"""Muestra información sobre tipos disponibles""" """Muestra información sobre tipos disponibles"""
@ -317,7 +368,9 @@ CARACTERÍSTICAS:
self._show_info_dialog("Información del Sistema", info_text) self._show_info_dialog("Información del Sistema", info_text)
except Exception as e: 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): def show_types_syntax(self):
"""Muestra sintaxis de tipos disponibles""" """Muestra sintaxis de tipos disponibles"""
@ -331,20 +384,37 @@ CARACTERÍSTICAS:
self._show_info_dialog("Sintaxis de Tipos", syntax_text) self._show_info_dialog("Sintaxis de Tipos", syntax_text)
except Exception as e: 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 ========== # ========== FUNCIONES DE DIAGNÓSTICO ==========
def _diagnose_mathjax(self): def _diagnose_mathjax(self):
"""Ejecuta diagnóstico de MathJax""" """Ejecuta diagnóstico de MathJax"""
if not hasattr(self.main_window.latex_panel, '_webview_available') or not self.main_window.latex_panel._webview_available: if (
QMessageBox.warning(self.main_window, "Diagnóstico", "Panel LaTeX no usa WebEngine (usando fallback)") 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 return
# Aquí iría el código de diagnóstico # Aquí iría el código de diagnóstico
# Por ahora solo mostrar estado # Por ahora solo mostrar estado
status = "WebEngine disponible" if self.main_window.latex_panel._webview_available else "Usando fallback HTML" status = (
equations = len(self.main_window._latex_equations) if hasattr(self.main_window, '_latex_equations') else 0 "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 info = f"""DIAGNÓSTICO MATHJAX
@ -360,10 +430,16 @@ en el WebEngineView.
def _show_latex_panel_status(self): def _show_latex_panel_status(self):
"""Muestra estado del panel LaTeX""" """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 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 webview_available = (
equations_count = len(self.main_window._latex_equations) if hasattr(self.main_window, '_latex_equations') else 0 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 status_message = f"""ESTADO DEL PANEL LATEX
@ -390,9 +466,12 @@ PARA SOLUCIONAR:
# Obtener HTML del panel LaTeX # Obtener HTML del panel LaTeX
latex_panel = self.main_window.latex_panel 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 # 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 # Obtener HTML actual del WebView
def on_html_received(html_content): def on_html_received(html_content):
self._save_html_to_file(html_content, "webview_current") self._save_html_to_file(html_content, "webview_current")
@ -408,24 +487,24 @@ PARA SOLUCIONAR:
self._save_html_to_file(base_html, "webview_not_initialized") self._save_html_to_file(base_html, "webview_not_initialized")
else: else:
# Panel con fallback text browser # Panel con fallback text browser
if hasattr(latex_panel, 'text_browser'): if hasattr(latex_panel, "text_browser"):
fallback_html = latex_panel.text_browser.toHtml() fallback_html = latex_panel.text_browser.toHtml()
self._save_html_to_file(fallback_html, "fallback_textbrowser") self._save_html_to_file(fallback_html, "fallback_textbrowser")
else: 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( QMessageBox.information(
self.main_window, self.main_window,
"HTML Guardado", "HTML Guardado",
"El HTML del panel LaTeX ha sido guardado en ./debug_html/ para diagnóstico." "El HTML del panel LaTeX ha sido guardado en ./debug_html/ para diagnóstico.",
) )
except Exception as e: except Exception as e:
self.logger.error(f"Error guardando HTML del panel LaTeX: {e}") self.logger.error(f"Error guardando HTML del panel LaTeX: {e}")
QMessageBox.critical( QMessageBox.critical(
self.main_window, self.main_window, "Error", f"Error guardando HTML: {e}"
"Error",
f"Error guardando HTML: {e}"
) )
def _save_html_to_file(self, html_content, file_suffix): def _save_html_to_file(self, html_content, file_suffix):
@ -462,11 +541,16 @@ PARA SOLUCIONAR:
# Obtener ecuaciones LaTeX si están disponibles # Obtener ecuaciones LaTeX si están disponibles
latex_equations = "" latex_equations = ""
if hasattr(self.main_window, '_latex_equations') and self.main_window._latex_equations: if (
latex_equations = "\\n".join([ hasattr(self.main_window, "_latex_equations")
f"[{eq['type']}] {eq['content']}" and self.main_window._latex_equations
for eq in 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 # Crear reporte de debug completo
debug_report = f"""=== REPORTE DEBUG CALCULADORA MAV === debug_report = f"""=== REPORTE DEBUG CALCULADORA MAV ===
@ -499,11 +583,17 @@ Panel LaTeX visible: {self.main_window.latex_panel_visible}
clipboard.setText(debug_report) clipboard.setText(debug_report)
# Mostrar confirmación # 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: except Exception as e:
self.logger.error(f"Error copiando debug: {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 ========== # ========== FUNCIONES DE MENÚ AYUDA ==========

View File

@ -1,6 +1,7 @@
""" """
Sistema de Configuración y Persistencia para la Calculadora MAV CAS Híbrida Sistema de Configuración y Persistencia para la Calculadora MAV CAS Híbrida
""" """
import os import os
import json import json
import logging import logging
@ -33,7 +34,8 @@ class SettingsManager:
"debug_mode": False, "debug_mode": False,
"latex_panel_visible": False, "latex_panel_visible": False,
"latex_panel_width": 300, "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): def _save_settings(self):
@ -48,15 +50,15 @@ class SettingsManager:
"x": geometry.x(), "x": geometry.x(),
"y": geometry.y(), "y": geometry.y(),
"width": geometry.width(), "width": geometry.width(),
"height": geometry.height() "height": geometry.height(),
} }
# Guardar tamaños del splitter # 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["splitter_sizes"] = self.main_window.main_splitter.sizes()
self.settings["latex_panel_visible"] = self.main_window.latex_panel_visible 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: with open(self.SETTINGS_FILE, "w", encoding="utf-8") as f:
json.dump(self.settings, f, indent=4, ensure_ascii=False) json.dump(self.settings, f, indent=4, ensure_ascii=False)
@ -97,11 +99,13 @@ class SettingsManager:
try: try:
geom = self.settings.get("window_geometry") geom = self.settings.get("window_geometry")
if geom and isinstance(geom, dict): 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 # Restaurar splitter
sizes = self.settings.get("splitter_sizes") 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) self.main_window.main_splitter.setSizes(sizes)
except Exception as e: except Exception as e:
@ -124,3 +128,21 @@ class SettingsManager:
def set_setting(self, key: str, value): def set_setting(self, key: str, value):
"""Establece un valor de configuración""" """Establece un valor de configuración"""
self.settings[key] = value 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}")