splash screen utility

This commit is contained in:
Louis Mazin 2025-09-26 18:56:32 +02:00
parent 20945c44aa
commit e3688b0d84
3 changed files with 133 additions and 51 deletions

View File

@ -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"))
self.setStyleSheet(self.theme_manager.get_sheet())

View File

@ -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()

70
main.py
View File

@ -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()