4.4 Flujos de Ejecución y Secuencia
Dada la naturaleza de la herramienta —operada principalmente mediante Interfaz de Línea de Comandos (CLI) y orquestada a través de un patrón Pipeline—, se ha prescindido de los diagramas de Casos de Uso genéricos en favor de un modelado de comportamiento mucho más estricto y técnico: el Diagrama de Secuencia.
Este enfoque permite documentar el ciclo de vida exacto de la lógica de negocio, mapeando las interacciones síncronas entre el motor de ejecución principal, los nodos del escáner (Steps), el gestor de la base de datos local (DB) y el sistema de archivos (Path).
4.4.1 Ejecución en Hilo Independiente (QThread)
Cuando el usuario interactúa con la interfaz gráfica (UI), las operaciones de pipeline pueden implicar lecturas intensivas del sistema de archivos o consultas a la base de datos. Ejecutar estas operaciones directamente en el hilo principal de Qt bloquearía la interfaz, dejándola sin respuesta durante el tiempo de procesamiento.
Para evitar este bloqueo, la aplicación implementa el patrón
Worker / QThread: cada vez que el usuario lanza una
operación (por ejemplo, pulsando el botón main_check), el
método run_pipeline() de
MainWindow instancia un objeto
Worker(QThread) y lo inicia en un hilo secundario.
Worker.run() → Pipeline → Steps → Reporter
Reporter → Handler.método(context)
Handler → Signal Qt → MainWindow (hilo principal)
Las señales Qt (ui_signal,
update_widget_signal) son el mecanismo seguro que provee
PySide6 para cruzar datos desde un hilo secundario hacia el hilo
principal de la UI. De esta forma, los widgets de la interfaz se
actualizan siempre desde el hilo correcto, sin condiciones de carrera.
4.4.2 El Reporter: Adaptador de Salida Centralizado
Uno de los requerimientos de diseño del proyecto es que la lógica de negocio (los Steps del Pipeline) sea completamente agnóstica respecto a la interfaz de salida. El mismo pipeline debe poder ejecutarse desde la CLI, desde la UI gráfica o de forma silenciosa (modo headless/cron) sin modificar ningún paso.
Este objetivo se resuelve mediante el componente
Reporter, localizado en
core/reporter.py. Al instanciar un
Reporter, se le inyecta un objeto sender —que
puede ser el Handler de la UI o un reportero de CLI—.
Cuando un Step del pipeline necesita emitir información,
llama a reporter.send("nombre_del_step", context), y el
Reporter invoca dinámicamente el método
homónimo del sender:
context.reporter.send("compare_hashes", context)
# Reporter resuelve dinámicamente:
sender = Handler() # en modo UI
sender.compare_hashes(context) # → emite Signal Qt
# Si sender no tiene el método → log de error, sin crash.
Esta arquitectura garantiza que añadir soporte para un nuevo destino de salida (por ejemplo, notificaciones de escritorio o un servidor remoto) solo requiere implementar un nuevo sender con los métodos correspondientes, sin tocar ningún Step del pipeline.