refactor: modularizar como PocketSync con soporte de perfiles
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,220 @@
|
||||
import os
|
||||
import threading
|
||||
import tkinter as tk
|
||||
|
||||
from core.config import ConfigManager, Profile
|
||||
from core.robocopy_engine import RobocopySyncEngine
|
||||
from core.sync_engine import SyncEngine
|
||||
from ui import styles
|
||||
from ui.path_panel import PathPanel
|
||||
from ui.profile_bar import ProfileBar
|
||||
from ui.status_bar import StatusBar
|
||||
from ui.summary_panel import SummaryPanel
|
||||
from ui.system_list import SystemList
|
||||
|
||||
|
||||
class PocketSyncApp:
|
||||
def __init__(self, root: tk.Tk, config_manager: ConfigManager):
|
||||
self.root = root
|
||||
self.cm = config_manager
|
||||
self.engine: SyncEngine = RobocopySyncEngine()
|
||||
|
||||
self._build_ui()
|
||||
self._load_profile(self.cm.active_profile)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Construcción de la UI
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def _build_ui(self) -> None:
|
||||
self.root.title("PocketSync")
|
||||
self.root.geometry(f"{styles.WINDOW_WIDTH}x{styles.WINDOW_HEIGHT}")
|
||||
|
||||
# --- Barra de perfiles ---
|
||||
self.profile_bar = ProfileBar(self.root, on_change=self._on_profile_change)
|
||||
self.profile_bar.pack(fill="x", padx=styles.PAD_X, pady=(8, 2))
|
||||
self.profile_bar.set_callbacks(
|
||||
on_new=self._on_new_profile,
|
||||
on_rename=self._on_rename_profile,
|
||||
on_delete=self._on_delete_profile,
|
||||
)
|
||||
self._refresh_profile_bar()
|
||||
|
||||
# --- Rutas de origen ---
|
||||
self.src_panel = PathPanel(self.root, title="Rutas de origen")
|
||||
self.src_panel.pack(fill="x", padx=styles.PAD_X, pady=styles.PAD_Y)
|
||||
self.src_panel.set_roms_callback(self._on_roms_src_change)
|
||||
|
||||
# --- Lista de sistemas ---
|
||||
self.system_list = SystemList(self.root)
|
||||
self.system_list.pack(fill="both", expand=True, padx=styles.PAD_X)
|
||||
|
||||
# --- Rutas de destino ---
|
||||
self.dst_panel = PathPanel(self.root, title="Rutas de destino")
|
||||
self.dst_panel.pack(fill="x", padx=styles.PAD_X, pady=styles.PAD_Y)
|
||||
|
||||
# --- Botón Sync ---
|
||||
tk.Button(
|
||||
self.root,
|
||||
text="Sync Now",
|
||||
font=styles.FONT_BUTTON,
|
||||
command=self._run_sync,
|
||||
).pack(pady=6)
|
||||
|
||||
# --- Barra de estado ---
|
||||
self.status_bar = StatusBar(self.root)
|
||||
self.status_bar.pack(fill="x", padx=styles.PAD_X, pady=styles.PAD_Y)
|
||||
|
||||
# --- Panel de resumen ---
|
||||
self.summary = SummaryPanel(self.root)
|
||||
self.summary.pack(fill="x", padx=styles.PAD_X, pady=styles.PAD_Y)
|
||||
|
||||
self.root.protocol("WM_DELETE_WINDOW", self._on_close)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Gestión de perfiles
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def _refresh_profile_bar(self) -> None:
|
||||
self.profile_bar.refresh(self.cm.profile_names(), self.cm.active_profile_name)
|
||||
|
||||
def _collect_ui_into_profile(self) -> None:
|
||||
"""Escribe el estado actual de la UI en el perfil activo."""
|
||||
p = self.cm.active_profile
|
||||
p.esde_src = self.src_panel.get_esde()
|
||||
p.roms_src = self.src_panel.get_roms()
|
||||
p.esde_dst = self.dst_panel.get_esde()
|
||||
p.roms_dst = self.dst_panel.get_roms()
|
||||
p.selected = self.system_list.get_selected()
|
||||
|
||||
def _load_profile(self, profile: Profile) -> None:
|
||||
self.src_panel.set_esde(profile.esde_src)
|
||||
self.src_panel.set_roms(profile.roms_src)
|
||||
self.dst_panel.set_esde(profile.esde_dst)
|
||||
self.dst_panel.set_roms(profile.roms_dst)
|
||||
|
||||
if os.path.isdir(profile.roms_src):
|
||||
self.system_list.populate(profile.roms_src)
|
||||
else:
|
||||
self.system_list.populate("")
|
||||
|
||||
self.system_list.set_selected(profile.selected)
|
||||
|
||||
def _on_profile_change(self, name: str) -> None:
|
||||
self._collect_ui_into_profile()
|
||||
self.cm.active_profile_name = name
|
||||
self._load_profile(self.cm.active_profile)
|
||||
self._refresh_profile_bar()
|
||||
|
||||
def _on_new_profile(self, name: str) -> bool:
|
||||
if self.cm.get_profile(name) is not None:
|
||||
return False
|
||||
self._collect_ui_into_profile()
|
||||
self.cm.add_profile(name)
|
||||
self.cm.active_profile_name = name
|
||||
self._load_profile(self.cm.active_profile)
|
||||
self._refresh_profile_bar()
|
||||
return True
|
||||
|
||||
def _on_rename_profile(self, old: str, new: str) -> bool:
|
||||
ok = self.cm.rename_profile(old, new)
|
||||
if ok:
|
||||
self._refresh_profile_bar()
|
||||
return ok
|
||||
|
||||
def _on_delete_profile(self, name: str) -> bool:
|
||||
ok = self.cm.delete_profile(name)
|
||||
if ok:
|
||||
self._load_profile(self.cm.active_profile)
|
||||
self._refresh_profile_bar()
|
||||
return ok
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Callbacks de widgets
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def _on_roms_src_change(self, path: str) -> None:
|
||||
self.system_list.populate(path)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Loop de sincronización
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def _run_sync(self) -> None:
|
||||
thread = threading.Thread(target=self._sync_thread)
|
||||
thread.daemon = True
|
||||
thread.start()
|
||||
|
||||
def _sync_thread(self) -> None:
|
||||
selected = self.system_list.get_selected()
|
||||
|
||||
if not selected:
|
||||
self.summary.append("No hay sistemas seleccionados.")
|
||||
return
|
||||
|
||||
esde_src = self.src_panel.get_esde()
|
||||
roms_src = self.src_panel.get_roms()
|
||||
esde_dst = self.dst_panel.get_esde()
|
||||
roms_dst = self.dst_panel.get_roms()
|
||||
|
||||
if not all([esde_src, roms_src, esde_dst, roms_dst]):
|
||||
self.summary.append("ERROR: Debes configurar todas las rutas antes de continuar.")
|
||||
return
|
||||
|
||||
self.summary.clear()
|
||||
self.summary.append("=" * 60)
|
||||
self.summary.append("INICIANDO PROCESO DE COPIA")
|
||||
self.summary.append(f"Total de sistemas a procesar: {len(selected)}")
|
||||
self.summary.append("=" * 60)
|
||||
|
||||
total = len(selected)
|
||||
|
||||
for idx, system in enumerate(selected, 1):
|
||||
self.status_bar.set_system(f"Sistema: {idx}/{total} - {system.upper()}")
|
||||
self.summary.append(f"\nSISTEMA [{idx}/{total}]: {system.upper()}")
|
||||
|
||||
# Fase 1: ROMs
|
||||
self.status_bar.set_phase("Fase: [1/3] Copiando ROMs...")
|
||||
self.summary.append(" [1/3] Copiando ROMs...")
|
||||
self.engine.sync_folder(
|
||||
os.path.join(roms_src, system),
|
||||
os.path.join(roms_dst, system),
|
||||
on_file=self.status_bar.set_file,
|
||||
on_summary=self.summary.append,
|
||||
)
|
||||
|
||||
# Fase 2: gamelists
|
||||
self.status_bar.set_phase("Fase: [2/3] Copiando gamelists...")
|
||||
self.summary.append(" [2/3] Copiando gamelists...")
|
||||
self.engine.sync_folder(
|
||||
os.path.join(esde_src, "gamelists", system),
|
||||
os.path.join(esde_dst, "gamelists", system),
|
||||
on_file=self.status_bar.set_file,
|
||||
on_summary=self.summary.append,
|
||||
)
|
||||
|
||||
# Fase 3: media
|
||||
self.status_bar.set_phase("Fase: [3/3] Copiando media...")
|
||||
self.summary.append(" [3/3] Copiando media...")
|
||||
self.engine.sync_folder(
|
||||
os.path.join(esde_src, "downloaded_media", system),
|
||||
os.path.join(esde_dst, "downloaded_media", system),
|
||||
on_file=self.status_bar.set_file,
|
||||
on_summary=self.summary.append,
|
||||
)
|
||||
|
||||
self.summary.append(f" Sistema '{system}' completado\n")
|
||||
|
||||
self.status_bar.reset()
|
||||
self.summary.append("=" * 60)
|
||||
self.summary.append("PROCESO COMPLETADO EXITOSAMENTE")
|
||||
self.summary.append("=" * 60)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Cierre
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def _on_close(self) -> None:
|
||||
self._collect_ui_into_profile()
|
||||
self.cm.save()
|
||||
self.root.destroy()
|
||||
Reference in New Issue
Block a user