icon and splash screen
This commit is contained in:
parent
f2857f2fe6
commit
c2067b2a41
67
app/ui/widgets/loading_spinner.py
Normal file
67
app/ui/widgets/loading_spinner.py
Normal 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()
|
132
app/ui/windows/splash_screen.py
Normal file
132
app/ui/windows/splash_screen.py
Normal 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()
|
@ -4,6 +4,7 @@
|
|||||||
"app_version": "1.0.0",
|
"app_version": "1.0.0",
|
||||||
"architecture": "x64",
|
"architecture": "x64",
|
||||||
"icon_path": "data/assets/icon.ico",
|
"icon_path": "data/assets/icon.ico",
|
||||||
|
"splash_image": "splash",
|
||||||
"main_script": "main.py",
|
"main_script": "main.py",
|
||||||
"git_repo": "https://gitea.louismazin.ovh/LouisMazin/PythonApplicationTemplate"
|
"git_repo": "https://gitea.louismazin.ovh/LouisMazin/PythonApplicationTemplate"
|
||||||
}
|
}
|
BIN
data/assets/icon.icns
Normal file
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
BIN
data/assets/splash.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 54 KiB |
37
main.py
37
main.py
@ -3,23 +3,52 @@ import app.utils.paths as paths
|
|||||||
from PyQt6.QtWidgets import QApplication
|
from PyQt6.QtWidgets import QApplication
|
||||||
from PyQt6.QtGui import QIcon
|
from PyQt6.QtGui import QIcon
|
||||||
from app.ui.main_window import MainWindow
|
from app.ui.main_window import MainWindow
|
||||||
|
from app.ui.windows.splash_screen import SplashScreen
|
||||||
from app.core.main_manager import MainManager
|
from app.core.main_manager import MainManager
|
||||||
|
|
||||||
def main() -> int:
|
def main() -> int:
|
||||||
main_manager: MainManager = MainManager.get_instance()
|
main_manager: MainManager = MainManager.get_instance()
|
||||||
theme_manager = main_manager.get_theme_manager()
|
theme_manager = main_manager.get_theme_manager()
|
||||||
settings_manager = main_manager.get_settings_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: QApplication = QApplication(sys.argv)
|
||||||
app.setStyleSheet(theme_manager.get_sheet())
|
|
||||||
app.setApplicationName(settings_manager.get_config("app_name"))
|
app.setApplicationName(settings_manager.get_config("app_name"))
|
||||||
app.setWindowIcon(QIcon(paths.get_asset_path("icon")))
|
app.setWindowIcon(QIcon(paths.get_asset_path("icon")))
|
||||||
|
|
||||||
if update_manager.check_for_update():
|
# Vérifier si l'image splash existe
|
||||||
return
|
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.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: MainWindow = MainWindow()
|
||||||
window.show()
|
window.show()
|
||||||
|
|
||||||
|
app.setStyleSheet(theme_manager.get_sheet())
|
||||||
return app.exec()
|
return app.exec()
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
Loading…
x
Reference in New Issue
Block a user