icon and splash screen

This commit is contained in:
Louis Mazin 2025-09-09 19:06:30 +02:00
parent f2857f2fe6
commit c2067b2a41
8 changed files with 235 additions and 6 deletions

View File

@ -0,0 +1,67 @@
from PyQt6.QtCore import Qt, QTimer
from PyQt6.QtWidgets import QLabel
from PyQt6.QtGui import QPainter, QPen
from app.core.main_manager import MainManager
import math
class LoadingSpinner(QLabel):
def __init__(self, size=40, parent=None):
super().__init__(parent)
self.size = size
self.angle = 0
self.setFixedSize(size, size)
# Timer pour l'animation
self.timer = QTimer()
self.timer.timeout.connect(self.rotate)
self.timer.start(50) # 50ms = rotation fluide
def rotate(self):
self.angle = (self.angle + 10) % 360
self.update()
def paintEvent(self, event):
painter = QPainter(self)
painter.setRenderHint(QPainter.RenderHint.Antialiasing)
# Obtenir la couleur du thème
main_manager = MainManager.get_instance()
theme = main_manager.get_theme_manager().get_theme()
color = theme.get_color("background_secondary_color")
# Dessiner le cercle de chargement
rect = self.rect()
center_x, center_y = rect.width() // 2, rect.height() // 2
radius = min(center_x, center_y) - 5
painter.translate(center_x, center_y)
painter.rotate(self.angle)
# Dessiner les segments du spinner
pen = QPen()
pen.setWidth(3)
pen.setCapStyle(Qt.PenCapStyle.RoundCap)
for i in range(8):
alpha = 255 - (i * 25) # Dégradé d'opacité
pen.setColor(self.hex_to_qcolor(color, alpha))
painter.setPen(pen)
angle = i * 45
start_x = radius * 0.7 * math.cos(math.radians(angle))
start_y = radius * 0.7 * math.sin(math.radians(angle))
end_x = radius * math.cos(math.radians(angle))
end_y = radius * math.sin(math.radians(angle))
painter.drawLine(int(start_x), int(start_y), int(end_x), int(end_y))
def hex_to_qcolor(self, hex_color, alpha=255):
from PyQt6.QtGui import QColor
hex_color = hex_color.lstrip('#')
r = int(hex_color[0:2], 16)
g = int(hex_color[2:4], 16)
b = int(hex_color[4:6], 16)
return QColor(r, g, b, alpha)
def stop(self):
self.timer.stop()

View File

@ -0,0 +1,132 @@
from PyQt6.QtWidgets import QWidget, QVBoxLayout, QLabel
from PyQt6.QtCore import Qt, QTimer, pyqtSignal
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()
def __init__(self, duration=3000, parent=None):
super().__init__(parent)
self.duration = duration
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.setup_ui()
self.setup_timer()
def setup_ui(self):
# Configuration de la fenêtre
self.setWindowFlags(Qt.WindowType.FramelessWindowHint | Qt.WindowType.WindowStaysOnTopHint)
self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
self.setFixedSize(800, 600)
# Layout principal
layout = QVBoxLayout(self)
layout.setAlignment(Qt.AlignmentFlag.AlignCenter)
layout.setSpacing(60)
layout.setContentsMargins(80, 80, 80, 80)
# Image splash
self.image_label = QLabel()
self.image_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.load_splash_image()
layout.addWidget(self.image_label)
# Spinner de chargement
self.spinner = LoadingSpinner(50, self)
spinner_layout = QVBoxLayout()
spinner_layout.setAlignment(Qt.AlignmentFlag.AlignCenter)
spinner_layout.addWidget(self.spinner)
layout.addLayout(spinner_layout)
# Appliquer le thème
self.apply_theme()
# Centrer la fenêtre
self.center_on_screen()
def load_splash_image(self):
"""Charge l'image splash depuis la config"""
try:
splash_image_path = paths.get_asset_path(self.settings_manager.get_config("splash_image"))
if splash_image_path:
# Essayer le chemin depuis la config
if not splash_image_path.startswith('/') and not splash_image_path.startswith('\\') and ':' not in splash_image_path:
# Chemin relatif, le résoudre depuis le dossier assets
splash_image_path = paths.get_asset_path(splash_image_path)
pixmap = QPixmap(splash_image_path)
if not pixmap.isNull():
# Redimensionner l'image
scaled_pixmap = pixmap.scaled(400, 300, Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.SmoothTransformation)
self.image_label.setPixmap(scaled_pixmap)
return
# Fallback : essayer l'icône par défaut
fallback_path = paths.get_asset_path("icon.png")
pixmap = QPixmap(fallback_path)
if not pixmap.isNull():
scaled_pixmap = pixmap.scaled(240, 240, Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.SmoothTransformation)
self.image_label.setPixmap(scaled_pixmap)
else:
# Dernier fallback : texte
self.image_label.setText("🚀")
self.image_label.setStyleSheet("font-size: 48px;")
except Exception:
# En cas d'erreur, afficher un emoji
self.image_label.setText("🚀")
self.image_label.setStyleSheet("font-size: 48px;")
def apply_theme(self):
"""Applique le thème actuel"""
theme = self.theme_manager.get_theme()
style = f"""
QWidget {{
background-color: {theme.get_color("background_color")};
border-radius: 15px;
border: 2px solid {theme.get_color("primary_color")};
}}
QLabel {{
color: {theme.get_color("text_color")};
background: transparent;
border: none;
}}
"""
self.setStyleSheet(style)
def center_on_screen(self):
"""Centre la fenêtre sur l'écran"""
from PyQt6.QtWidgets import QApplication
screen = QApplication.primaryScreen()
screen_geometry = screen.geometry()
x = (screen_geometry.width() - self.width()) // 2
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()
self.raise_()
self.activateWindow()

View File

@ -4,6 +4,7 @@
"app_version": "1.0.0",
"architecture": "x64",
"icon_path": "data/assets/icon.ico",
"splash_image": "splash",
"main_script": "main.py",
"git_repo": "https://gitea.louismazin.ovh/LouisMazin/PythonApplicationTemplate"
}

BIN
data/assets/icon.icns Normal file

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

BIN
data/assets/splash.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

41
main.py
View File

@ -3,23 +3,52 @@ import app.utils.paths as paths
from PyQt6.QtWidgets import QApplication
from PyQt6.QtGui import QIcon
from app.ui.main_window import MainWindow
from app.ui.windows.splash_screen import SplashScreen
from app.core.main_manager import MainManager
def main() -> int:
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() # Ajout
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")))
if update_manager.check_for_update():
return
# 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()
window: MainWindow = MainWindow()
window.show()
if use_splash:
# Créer et afficher le splash screen
splash = SplashScreen(duration=1500)
splash.show_splash()
# Vérifier les mises à jour en arrière-plan
if update_manager.check_for_update():
splash.close_splash()
return 0
# Créer la fenêtre principale
window: MainWindow = MainWindow()
# Connecter le signal finished pour afficher la fenêtre principale
def show_main_window():
splash.close()
window.show()
splash.finished.connect(show_main_window)
else:
# Pas de splash screen, vérifier directement les mises à jour
if update_manager.check_for_update():
return 0
# Créer et afficher directement la fenêtre principale
window: MainWindow = MainWindow()
window.show()
app.setStyleSheet(theme_manager.get_sheet())
return app.exec()
if __name__ == "__main__":