generated from LouisMazin/PythonApplicationTemplate
splash screen and alerts
This commit is contained in:
parent
7531a1cbfa
commit
2e3be1f3ad
332
README.md
332
README.md
@ -1,162 +1,274 @@
|
||||
# 🚀 Python PyQt6 Application Template
|
||||
# 🏥 HoDA Radio - Anonymisation d'Images Médicales
|
||||
|
||||
Bienvenue ! Ce projet est **bien plus qu'un simple template** : c'est une boîte à outils moderne pour créer rapidement des applications desktop Python élégantes, robustes et évolutives.
|
||||
Vous voulez coder, personnaliser, traduire, mettre à jour, distribuer ? Tout est déjà prêt !
|
||||
**HoDA Radio** est une application moderne et intuitive pour l'anonymisation d'images médicales DICOM. Développée en Python avec PyQt6, elle offre une interface élégante et des outils professionnels pour protéger la confidentialité des patients tout en conservant l'utilité médicale des images.
|
||||
|
||||
---
|
||||
|
||||
## ✨ Fonctionnalités clés
|
||||
## ✨ Fonctionnalités principales
|
||||
|
||||
- **Interface moderne (PyQt6)** : Responsive, stylée, et facile à personnaliser.
|
||||
- **Thèmes dynamiques** : Passez du clair au sombre en un clic, ou créez le vôtre !
|
||||
- **Multi-langues** : Français, anglais... et ajoutez-en autant que vous voulez.
|
||||
- **Paramètres utilisateurs** : Tout est sauvegardé (thème, langue, taille de fenêtre...).
|
||||
- **Architecture modulaire** : Des managers pour chaque besoin, tout est organisé.
|
||||
- **Notifications automatiques** : Les widgets s'adaptent instantanément aux changements.
|
||||
- **Barre d’onglets flexible** : Ajoutez vos fenêtres où vous voulez, comme vous voulez.
|
||||
- **Système de suggestion** : Vos utilisateurs peuvent vous écrire directement depuis l’app.
|
||||
- **Mise à jour automatique** : Téléchargez la dernière version sans effort, avec barre de progression !
|
||||
- **Build & environnement** : Scripts pour tout automatiser, sur Windows, Linux, macOS.
|
||||
### 🔒 Anonymisation avancée
|
||||
- **Importation DICOM** : Support complet des fichiers et dossiers DICOM
|
||||
- **Édition en temps réel** : Outils de gomme et masquage intuitifs
|
||||
- **Prévisualisation instantanée** : Visualisation des modifications avant export
|
||||
- **Métadonnées** : Nettoyage automatique des informations sensibles
|
||||
|
||||
### 🎨 Interface moderne
|
||||
- **Thèmes dynamiques** : Clair/sombre avec personnalisation complète
|
||||
- **Multi-langues** : Français, anglais, espagnol (extensible)
|
||||
- **Navigation fluide** : Barre d'onglets verticale avec icônes SVG
|
||||
- **Responsive** : Interface adaptative selon la taille d'écran
|
||||
|
||||
### 📤 Export flexible
|
||||
- **Formats multiples** : PNG, PDF, DCM, DICOMDIR
|
||||
- **Métadonnées** : Export JSON et XLS des informations
|
||||
- **Batch processing** : Traitement par lots pour l'efficacité
|
||||
- **Préservation qualité** : Compression optimisée
|
||||
|
||||
### 🔄 Système intelligent
|
||||
- **Mise à jour automatique** : Vérification et téléchargement des nouvelles versions
|
||||
- **Splash screen utile** : Chargement avec feedback en temps réel
|
||||
- **Gestion d'erreurs** : Messages informatifs et récupération gracieuse
|
||||
- **Cache optimisé** : Performance améliorée pour les gros volumes
|
||||
|
||||
---
|
||||
|
||||
## 🗂️ Structure du projet
|
||||
## 🗂️ Architecture du projet
|
||||
|
||||
```
|
||||
Template/
|
||||
HoDA_Radio/
|
||||
├── app/
|
||||
│ ├── core/ # Managers (thème, langue, update, etc.)
|
||||
│ ├── core/ # Gestionnaires principaux
|
||||
│ │ ├── main_manager.py # Chef d'orchestre
|
||||
│ │ ├── dicom_manager.py # Gestion des fichiers DICOM
|
||||
│ │ ├── theme_manager.py # Thèmes et styles
|
||||
│ │ ├── language_manager.py # Traductions
|
||||
│ │ ├── settings_manager.py # Paramètres utilisateur
|
||||
│ │ ├── update_manager.py # Mises à jour
|
||||
│ │ └── alert_manager.py # Messages et alertes
|
||||
│ ├── ui/
|
||||
│ │ ├── widgets/ # Onglets, loading bar, etc.
|
||||
│ │ └── windows/ # Paramètres, suggestion...
|
||||
│ └── utils/ # Fonctions utilitaires
|
||||
│ │ ├── widgets/ # Composants réutilisables
|
||||
│ │ │ ├── tabs_widget.py # Barre d'onglets personnalisée
|
||||
│ │ │ ├── loading_spinner.py # Indicateur de chargement
|
||||
│ │ │ ├── loading_bar.py # Barre de progression
|
||||
│ │ │ └── image_viewer.py # Visualiseur d'images DICOM
|
||||
│ │ └── windows/ # Fenêtres principales
|
||||
│ │ ├── main_window.py # Fenêtre principale
|
||||
│ │ ├── splash_screen.py # Écran de chargement
|
||||
│ │ ├── dicom_window.py # Interface d'édition DICOM
|
||||
│ │ ├── import_window.py # Interface d'importation
|
||||
│ │ ├── export_window.py # Interface d'exportation
|
||||
│ │ ├── settings_window.py # Paramètres
|
||||
│ │ └── suggestion_window.py # Feedback utilisateur
|
||||
│ └── utils/ # Utilitaires
|
||||
│ └── paths.py # Gestion des chemins
|
||||
├── data/
|
||||
│ ├── assets/ # Icônes, images
|
||||
│ ├── lang/ # Traductions
|
||||
│ ├── themes/ # Thèmes
|
||||
│ └── others/ # Autres
|
||||
├── tools/ # Scripts build/dev
|
||||
├── config.json # Config principale
|
||||
├── requirements.txt # Dépendances
|
||||
├── BUILD.spec # PyInstaller
|
||||
└── main.py # Point d’entrée
|
||||
│ ├── assets/ # Ressources (icônes, images)
|
||||
│ ├── lang/ # Fichiers de traduction
|
||||
│ │ ├── fr.json # Français
|
||||
│ │ ├── en.json # Anglais
|
||||
│ │ └── es.json # Espagnol
|
||||
│ ├── themes/ # Thèmes visuels
|
||||
│ │ ├── dark.json # Thème sombre
|
||||
│ │ └── light.json # Thème clair
|
||||
│ └── others/ # Configurations
|
||||
│ └── defaults_settings.json
|
||||
├── tools/ # Scripts de développement
|
||||
│ ├── build.bat # Build Windows
|
||||
│ ├── build.command # Build macOS
|
||||
│ └── open.bat # Environnement de dev
|
||||
├── config.json # Configuration principale
|
||||
├── requirements.txt # Dépendances Python
|
||||
├── BUILD.spec # Configuration PyInstaller
|
||||
└── main.py # Point d'entrée
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚡ Démarrage Express
|
||||
## ⚡ Installation et utilisation
|
||||
|
||||
1. **Configurez `config.json`**
|
||||
(nom, version, OS, architecture, icône, dépôt git...)
|
||||
### Pré-requis
|
||||
- **Python 3.10+** (recommandé 3.11)
|
||||
- **Système** : Windows 10+, macOS 10.15+, ou Linux Ubuntu 20.04+
|
||||
|
||||
2. **Copiez `.env.example` → `.env` et configurez les variables requises**
|
||||
- Depuis la racine du projet :
|
||||
- Windows (PowerShell / cmd) : copy .env.example .env
|
||||
- Linux / macOS : cp .env.example .env
|
||||
- Au minimum, renseignez dans `.env` :
|
||||
- PYTHON_PATH : chemin absolu vers votre exécutable Python (utilisé par tools/open.bat)
|
||||
- les identifiants email si vous comptez utiliser l'envoi de suggestions (email + mot de passe / mot de passe d'application)
|
||||
- Remarque : l'outil `tools/open.bat` s'appuie sur PYTHON_PATH ; sans cette variable, l'ouverture/initialisation de l'environnement échouera.
|
||||
### 🚀 Démarrage rapide
|
||||
|
||||
3. **Lancez le dev**
|
||||
- Windows : tools\open.bat (nécessite PYTHON_PATH dans `.env`)
|
||||
- Linux : tools/open.sh (si présent / exécutable)
|
||||
- macOS : tools/open.command (si présent / exécutable)
|
||||
- Exécutez depuis leur fichier parent pour que les chemins relatifs fonctionnent correctement.
|
||||
1. **Clonez le projet**
|
||||
```bash
|
||||
git clone https://gitea.louismazin.ovh/LouisMazin/HoDA_Radio.git
|
||||
cd HoDA_Radio
|
||||
```
|
||||
|
||||
4. **Build en un clic**
|
||||
- Windows : tools\build.bat
|
||||
- Linux : tools/build.sh
|
||||
- macOS : tools/build.command
|
||||
- Ces scripts supposent que `.env` est configuré et que les outils requis (pyinstaller, etc.) sont installés.
|
||||
2. **Configurez l'environnement**
|
||||
```bash
|
||||
# Copiez le fichier d'environnement
|
||||
cp .env.example .env
|
||||
|
||||
# Éditez .env avec vos paramètres
|
||||
# PYTHON_PATH=/chemin/vers/python
|
||||
# EMAIL_USER=votre.email@gmail.com (optionnel)
|
||||
# EMAIL_PASSWORD=votre_mot_de_passe (optionnel)
|
||||
```
|
||||
|
||||
3. **Lancez l'application**
|
||||
- **Windows** : Double-cliquez sur `tools\open.bat`
|
||||
- **macOS** : Exécutez `tools/open.command`
|
||||
- **Linux** : Exécutez `tools/open.sh`
|
||||
|
||||
### 📦 Build de production
|
||||
|
||||
1. **Build automatique**
|
||||
- **Windows** : `tools\build.bat`
|
||||
- **macOS** : `tools/build.command`
|
||||
- **Linux** : `tools/build.sh`
|
||||
|
||||
2. **Fichier généré**
|
||||
- L'exécutable se trouve dans le dossier `build/`
|
||||
- Nom automatique : `HoDA_Radio-{OS}-{ARCH}-v{VERSION}`
|
||||
|
||||
---
|
||||
|
||||
## 🎨 Thèmes & 🌍 Langues
|
||||
## 🎯 Utilisation de l'application
|
||||
|
||||
- **Thèmes** : Ajoutez vos fichiers dans `data/themes/` (JSON).
|
||||
Changez les couleurs, créez votre ambiance !
|
||||
- **Langues** : Ajoutez vos fichiers dans `data/lang/` (JSON).
|
||||
Traduisez tout, c’est instantané.
|
||||
### 1. **Import des images DICOM**
|
||||
- Glissez-déposez vos fichiers ou dossiers DICOM
|
||||
- Support des archives et structures complexes
|
||||
- Validation automatique des formats
|
||||
|
||||
### 2. **Anonymisation interactive**
|
||||
- Utilisez l'outil gomme pour masquer les zones sensibles
|
||||
- Prévisualisez en temps réel vos modifications
|
||||
- Ajustez la taille du pinceau selon vos besoins
|
||||
|
||||
### 3. **Export sécurisé**
|
||||
- Choisissez vos formats de sortie
|
||||
- Sélectionnez les images à exporter
|
||||
- Nettoyage automatique des métadonnées
|
||||
|
||||
---
|
||||
|
||||
## 🧩 Managers & Architecture
|
||||
## 🎨 Personnalisation
|
||||
|
||||
- **MainManager** : Le chef d’orchestre.
|
||||
- **SettingsManager** : Les préférences utilisateur.
|
||||
- **ThemeManager** : Les couleurs et le style.
|
||||
- **LanguageManager** : Les textes traduits.
|
||||
- **AlertManager** : Les messages, confirmations, erreurs.
|
||||
- **UpdateManager** : Les mises à jour automatiques.
|
||||
- **ObserverManager** : Les notifications internes.
|
||||
### Thèmes personnalisés
|
||||
Créez votre thème dans `data/themes/mon_theme.json` :
|
||||
```json
|
||||
{
|
||||
"theme_name": "Mon Thème",
|
||||
"colors": {
|
||||
"background_color": "#1a1a1a",
|
||||
"primary_color": "#007acc",
|
||||
"text_color": "#ffffff"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Nouvelles langues
|
||||
Ajoutez une traduction dans `data/lang/code_langue.json` :
|
||||
```json
|
||||
{
|
||||
"lang_name": "Deutsch",
|
||||
"language": "Sprache :",
|
||||
"theme": "Thema :",
|
||||
"loading": "Laden..."
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Développement avancé
|
||||
|
||||
### Architecture des managers
|
||||
- **MainManager** : Singleton coordonnant tous les autres managers
|
||||
- **DicomManager** : Logique métier pour les fichiers médicaux
|
||||
- **ThemeManager** : Système de thèmes dynamiques avec cache
|
||||
- **ObserverManager** : Pattern Observer pour les notifications inter-composants
|
||||
|
||||
### Ajout de fonctionnalités
|
||||
1. Créez votre fenêtre dans `app/ui/windows/`
|
||||
2. Ajoutez-la aux onglets dans `main_window.py`
|
||||
3. Implémentez les notifications de thème/langue si nécessaire
|
||||
|
||||
### Tests et debugging
|
||||
```bash
|
||||
# Mode développement avec logs
|
||||
python main.py --debug
|
||||
|
||||
# Tests unitaires (si implémentés)
|
||||
python -m pytest tests/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📋 Dépendances principales
|
||||
|
||||
- **PyQt6** : Interface graphique moderne
|
||||
- **pydicom** : Manipulation des fichiers DICOM
|
||||
- **pillow** : Traitement d'images
|
||||
- **numpy** : Calculs numériques
|
||||
- **requests** : Mises à jour automatiques
|
||||
- **packaging** : Gestion des versions
|
||||
- **python-dotenv** : Variables d'environnement
|
||||
|
||||
---
|
||||
|
||||
## 🔒 Sécurité et confidentialité
|
||||
|
||||
### Protection des données
|
||||
- **Traitement local** : Aucune donnée médicale envoyée sur internet
|
||||
- **Cache temporaire** : Nettoyage automatique à la fermeture
|
||||
- **Métadonnées** : Suppression des informations personnelles
|
||||
|
||||
### Bonnes pratiques
|
||||
- Utilisez des mots de passe d'application pour Gmail
|
||||
- Ne versionnez jamais le fichier `.env`
|
||||
- Vérifiez les exports avant diffusion
|
||||
|
||||
---
|
||||
|
||||
## 🔄 Mise à jour automatique
|
||||
|
||||
- Vérifie la dernière version sur le dépôt Git à chaque démarrage.
|
||||
- Propose la mise à jour si disponible.
|
||||
- Télécharge le bon fichier selon votre OS/architecture.
|
||||
- Affiche une barre de progression stylée.
|
||||
- Lance la nouvelle version automatiquement !
|
||||
L'application vérifie automatiquement les nouvelles versions :
|
||||
- **Au démarrage** : Vérification silencieuse
|
||||
- **Notification** : Proposition de téléchargement si disponible
|
||||
- **Installation** : Téléchargement avec barre de progression
|
||||
- **Compatibilité** : Détection automatique OS/architecture
|
||||
|
||||
---
|
||||
|
||||
## 💡 Suggestions & Feedback
|
||||
## 🆘 Support et contribution
|
||||
|
||||
- Fenêtre dédiée pour envoyer vos idées ou questions par email.
|
||||
- Sécurisé via `.env`.
|
||||
- Gestion des erreurs/succès avec AlertManager.
|
||||
### Signaler un problème
|
||||
1. Utilisez l'onglet "Suggestion" dans l'application
|
||||
2. Ou ouvrez une issue sur [Gitea](https://gitea.louismazin.ovh/LouisMazin/HoDA_Radio)
|
||||
3. Incluez : OS, version Python, logs d'erreur, étapes de reproduction
|
||||
|
||||
---
|
||||
### Contribuer
|
||||
1. Forkez le dépôt
|
||||
2. Créez une branche feature/fix
|
||||
3. Testez vos modifications
|
||||
4. Soumettez une pull request
|
||||
|
||||
## 🛠️ Ajouter vos fenêtres & widgets
|
||||
|
||||
- Créez une classe héritant de `QWidget`.
|
||||
- Ajoutez-la dans la barre d’onglets (`TabsWidget`).
|
||||
- Abonnez-vous aux notifications pour la langue/le thème.
|
||||
|
||||
---
|
||||
|
||||
## 📦 Dépendances
|
||||
|
||||
- **Python 3.10+ recommandé** (compatibilité testée avec 3.10/3.11).
|
||||
- **PyQt6** : GUI moderne.
|
||||
- **pyinstaller** : Build d’exécutables.
|
||||
- **python-dotenv** : Variables d’environnement.
|
||||
- **requests** : Requêtes HTTP (update).
|
||||
|
||||
---
|
||||
|
||||
## 🔒 Sécurité
|
||||
|
||||
- **Ne versionnez jamais `.env`** (déjà dans `.gitignore`).
|
||||
- Utilisez un mot de passe d’application pour Gmail.
|
||||
### Règles de contribution
|
||||
- Code Python PEP 8 compliant
|
||||
- Documentation des nouvelles fonctionnalités
|
||||
- Tests unitaires pour la logique métier critique
|
||||
- Respect de l'architecture existante
|
||||
|
||||
---
|
||||
|
||||
## 📝 Licence
|
||||
|
||||
Attribution License — voir le fichier local [`LICENSE`](https://gitea.louismazin.ovh/LouisMazin/PythonApplicationTemplate/src/branch/main/LICENSE) pour le texte complet.
|
||||
Toute utilisation ou distribution doit inclure une attribution visible à l'auteur : LouisMazin.
|
||||
**Attribution License** - voir [LICENSE](LICENSE) pour les détails complets.
|
||||
|
||||
Toute utilisation, reproduction ou distribution doit inclure une attribution visible à l'auteur : **LouisMazin**.
|
||||
|
||||
---
|
||||
|
||||
## 🤝 Contribution
|
||||
## 🙏 Remerciements
|
||||
|
||||
1. Forkez le dépôt
|
||||
2. Créez une branche
|
||||
3. Proposez vos modifications via pull request
|
||||
- **PyQt6** pour le framework d'interface
|
||||
- **pydicom** pour la manipulation des fichiers médicaux
|
||||
- **Communauté open source** pour les contributions et retours
|
||||
|
||||
---
|
||||
|
||||
## 🆘 Support
|
||||
|
||||
- Ouvrez une issue sur le dépôt
|
||||
- Précisez votre OS, version Python, logs d’erreur
|
||||
|
||||
---
|
||||
|
||||
**Ce template est fait pour vous faire gagner du temps et coder avec plaisir !
|
||||
Testez-le, améliorez-le, partagez-le 🚀**
|
||||
**HoDA Radio - Protégez la confidentialité, préservez l'utilité médicale** 🚀
|
@ -24,8 +24,16 @@ class MainWindow(QMainWindow):
|
||||
self.observer_manager.subscribe(NotificationType.THEME, self.update_theme)
|
||||
self.observer_manager.subscribe(NotificationType.DICOM, self.goto_dicom_window)
|
||||
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
|
||||
@ -33,13 +41,21 @@ class MainWindow(QMainWindow):
|
||||
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"""
|
||||
|
@ -111,6 +111,8 @@ class ExportWindow(QWidget):
|
||||
def show_dicom_selection_popup(self):
|
||||
dicom_list = self.export_manager.get_export_dicom_list()
|
||||
if not dicom_list:
|
||||
# Afficher une alerte si aucune donnée n'est disponible
|
||||
self.alert_manager.show_error("no_dicoms_to_export")
|
||||
return
|
||||
|
||||
popup = QDialog(self)
|
||||
|
@ -294,10 +294,18 @@ class ImportWindow(QWidget):
|
||||
self.observer_manager.notify(NotificationType.DICOM)
|
||||
|
||||
def select_file(self):
|
||||
self.input_entry.setText(QFileDialog.getOpenFileName(self, self.language_manager.get_text("select_image"), get_current_dir(), "DICOMDIR Files (DICOMDIR) ;; DICOM Files (*.dcm)")[0])
|
||||
path = QFileDialog.getOpenFileName(self, self.language_manager.get_text("select_image"), get_current_dir(), "DICOMDIR Files (DICOMDIR) ;; DICOM Files (*.dcm)")[0]
|
||||
if path:
|
||||
self.input_entry.setText(path)
|
||||
else:
|
||||
self.input_entry.setText(self.language_manager.get_text("path_placeholder"))
|
||||
|
||||
def select_folder(self):
|
||||
self.input_entry.setText(QFileDialog.getExistingDirectory(self, self.language_manager.get_text("select_folder"), get_current_dir()))
|
||||
path = QFileDialog.getExistingDirectory(self, self.language_manager.get_text("select_folder"), get_current_dir())
|
||||
if path:
|
||||
self.input_entry.setText(path)
|
||||
else:
|
||||
self.input_entry.setText(self.language_manager.get_text("path_placeholder"))
|
||||
|
||||
def find_dicoms_from_files(self, path):
|
||||
if isinstance(path, tuple):
|
||||
|
@ -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.finished.emit()
|
||||
self.close()
|
||||
|
||||
def show_splash(self):
|
||||
"""Affiche le splash screen"""
|
||||
self.show()
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 53 KiB |
Binary file not shown.
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 53 KiB |
@ -73,5 +73,10 @@
|
||||
"error_loading_image": "Error loading image : {x}.",
|
||||
"export_metadata_error": "Error exporting metadata. Some files may have been exported.",
|
||||
"export_partial_success": "Export partially successful. Some files could not be exported.",
|
||||
"choose_export_destination": "Choose export destination folder"
|
||||
"choose_export_destination": "Choose export destination folder",
|
||||
"loading": "Loading...",
|
||||
"checking_updates": "Checking for updates...",
|
||||
"initializing": "Initializing...",
|
||||
"loading_complete": "Loading complete",
|
||||
"no_dicoms_to_export": "No DICOM files available for export. Please import DICOM files first."
|
||||
}
|
@ -73,5 +73,10 @@
|
||||
"error_loading_image": "Error al cargar la imagen: {x}.",
|
||||
"export_metadata_error": "Error al exportar metadatos. Algunos archivos pueden haber sido exportados.",
|
||||
"export_partial_success": "Exportación parcialmente exitosa. Algunos archivos no pudieron ser exportados.",
|
||||
"choose_export_destination": "Elige la carpeta de destino para la exportación"
|
||||
"choose_export_destination": "Elige la carpeta de destino para la exportación",
|
||||
"loading": "Cargando...",
|
||||
"checking_updates": "Verificando actualizaciones...",
|
||||
"initializing": "Inicializando...",
|
||||
"loading_complete": "Carga completa",
|
||||
"no_dicoms_to_export": "No hay archivos DICOM disponibles para exportar. Por favor, importe archivos DICOM primero."
|
||||
}
|
@ -73,5 +73,10 @@
|
||||
"error_loading_image": "Erreur lors du chargement de l'image : {x}.",
|
||||
"export_metadata_error": "Erreur lors de l'exportation des métadonnées. Certains fichiers ont pu être exportés.",
|
||||
"export_partial_success": "Exportation partiellement réussie. Certains fichiers n'ont pas pu être exportés.",
|
||||
"choose_export_destination": "Choisissez le dossier de destination pour l'exportation"
|
||||
"choose_export_destination": "Choisissez le dossier de destination pour l'exportation",
|
||||
"loading": "Chargement...",
|
||||
"checking_updates": "Vérification des mises à jour...",
|
||||
"initializing": "Initialisation de l'interface...",
|
||||
"loading_complete": "Chargement terminé",
|
||||
"no_dicoms_to_export": "Aucun fichier DICOM disponible pour l'exportation. Veuillez d'abord importer des fichiers DICOM."
|
||||
}
|
81
main.py
81
main.py
@ -6,11 +6,54 @@ from app.ui.main_window import MainWindow
|
||||
from app.ui.windows.splash_screen import SplashScreen
|
||||
from app.core.main_manager import MainManager
|
||||
|
||||
# Variable globale pour stocker la fenêtre préchargée
|
||||
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()
|
||||
|
||||
# Étape 1: Vérification des mises à jour
|
||||
progress_callback(language_manager.get_text("checking_updates"))
|
||||
|
||||
# Vérifier s'il y a une mise à jour
|
||||
if update_manager.check_for_update():
|
||||
# Une mise à jour est en cours de téléchargement, on ferme l'app
|
||||
return False
|
||||
|
||||
# Étape 2: Initialisation de la fenêtre principale
|
||||
progress_callback(language_manager.get_text("initializing"))
|
||||
|
||||
# Créer la fenêtre principale avec tous ses composants
|
||||
preloaded_window = MainWindow()
|
||||
|
||||
# Étape 3: Finalisation
|
||||
progress_callback(language_manager.get_text("loading_complete"))
|
||||
|
||||
return True
|
||||
|
||||
except Exception:
|
||||
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())
|
||||
@ -22,24 +65,38 @@ def main() -> int:
|
||||
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)
|
||||
# Créer et afficher le splash screen avec préchargement
|
||||
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():
|
||||
# Connecter le signal finished pour afficher la fenêtre principale préchargée
|
||||
def on_splash_finished(success):
|
||||
global preloaded_window
|
||||
if not success:
|
||||
# Le préchargement a échoué ou une mise à jour est en cours
|
||||
app.quit()
|
||||
return
|
||||
window: MainWindow = MainWindow()
|
||||
window.show()
|
||||
|
||||
# Afficher la fenêtre préchargée
|
||||
if preloaded_window:
|
||||
preloaded_window.show()
|
||||
else:
|
||||
# Fallback si le préchargement a échoué
|
||||
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():
|
||||
# Pas de splash screen, exécuter le préchargement directement
|
||||
def dummy_progress(text):
|
||||
pass # Pas de callback de progression sans splash
|
||||
|
||||
success = preload_application(dummy_progress)
|
||||
if not success:
|
||||
return 0
|
||||
window: MainWindow = MainWindow()
|
||||
|
||||
# Utiliser la fenêtre préchargée ou en créer une nouvelle
|
||||
window = preloaded_window if preloaded_window else MainWindow()
|
||||
window.show()
|
||||
|
||||
return app.exec()
|
||||
|
Loading…
x
Reference in New Issue
Block a user