256 lines
7.7 KiB
Python
256 lines
7.7 KiB
Python
#!/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"""
|
|
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"
|
|
|
|
# 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)
|