Centro de mando de formaciones (VII):Interfaz gráfica con PyQt6: estructura modular y vistas funcionales

En esta séptima entrega abordamos uno de los hitos más esperados del proyecto: la creación de la interfaz gráfica completa con PyQt6. La aplicación ya permite navegar entre secciones, consultar los datos y gestionar la información de clientes, formaciones e interacciones desde una estructura modular y profesional.

Interfaz gráfica con PyQt6: estructura modular y vistas funcionales

Llegamos a una de las fases más visibles del proyecto: la interfaz gráfica.
Después de definir las capas de datos, dominio y servicios, tocaba integrar toda esa lógica en una interfaz cómoda, clara y funcional.

Hemos elegido PyQt6 como base tecnológica, por su potencia, estabilidad y capacidad de generar interfaces profesionales con una estructura modular.

Estructura de la interfaz

La interfaz está organizada de forma limpia dentro de planificador/ui/:

ui/
├── main_window.py
└── vistas/
├── vista_clientes.py
├── vista_calendario.py
├── vista_formaciones.py
├── vista_interacciones.py
└── vista_configuracion.py

Cada vista corresponde a una sección de la aplicación y se carga dinámicamente desde el panel lateral.
El objetivo es mantener el código separado por responsabilidad, facilitando futuras ampliaciones (formularios, drag&drop, filtros, etc.).

La ventana principal (main_window.py)

La clase principal gestiona la estructura general de la aplicación: un panel lateral con botones de navegación y un área central donde se muestran las vistas.

from PyQt6.QtWidgets import QApplication, QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLabel, QStackedWidget
from planificador.ui.vistas.vista_clientes import VistaClientes
from planificador.ui.vistas.vista_calendario import VistaCalendario
from planificador.ui.vistas.vista_formaciones import VistaFormaciones
from planificador.ui.vistas.vista_interacciones import VistaInteracciones
from planificador.ui.vistas.vista_configuracion import VistaConfiguracion

class MainWindow(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Planificador de formaciones")
        self.resize(1100, 700)

        layout = QHBoxLayout()
        self.setLayout(layout)

        # Panel lateral
        sidebar = QVBoxLayout()
        layout.addLayout(sidebar)

        botones = [
            ("Clientes", VistaClientes),
            ("Calendario", VistaCalendario),
            ("Formaciones", VistaFormaciones),
            ("Interacciones", VistaInteracciones),
            ("Configuración", VistaConfiguracion)
        ]

        self.stack = QStackedWidget()
        layout.addWidget(self.stack)

        for nombre, clase_vista in botones:
            btn = QPushButton(nombre)
            btn.clicked.connect(lambda _, c=clase_vista: self._mostrar_vista(c))
            sidebar.addWidget(btn)
            self.stack.addWidget(clase_vista())

        sidebar.addStretch()

        self._mostrar_vista(VistaClientes)

    def _mostrar_vista(self, clase_vista):
        for i in range(self.stack.count()):
            if isinstance(self.stack.widget(i), clase_vista):
                self.stack.setCurrentIndex(i)
                break

if __name__ == "__main__":
    import sys
    app = QApplication(sys.argv)
    ventana = MainWindow()
    ventana.show()
    sys.exit(app.exec())

La estructura es sencilla pero potente: permite añadir nuevas vistas sin tocar la lógica general.

Las vistas: modulares y funcionales

Cada vista se centra en un ámbito del proyecto.

Por ejemplo, la de clientes muestra los datos de empresas y contactos; la de formaciones lista el catálogo de cursos; la de interacciones actúa como un mini-CRM para el seguimiento comercial.

Todas las vistas comparten el mismo patrón:

  • Cargan datos desde sus repositorios (ClienteRepository, FormacionBaseRepository, InteraccionRepository…).
  • Muestran los registros en tablas o listas.
  • Incluyen botones de acción para recargar o añadir nuevos elementos.
  • Generan mensajes claros cuando los repositorios aún no están disponibles.

Ejemplo: vista_calendario.py

from PyQt6.QtWidgets import QWidget, QVBoxLayout, QLabel, QCalendarWidget, QListWidget, QPushButton
from PyQt6.QtCore import QDate
from planificador.data.repositories.sesion_repo import SesionRepository

class VistaCalendario(QWidget):
    def __init__(self):
        super().__init__()
        layout = QVBoxLayout()
        self.setLayout(layout)

        titulo = QLabel("Calendario")
        layout.addWidget(titulo)

        self.calendario = QCalendarWidget()
        self.calendario.selectionChanged.connect(self.cargar_sesiones)
        layout.addWidget(self.calendario)

        self.lista_sesiones = QListWidget()
        layout.addWidget(self.lista_sesiones)

        self.btn_recargar = QPushButton("Recargar sesiones")
        layout.addWidget(self.btn_recargar)
        self.btn_recargar.clicked.connect(self.cargar_sesiones)

        self.cargar_sesiones()

    def cargar_sesiones(self):
        fecha = self.calendario.selectedDate().toString("yyyy-MM-dd")
        self.lista_sesiones.clear()
        sesiones = SesionRepository.listar_por_fecha(fecha)
        if not sesiones:
            self.lista_sesiones.addItem(f"No hay sesiones para {fecha}")
            return
        for s in sesiones:
            self.lista_sesiones.addItem(f"{s['hora_inicio']} - {s['hora_fin']} | {s['direccion']}")

Integración con la base de datos

Todas las vistas están ya conectadas con los repositorios CRUD de la capa de datos. Cuando se ejecuta la aplicación, PyQt6 consulta directamente SQLite a través de los métodos de cada repositorio.

Esto significa que los datos mostrados son reales y actualizados — no hay simulaciones ni valores estáticos. El sistema muestra mensajes amigables cuando una tabla está vacía o un repositorio no está disponible.

Configuración y utilidades

La vista de Configuración permite crear copias de seguridad y exportar listados CSV con un solo clic. Gracias a los servicios creados en fases anteriores, estas funciones ya son plenamente operativas.

self.btn_backup.clicked.connect(self._crear_backup)
self.btn_exportar_csv.clicked.connect(self._exportar_clientes_csv)

Detrás de estos botones se ejecutan funciones reales que:

  • crean un archivo ZIP con la base de datos actual, y
  • exportan datos de clientes a CSV.

Resultados

La aplicación ya cuenta con una interfaz completa y funcional, desde la que se puede:

  • ✅ Navegar entre secciones (Clientes, Calendario, Formaciones, Interacciones, Configuración).
  • ✅ Visualizar y consultar datos reales almacenados en SQLite.
  • ✅ Generar copias de seguridad y exportaciones.
  • ✅ Registrar interacciones comerciales y preparar futuras acciones.
  • ✅ Mantener una estructura modular y extensible para próximas mejoras.

Próximos pasos

En la siguiente fase nos centraremos en formularios interactivos, drag & drop, vistas semanales y diarias, y recordatorios automáticos, completando así el flujo de trabajo de un planificador profesional de formaciones.

El proyecto Planificador sigue creciendo, y cada paso nos acerca más a una herramienta completa, útil y mantenible.

En la próxima entrega: formularios y funcionalidades avanzadas.