#!/usr/bin/env python3 """ Launcher principal para Calculadora MAV - CAS Híbrido Este script maneja la inicialización y ejecución de la aplicación """ import sys import os import subprocess import tkinter as tk from tkinter import messagebox from pathlib import Path import importlib.util import logging import datetime import traceback import platform 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) # Archivo de log con timestamp 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, format='%(asctime)s - %(levelname)s - %(funcName)s:%(lineno)d - %(message)s', handlers=[ logging.FileHandler(log_file, encoding='utf-8'), logging.StreamHandler(sys.stdout) ] ) logger = logging.getLogger(__name__) return logger, log_file def log_error_with_context(logger, error, context=""): """Registra un error con contexto completo""" logger.error("=" * 50) logger.error("ERROR DETECTADO") logger.error("=" * 50) if context: logger.error(f"Contexto: {context}") logger.error(f"Tipo de error: {type(error).__name__}") logger.error(f"Mensaje: {str(error)}") logger.error("Traceback completo:") logger.error(traceback.format_exc()) logger.error("Variables locales en el momento del error:") # Intentar capturar variables locales del frame donde ocurrió el error try: tb = traceback.extract_tb(error.__traceback__) if tb: last_frame = tb[-1] logger.error(f" Archivo: {last_frame.filename}") logger.error(f" Línea: {last_frame.lineno}") logger.error(f" Función: {last_frame.name}") logger.error(f" Código: {last_frame.line}") except Exception as frame_error: logger.error(f"No se pudieron obtener detalles del frame: {frame_error}") logger.error("=" * 50) def show_error_with_log_info(error, log_file, context=""): """Muestra error al usuario con información del log""" error_msg = f"""Error en Calculadora MAV: {context} Error: {type(error).__name__}: {str(error)} INFORMACIÓN DE DEBUGGING: • Log completo guardado en: {log_file} • Para soporte, enviar el archivo de log • Timestamp: {datetime.datetime.now()} ¿Qué hacer ahora? 1. Revisar el archivo de log para más detalles 2. Intentar reiniciar la aplicación 3. Verificar dependencias con: python launcher.py --setup 4. Ejecutar tests con: python launcher.py --test """ try: root = tk.Tk() root.withdraw() # Crear ventana de error personalizada error_window = tk.Toplevel(root) error_window.title("Error - Calculadora MAV") error_window.geometry("600x400") error_window.configure(bg="#2b2b2b") # Hacer la ventana modal error_window.transient(root) error_window.grab_set() # Centrar ventana error_window.update_idletasks() x = (error_window.winfo_screenwidth() // 2) - (error_window.winfo_width() // 2) y = (error_window.winfo_screenheight() // 2) - (error_window.winfo_height() // 2) error_window.geometry(f"+{x}+{y}") # Contenido from tkinter import scrolledtext text_widget = scrolledtext.ScrolledText( error_window, font=("Consolas", 10), bg="#1e1e1e", fg="#ff6b6b", wrap=tk.WORD ) text_widget.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) text_widget.insert("1.0", error_msg) text_widget.config(state="disabled") # Botones button_frame = tk.Frame(error_window, bg="#2b2b2b") button_frame.pack(fill=tk.X, padx=10, pady=5) def open_log_folder(): try: if platform.system() == "Windows": os.startfile(log_file.parent) elif platform.system() == "Darwin": # macOS subprocess.run(["open", str(log_file.parent)]) else: # Linux subprocess.run(["xdg-open", str(log_file.parent)]) except Exception: pass def copy_log_path(): error_window.clipboard_clear() error_window.clipboard_append(str(log_file)) tk.Button( button_frame, text="Abrir Carpeta de Logs", command=open_log_folder, bg="#4fc3f7", fg="white" ).pack(side=tk.LEFT, padx=5) tk.Button( button_frame, text="Copiar Ruta del Log", command=copy_log_path, bg="#82aaff", fg="white" ).pack(side=tk.LEFT, padx=5) tk.Button( button_frame, text="Cerrar", command=error_window.destroy, bg="#ff6b6b", fg="white" ).pack(side=tk.RIGHT, padx=5) # Esperar a que se cierre la ventana error_window.wait_window() root.destroy() except Exception as gui_error: # Si falla la GUI, mostrar en consola print("ERROR: No se pudo mostrar ventana de error") print(error_msg) print(f"Error adicional: {gui_error}") # Variable global para el logger logger = None log_file = None def launch_application(): try: # Importar y ejecutar la aplicación from main_calc_app import HybridCalculatorApp root = tk.Tk() app = HybridCalculatorApp(root) root.mainloop() logger.info("Aplicación cerrada normalmente") except ImportError as e: logger.error(f"Error de importación: {e}") log_error_with_context(logger, e, "Importación de módulos de la aplicación") show_error_with_log_info(e, log_file, "Error importando módulos de la aplicación") except Exception as e: logger.error(f"Error durante ejecución de la aplicación: {e}") log_error_with_context(logger, e, "Ejecución de la aplicación principal") show_error_with_log_info(e, log_file, "Error durante la ejecución de la aplicación") def main(): """Función principal del launcher""" global logger, log_file # Configurar logging al inicio try: logger, log_file = setup_logging() except Exception as e: print(f"ERROR: No se pudo configurar logging: {e}") # Continuar sin logging si falla logger = None log_file = None logger.info("Iniciando verificaciones del sistema...") try: launch_application() logger.info("Aplicación cerrada - fin de sesión") logger.info("=" * 60) except KeyboardInterrupt: logger.info("Aplicación interrumpida por el usuario (Ctrl+C)") sys.exit(0) except Exception as e: logger.error("Error crítico en main():") log_error_with_context(logger, e, "Función principal del launcher") show_error_with_log_info(e, log_file, "Error crítico durante el inicio") sys.exit(1) if __name__ == "__main__": # Configurar logging básico para manejo de argumentos temp_logger = None temp_log_file = None # Inicio normal try: main() except Exception as e: if temp_logger: log_error_with_context(temp_logger, e, "Error crítico en __main__") print(f"ERROR CRÍTICO: {e}") if temp_log_file: print(f"Ver log completo en: {temp_log_file}") sys.exit(1)