From e3688b0d84d4c0cc7de33d2a6f5cebc0c73e22d3 Mon Sep 17 00:00:00 2001 From: Louis Mazin Date: Fri, 26 Sep 2025 18:56:32 +0200 Subject: [PATCH] splash screen utility --- app/ui/main_window.py | 45 ++++++++++++--------- app/ui/windows/splash_screen.py | 69 ++++++++++++++++++++++---------- main.py | 70 +++++++++++++++++++++++++++------ 3 files changed, 133 insertions(+), 51 deletions(-) diff --git a/app/ui/main_window.py b/app/ui/main_window.py index f5c21c4..d8f606e 100644 --- a/app/ui/main_window.py +++ b/app/ui/main_window.py @@ -1,6 +1,6 @@ -from PyQt6.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QLabel, QFrame +from PyQt6.QtWidgets import QApplication, QMainWindow, QLabel from PyQt6.QtGui import QResizeEvent, QCloseEvent -from PyQt6.QtCore import QSize, QEvent, Qt +from PyQt6.QtCore import QSize, QEvent from app.core.main_manager import MainManager, NotificationType from app.ui.widgets.tabs_widget import TabsWidget, MenuDirection, ButtonPosition, BorderSide, TabSide from app.ui.windows.settings_window import SettingsWindow @@ -19,24 +19,38 @@ class MainWindow(QMainWindow): self.settings_manager = self.main_manager.get_settings_manager() self.observer_manager = self.main_manager.get_observer_manager() self.observer_manager.subscribe(NotificationType.THEME, self.update_theme) - self.is_maximizing: bool = False - self.current_size: QSize - self.previous_size: QSize + + # Initialiser les attributs de taille AVANT setup_ui + app: Optional[QApplication] = QApplication.instance() + size: QSize = app.primaryScreen().size() + self.settings_manager.minScreenSize = min(size.height(), size.width()) + + # Initialiser les tailles par défaut + window_size: dict = self.settings_manager.get_window_size() + self.current_size: QSize = QSize(window_size["width"], window_size["height"]) + self.previous_size: QSize = QSize(window_size["width"], window_size["height"]) # UI elements self.side_menu: TabsWidget self.settings_window: SettingsWindow self.suggestion_window: SuggestionWindow - self.footer_label: QLabel # Ajout d'un attribut pour le footer - - app: Optional[QApplication] = QApplication.instance() - size: QSize = app.primaryScreen().size() - self.settings_manager.minScreenSize = min(size.height(),size.width()) self.setMinimumSize(600, 400) + + # Initialiser l'UI immédiatement (sera fait pendant le splash) self.setup_ui() - self.apply_saved_window_state() + + # Différer l'application des paramètres de fenêtre jusqu'à l'affichage réel + # (cela évite des bugs de taille pendant le préchargement) + self._window_state_applied = False + + def showEvent(self, event): + """Applique les paramètres de fenêtre lors du premier affichage""" + super().showEvent(event) + if not self._window_state_applied: + self.apply_saved_window_state() + self._window_state_applied = True def apply_saved_window_state(self) -> None: """Apply saved window size and maximized state""" @@ -77,8 +91,6 @@ class MainWindow(QMainWindow): self.current_size.width(), self.current_size.height() ) - - # Nettoyage des icônes temporaires générées (supprime tout le dossier temp_icons à la fermeture) try: shutil.rmtree(paths.get_user_temp(self.settings_manager.get_config("app_name"))) except Exception: @@ -93,11 +105,8 @@ class MainWindow(QMainWindow): self.settings_window = SettingsWindow(self) self.side_menu.add_widget(self.settings_window, "", paths.get_asset_svg_path("settings"), position=ButtonPosition.CENTER) - + self.setCentralWidget(self.side_menu) def update_theme(self) -> None: - self.setStyleSheet(self.theme_manager.get_sheet()) - - def update_language(self) -> None: - self.footer_label.setText(self.language_manager.get_text("footer_text")) \ No newline at end of file + self.setStyleSheet(self.theme_manager.get_sheet()) \ No newline at end of file diff --git a/app/ui/windows/splash_screen.py b/app/ui/windows/splash_screen.py index 3bcc02a..bd38e53 100644 --- a/app/ui/windows/splash_screen.py +++ b/app/ui/windows/splash_screen.py @@ -1,24 +1,29 @@ from PyQt6.QtWidgets import QWidget, QVBoxLayout, QLabel -from PyQt6.QtCore import Qt, QTimer, pyqtSignal +from PyQt6.QtCore import Qt, pyqtSignal, QTimer from PyQt6.QtGui import QPixmap from app.core.main_manager import MainManager from app.ui.widgets.loading_spinner import LoadingSpinner import app.utils.paths as paths class SplashScreen(QWidget): - finished = pyqtSignal() + finished = pyqtSignal(bool) # True si succès, False si échec/interruption - def __init__(self, duration=3000, parent=None): + def __init__(self, parent=None, preload_function=None): super().__init__(parent) - self.duration = duration + self.preload_function = preload_function + self.preload_result = True self.main_manager = MainManager.get_instance() self.theme_manager = self.main_manager.get_theme_manager() self.settings_manager = self.main_manager.get_settings_manager() + self.language_manager = self.main_manager.get_language_manager() self.setup_ui() - self.setup_timer() - + if self.preload_function: + self.start_preloading() + else: + # Pas de préchargement, fermer immédiatement + QTimer.singleShot(100, lambda: self.finished.emit(True)) def setup_ui(self): # Configuration de la fenêtre @@ -38,6 +43,12 @@ class SplashScreen(QWidget): self.load_splash_image() layout.addWidget(self.image_label) + # Texte de progression + self.progress_label = QLabel("Chargement...") + self.progress_label.setAlignment(Qt.AlignmentFlag.AlignCenter) + self.progress_label.setStyleSheet("font-size: 14px; color: #666;") + layout.addWidget(self.progress_label) + # Spinner de chargement self.spinner = LoadingSpinner(50, self) spinner_layout = QVBoxLayout() @@ -51,6 +62,38 @@ class SplashScreen(QWidget): # Centrer la fenêtre self.center_on_screen() + def start_preloading(self): + """Démarre le préchargement avec un délai pour permettre l'affichage du splash""" + # Laisser le temps au splash de s'afficher + QTimer.singleShot(200, self.do_preloading) + + def do_preloading(self): + """Effectue le préchargement dans le thread principal""" + try: + # Fonction callback pour mettre à jour le texte + def progress_callback(text): + self.progress_label.setText(text) + # Traiter les événements pour que l'UI se mette à jour + from PyQt6.QtWidgets import QApplication + QApplication.processEvents() + + # Appeler la fonction de préchargement + success = self.preload_function(progress_callback) + self.preload_result = success + + except Exception: + self.preload_result = False + + # Attendre un peu puis fermer + QTimer.singleShot(300, self.close_splash) + + def close_splash(self): + """Ferme le splash screen et émet le signal""" + if hasattr(self, 'spinner'): + self.spinner.stop() + self.finished.emit(self.preload_result) + self.close() + def load_splash_image(self): """Charge l'image splash depuis la config""" try: @@ -111,20 +154,6 @@ class SplashScreen(QWidget): y = (screen_geometry.height() - self.height()) // 2 self.move(x, y) - def setup_timer(self): - """Configure le timer pour fermer automatiquement le splash screen""" - self.timer = QTimer() - self.timer.timeout.connect(self.close_splash) - self.timer.start(self.duration) - - def close_splash(self): - """Ferme le splash screen et émet le signal""" - if hasattr(self, 'spinner'): - self.spinner.stop() - self.timer.stop() - self.hide() - self.finished.emit() - def show_splash(self): """Affiche le splash screen""" self.show() diff --git a/main.py b/main.py index 7e71c1e..c75f361 100644 --- a/main.py +++ b/main.py @@ -6,40 +6,84 @@ from app.ui.main_window import MainWindow from app.ui.windows.splash_screen import SplashScreen from app.core.main_manager import MainManager +preloaded_window = None + +def preload_application(progress_callback): + """ + Fonction de préchargement qui s'exécute pendant l'affichage du splash screen + + Args: + progress_callback: Fonction pour mettre à jour le texte de progression + + Returns: + bool: True si tout s'est bien passé, False sinon + """ + global preloaded_window + + try: + main_manager = MainManager.get_instance() + language_manager = main_manager.get_language_manager() + update_manager = main_manager.get_update_manager() + + progress_callback(language_manager.get_text("checking_updates")) + + if update_manager.check_for_update(): + return False + + progress_callback(language_manager.get_text("initializing")) + + preloaded_window = MainWindow() + + progress_callback(language_manager.get_text("loading_complete")) + + return True + + except Exception as e: + print(f"Error during preload: {e}") + return False + def main() -> int: + global preloaded_window + main_manager: MainManager = MainManager.get_instance() theme_manager = main_manager.get_theme_manager() settings_manager = main_manager.get_settings_manager() - update_manager = main_manager.get_update_manager() app: QApplication = QApplication(sys.argv) app.setStyleSheet(theme_manager.get_sheet()) app.setApplicationName(settings_manager.get_config("app_name")) app.setWindowIcon(QIcon(paths.get_asset_path("icon"))) - # Vérifier si l'image splash existe splash_image_path = paths.get_asset_path(settings_manager.get_config("splash_image")) use_splash = splash_image_path and paths.Path(splash_image_path).exists() if use_splash: - # Créer et afficher le splash screen - splash = SplashScreen(duration=1500) + splash = SplashScreen(preload_function=preload_application) splash.show_splash() - # Connecter le signal finished pour créer et afficher la fenêtre principale - def show_main_window(): - if update_manager.check_for_update(): + def on_splash_finished(success): + global preloaded_window + if not success: app.quit() return - window: MainWindow = MainWindow() - window.show() + + if preloaded_window: + preloaded_window.show() + else: + window = MainWindow() + window.show() - splash.finished.connect(show_main_window) + splash.finished.connect(on_splash_finished) else: - # Pas de splash screen, vérifier les mises à jour puis afficher la fenêtre principale - if update_manager.check_for_update(): + + def dummy_progress(text): + pass + + success = preload_application(dummy_progress) + if not success: return 0 - window: MainWindow = MainWindow() + + window = preloaded_window if preloaded_window else MainWindow() window.show() return app.exec()