feature/build-y-ui-contraible
This commit is contained in:
137
runner.py
137
runner.py
@@ -1,27 +1,15 @@
|
||||
"""Punto de entrada principal de la aplicación."""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
import traceback
|
||||
from pathlib import Path
|
||||
|
||||
ROOT_DIR = Path(__file__).parent.resolve()
|
||||
if not getattr(sys, "frozen", False):
|
||||
sys.path.insert(0, str(ROOT_DIR))
|
||||
|
||||
# Bootstrap y ODBC antes de importar módulos que usan pyodbc
|
||||
from app.config.bootstrap import ensure_runtime_layout
|
||||
from app.config.odbc_setup import configure_odbc_environment
|
||||
|
||||
ensure_runtime_layout()
|
||||
configure_odbc_environment()
|
||||
|
||||
from PySide6.QtWidgets import QApplication
|
||||
from PySide6.QtCore import Qt
|
||||
|
||||
from app.config import get_launch_options, initialize_env, is_panel_configured
|
||||
from app.ui.main_window import MainWindow
|
||||
from app.utils.logger import app_logger
|
||||
|
||||
|
||||
def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(description="CloudRestoreAS")
|
||||
@@ -38,23 +26,90 @@ def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
|
||||
return parser.parse_args(argv)
|
||||
|
||||
|
||||
def main():
|
||||
"""Función principal."""
|
||||
def _crash_log_targets() -> list[Path]:
|
||||
"""Ubicaciones candidatas para el crash log, de más a menos accesible."""
|
||||
targets: list[Path] = []
|
||||
try:
|
||||
targets.append(Path(sys.executable).resolve().parent / "CloudRestoreAS-crash.log")
|
||||
except Exception:
|
||||
pass
|
||||
base = os.environ.get("LOCALAPPDATA") or os.environ.get("APPDATA") or os.environ.get("HOME")
|
||||
if base:
|
||||
targets.append(Path(base) / "CloudRestoreAS" / "crash.log")
|
||||
import tempfile
|
||||
|
||||
targets.append(Path(tempfile.gettempdir()) / "CloudRestoreAS-crash.log")
|
||||
return targets
|
||||
|
||||
|
||||
def _write_crash_log(text: str) -> Path | None:
|
||||
"""Escribe el detalle del crash en la primera ubicación escribible."""
|
||||
for path in _crash_log_targets():
|
||||
try:
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_text(text, encoding="utf-8")
|
||||
return path
|
||||
except Exception:
|
||||
continue
|
||||
return None
|
||||
|
||||
|
||||
def _show_fatal(exc: BaseException) -> None:
|
||||
"""Hace VISIBLE un fallo de arranque: crash log + diálogo (o stderr)."""
|
||||
tb = "".join(traceback.format_exception(type(exc), exc, exc.__traceback__))
|
||||
detail = f"CloudRestoreAS no pudo iniciar.\n\n{type(exc).__name__}: {exc}\n\n{tb}"
|
||||
crash_path = _write_crash_log(detail)
|
||||
try:
|
||||
sys.stderr.write(detail)
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
from PySide6.QtWidgets import QApplication, QMessageBox
|
||||
|
||||
app = QApplication.instance() or QApplication(sys.argv)
|
||||
box = QMessageBox()
|
||||
box.setIcon(QMessageBox.Icon.Critical)
|
||||
box.setWindowTitle("CloudRestoreAS — Error al iniciar")
|
||||
box.setText("La aplicación no pudo iniciar.")
|
||||
info = str(exc) or type(exc).__name__
|
||||
if crash_path:
|
||||
info += f"\n\nDetalle guardado en:\n{crash_path}"
|
||||
box.setInformativeText(info)
|
||||
box.setDetailedText(tb)
|
||||
box.exec()
|
||||
except Exception:
|
||||
# Sin GUI disponible: el crash log y stderr ya quedaron escritos.
|
||||
pass
|
||||
|
||||
|
||||
def _run() -> int:
|
||||
"""Arranque real. Los imports están diferidos para que main() capture cualquier fallo."""
|
||||
# Bootstrap y ODBC antes de importar módulos que usan pyodbc.
|
||||
from app.config.bootstrap import ensure_runtime_layout
|
||||
from app.config.odbc_setup import configure_odbc_environment
|
||||
|
||||
ensure_runtime_layout()
|
||||
configure_odbc_environment()
|
||||
|
||||
from PySide6.QtWidgets import QApplication
|
||||
from PySide6.QtCore import Qt
|
||||
|
||||
from app.config import get_launch_options, initialize_env, is_panel_configured
|
||||
from app.ui.main_window import MainWindow
|
||||
from app.ui.tray_assets import load_tray_icon
|
||||
from app.utils.logger import app_logger
|
||||
|
||||
args = parse_args()
|
||||
launch = get_launch_options()
|
||||
initialize_env()
|
||||
|
||||
minimized = args.minimized or launch.minimized
|
||||
panel_ok = is_panel_configured()
|
||||
start_engine = args.start_engine or launch.start_engine
|
||||
|
||||
app_logger.info("=" * 80)
|
||||
app_logger.info("CloudRestoreAS - Iniciando aplicación")
|
||||
app_logger.info(f"Directorio app: {ROOT_DIR}")
|
||||
if minimized:
|
||||
app_logger.info("Modo: ventana oculta (--minimized o START_MINIMIZED)")
|
||||
else:
|
||||
app_logger.info("Modo: ventana visible; cerrar minimiza a bandeja")
|
||||
app_logger.info("Modo: ventana siempre visible; cerrar minimiza a bandeja")
|
||||
if start_engine:
|
||||
app_logger.info("Modo: motor auto-inicio")
|
||||
if not panel_ok:
|
||||
@@ -69,25 +124,31 @@ def main():
|
||||
app.setOrganizationName("Aduanasoft")
|
||||
app.setAttribute(Qt.ApplicationAttribute.AA_EnableHighDpiScaling)
|
||||
app.setQuitOnLastWindowClosed(False)
|
||||
app.setWindowIcon(load_tray_icon())
|
||||
|
||||
window = MainWindow(
|
||||
start_engine=start_engine,
|
||||
panel_configured=panel_ok,
|
||||
)
|
||||
# La ventana SIEMPRE se muestra al iniciar; cerrar (X) la manda a la bandeja.
|
||||
window.show()
|
||||
window.activateWindow()
|
||||
window.raise_()
|
||||
app_logger.info("Ventana principal mostrada")
|
||||
|
||||
exit_code = app.exec()
|
||||
app_logger.info(f"Aplicación finalizada con código: {exit_code}")
|
||||
return exit_code
|
||||
|
||||
|
||||
def main():
|
||||
"""Punto de entrada: ejecuta _run() y hace visible cualquier fallo de arranque."""
|
||||
try:
|
||||
window = MainWindow(
|
||||
minimized=minimized,
|
||||
start_engine=start_engine,
|
||||
panel_configured=panel_ok,
|
||||
)
|
||||
if not minimized:
|
||||
window.show()
|
||||
app_logger.info("Ventana principal mostrada")
|
||||
else:
|
||||
app_logger.info("Ventana oculta; icono en bandeja del sistema")
|
||||
|
||||
exit_code = app.exec()
|
||||
app_logger.info(f"Aplicación finalizada con código: {exit_code}")
|
||||
sys.exit(exit_code)
|
||||
|
||||
except Exception as e:
|
||||
app_logger.critical(f"Error fatal en la aplicación: {e}", exc_info=True)
|
||||
sys.exit(_run())
|
||||
except SystemExit:
|
||||
raise
|
||||
except BaseException as exc: # capturamos TODO en el arranque: nunca morir en silencio
|
||||
_show_fatal(exc)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user