diff --git a/README.md b/README.md new file mode 100644 index 0000000..d6a29da --- /dev/null +++ b/README.md @@ -0,0 +1,200 @@ +# Python Application Template + +> Un socle desktop PyQt6 pret a personnaliser pour lancer rapidement une application Python moderne sous Windows. + +Ce template fournit une base complete pour une application de bureau : fenetre principale, ecran de chargement, themes, traductions, gestion des parametres, mises a jour et generation d'un executable avec PyInstaller. + +## Apercu + +- Interface graphique construite avec **PyQt6** +- Themes `dark` et `light` facilement extensibles +- Traductions francaise et anglaise +- Ecran de demarrage avec progression +- Parametres persistants entre les lancements +- Verification des mises a jour depuis GitHub ou Gitea +- Onglet de suggestions avec envoi SMTP asynchrone +- Build automatise pour Windows et script prevu pour macOS/Linux + +## Demarrage rapide + +### Prerequis + +- Python 3 disponible dans le `PATH` +- Windows pour utiliser les scripts `.bat` +- PowerShell pour la lecture de `config.json` par les scripts Windows +- VS Code et la commande `code` pour utiliser `tools/open.bat` + +### Installation manuelle + +Depuis la racine du projet : + +```powershell +python -m venv .venv +.\.venv\Scripts\Activate.ps1 +python -m pip install --upgrade pip +python -m pip install -r config\requirements.txt +python main.py +``` + +Si PowerShell bloque l'activation de l'environnement, lancez directement : + +```powershell +.venv\Scripts\python.exe main.py +``` + +### Installation automatisee sous Windows + +Le script suivant cree l'environnement dedie dans `config/WINenv_x64`, installe les dependances puis ouvre le projet dans VS Code : + +```powershell +tools\open.bat +``` + +> `open.bat` prepare le workspace mais ne lance pas directement l'application. Utilisez ensuite `python main.py` depuis l'environnement actif. + +## Configuration + +### Identite de l'application + +Les informations generales sont centralisees dans [config.json](config.json) : + +```json +{ + "app_name": "Application", + "app_os": "Windows", + "app_version": "1.0.0", + "architecture": "x64", + "icon_path": "data/assets/icon.ico", + "splash_image": "splash", + "main_script": "main.py", + "git_repo": "https://example.com/owner/repository" +} +``` + +Adaptez au minimum `app_name`, `app_version`, `icon_path` et `git_repo` avant de distribuer votre application. + +### Variables d'environnement + +Copiez `.env.example` vers `.env`, puis renseignez les valeurs necessaires : + +```powershell +Copy-Item .env.example .env +``` + +Variables disponibles : + +| Variable | Role | +| --- | --- | +| `PYTHON_PATH` | Interpreteur Python utilise par les scripts de build | +| `EMAIL_ADDRESS` | Adresse SMTP utilisee pour recevoir les suggestions | +| `EMAIL_PASSWORD` | Mot de passe applicatif SMTP | +| `EMAIL_SMTP_SERVER` | Serveur SMTP, par defaut `smtp.gmail.com` | +| `EMAIL_SMTP_PORT` | Port SMTP, par defaut `587` | + +Ne committez jamais `.env` ni un mot de passe SMTP. Pour Gmail, utilisez un mot de passe d'application plutot que le mot de passe principal du compte. + +## Execution et build + +### Lancer l'application + +```powershell +python main.py +``` + +Au demarrage, l'application initialise ses gestionnaires, verifie les mises a jour disponibles, puis affiche la fenetre principale. + +### Generer l'executable Windows + +```powershell +tools\build.bat +``` + +Le script : + +1. cree l'environnement virtuel correspondant a l'architecture configuree ; +2. installe les dependances ; +3. execute PyInstaller avec `config/BUILD.spec` ; +4. place l'application generee dans `build/`. + +Le resultat est un executable sans console, adapte a une application graphique Windows. + +### macOS et Linux + +Les scripts Unix sont maintenant disponibles pour macOS et Linux. + +Pour preparer l'environnement et ouvrir le projet : + +```bash +bash tools/open.command +``` + +Le script choisit automatiquement `MACenv_` sur macOS ou `LINenv_` sur Linux, installe les dependances, puis ouvre le projet avec la commande `code`. A defaut, il utilise `open` sur macOS ou `xdg-open` sur Linux. + +Pour construire l'application : + +```bash +bash tools/build.command +``` + +Sur macOS, il utilise un environnement `MACenv_`, produit une application `.app` et cree une archive ZIP dans `build/`. Sur Linux, il utilise `LINenv_`, produit un binaire executable ainsi qu'un fichier `.desktop`, puis cree une archive ZIP dans `build/`. + +La commande `zip` doit etre installee sur le systeme pour generer l'archive. + +## Structure du projet + +```text +. +├── app/ +│ ├── core/ # Gestionnaires : langue, theme, parametres, mises a jour... +│ ├── ui/ # Fenetre principale, ecrans et widgets PyQt6 +│ └── utils/ # Utilitaires partages, notamment les chemins +├── config/ +│ ├── BUILD.spec # Configuration PyInstaller +│ └── requirements.txt # Dependances Python +├── data/ +│ ├── assets/ # Icones et images +│ ├── lang/ # Fichiers de traduction JSON +│ ├── themes/ # Themes JSON et feuilles QSS +│ └── others/ # Valeurs par defaut +├── tools/ # Scripts d'installation, lancement et build +├── config.json # Configuration de l'application +└── main.py # Point d'entree +``` + +## Personnalisation + +1. Modifiez `config.json` pour donner son identite a l'application. +2. Ajoutez ou adaptez les textes dans `data/lang/en.json` et `data/lang/fr.json`. +3. Creez un theme dans `data/themes/` et referencez-le dans le gestionnaire de themes. +4. Ajoutez vos vues dans `app/ui/` et votre logique metier dans `app/core/`. +5. Ajoutez les ressources necessaires dans `data/assets/`. +6. Mettez a jour `config/BUILD.spec` si votre application embarque de nouvelles ressources ou dependances dynamiques. + +## Architecture simplifiee + +```mermaid +flowchart TD + A[main.py] --> B[MainManager] + B --> C[SettingsManager] + B --> D[LanguageManager] + B --> E[ThemeManager] + B --> F[UpdateManager] + A --> G[SplashScreen] + A --> H[MainWindow] + H --> I[SettingsWindow] + H --> J[SuggestionWindow] + D --> K[data/lang] + E --> L[data/themes] + F --> M[GitHub ou Gitea] +``` + +## Dependances principales + +- [PyQt6](https://pypi.org/project/PyQt6/) : interface graphique +- [PyInstaller](https://pyinstaller.org/) : generation de l'executable +- [Requests](https://pypi.org/project/requests/) : appels HTTP pour les mises a jour +- [python-dotenv](https://pypi.org/project/python-dotenv/) : chargement des variables d'environnement + +## Licence + +Aucune licence n'est actuellement definie dans le depot. Ajoutez un fichier `LICENSE` avant toute distribution publique. diff --git a/config/BUILD.spec b/config/BUILD.spec index ceac592..e179ac0 100644 --- a/config/BUILD.spec +++ b/config/BUILD.spec @@ -29,8 +29,10 @@ datas = [ (str(PROJECT_DIR / "data/assets/*"), "data/assets/"), (str(PROJECT_DIR / "data/"), "data/"), (str(PROJECT_DIR / "config.json"), "."), - (str(PROJECT_DIR / ".env"), "."), ] +env_file = PROJECT_DIR / ".env" +if env_file.exists(): + datas.append((str(env_file), ".")) binaries = [] # --- Analysis --- @@ -96,7 +98,7 @@ elif sys.platform.startswith("linux"): desktop_content = f"""[Desktop Entry] Type=Application Name={app_name} -Exec={dist_bin} +Exec={name} Icon={icon if icon else "application-default-icon"} Terminal=false Categories=Utility; diff --git a/tools/build.command b/tools/build.command index 8f76d90..5f44922 100644 --- a/tools/build.command +++ b/tools/build.command @@ -1,88 +1,92 @@ #!/bin/bash -set -e +set -euo pipefail + SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -PARENT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +CONFIG_DIR="$PROJECT_DIR/config" +CONFIG_FILE="$PROJECT_DIR/config.json" +ENV_FILE="$PROJECT_DIR/.env" +BUILD_DIR="$PROJECT_DIR/build" +ICON_FILE="$PROJECT_DIR/data/assets/icon.png" -CONFIG_DIR="$PARENT_DIR/config" -CONFIG_FILE="$PARENT_DIR/config.json" -ICON_FILE="$PARENT_DIR/data/assets/icon.png" - -if [ -f "$PARENT_DIR/.env" ]; then +if [ -f "$ENV_FILE" ]; then set -a - . "$PARENT_DIR/.env" + # shellcheck disable=SC1090 + . "$ENV_FILE" set +a fi -# === Extract values from config.json === -ICON_PATH=$(python3 -c "import json; print(json.load(open('$CONFIG_FILE'))['icon_path'])") -APP_NAME=$(python3 -c "import json; print(json.load(open('$CONFIG_FILE'))['app_name'])") -ARCHITECTURE=$(python3 -c "import json; print(json.load(open('$CONFIG_FILE'))['architecture'])") - -# === Use the Python interpreter provided by the environment === SYSTEM_PYTHON="${PYTHON_PATH:-python3}" - -VENV_PATH="$CONFIG_DIR/MACenv_$ARCHITECTURE" -APP_FILE="$APP_NAME.app" -PYTHON_IN_VENV="$VENV_PATH/bin/python" -BUILD_DIR="$PARENT_DIR/build" -ZIP_FILE="$BUILD_DIR/$APP_NAME.zip" - -# === Verify Python existence === if ! command -v "$SYSTEM_PYTHON" >/dev/null 2>&1; then - echo "[ERROR] Python interpreter not found in PATH." + echo "[ERROR] Python introuvable : $SYSTEM_PYTHON" >&2 + exit 1 +fi +if ! command -v zip >/dev/null 2>&1; then + echo "[ERROR] La commande zip est requise pour creer l'archive." >&2 exit 1 fi -# === Check virtual environment === -if [ ! -f "$VENV_PATH/bin/activate" ]; then - echo "[INFO] Virtual environment not found. Creating..." - "$SYSTEM_PYTHON" -m venv "$VENV_PATH" +APP_NAME="$("$SYSTEM_PYTHON" -c "import json; print(json.load(open(r'$CONFIG_FILE', encoding='utf-8'))['app_name'])")" +ARCHITECTURE="$("$SYSTEM_PYTHON" -c "import json; print(json.load(open(r'$CONFIG_FILE', encoding='utf-8'))['architecture'])")" +OS_NAME="$(uname -s)" + +case "$OS_NAME" in + Darwin) + ENV_NAME="MACenv_${ARCHITECTURE}" + APP_FILE="$APP_NAME.app" + ;; + Linux) + ENV_NAME="LINenv_${ARCHITECTURE}" + APP_FILE="$APP_NAME" + ;; + *) + echo "[ERROR] Systeme non supporte : $OS_NAME" >&2 + exit 1 + ;; +esac + +ENV_PATH="$CONFIG_DIR/$ENV_NAME" +PYTHON_IN_VENV="$ENV_PATH/bin/python" +ZIP_FILE="$BUILD_DIR/${APP_NAME}-${OS_NAME}.zip" + +if [ ! -x "$PYTHON_IN_VENV" ]; then + echo "[INFO] Creation de l'environnement $ENV_NAME..." + "$SYSTEM_PYTHON" -m venv "$ENV_PATH" "$PYTHON_IN_VENV" -m pip install --upgrade pip "$PYTHON_IN_VENV" -m pip install -r "$CONFIG_DIR/requirements.txt" else - echo "[INFO] Virtual environment found." + echo "[INFO] Environnement $ENV_NAME trouve." fi -# === Run PyInstaller === +mkdir -p "$BUILD_DIR" +cd "$PROJECT_DIR" "$PYTHON_IN_VENV" -m PyInstaller \ - --distpath "$BUILD_DIR" \ - --workpath "$BUILD_DIR/dist" \ - --clean \ + --distpath "$BUILD_DIR" \ + --workpath "$BUILD_DIR/dist" \ + --clean \ "$CONFIG_DIR/BUILD.spec" - -# === Clean build cache === rm -rf "$BUILD_DIR/dist" -# === Create ZIP === -echo "[INFO] Creating ZIP archive..." +if [ ! -e "$BUILD_DIR/$APP_FILE" ]; then + echo "[ERROR] Artefact attendu introuvable : $BUILD_DIR/$APP_FILE" >&2 + exit 1 +fi TEMP_ZIP_DIR="$BUILD_DIR/temp_zip" - -# Remove old temp dir rm -rf "$TEMP_ZIP_DIR" mkdir -p "$TEMP_ZIP_DIR" - -# Move compiled app mv "$BUILD_DIR/$APP_FILE" "$TEMP_ZIP_DIR/" - -# Copy config.json cp "$CONFIG_FILE" "$TEMP_ZIP_DIR/" - -# Copy icon.png cp "$ICON_FILE" "$TEMP_ZIP_DIR/" +cp -R "$PROJECT_DIR/data/lang" "$TEMP_ZIP_DIR/lang" -# Copy data/lang -cp -R "$PARENT_DIR/data/lang" "$TEMP_ZIP_DIR/lang" +if [ "$OS_NAME" = "Linux" ] && [ -f "$BUILD_DIR/$APP_NAME.desktop" ]; then + cp "$BUILD_DIR/$APP_NAME.desktop" "$TEMP_ZIP_DIR/" +fi -# Remove old ZIP rm -f "$ZIP_FILE" - -# Create ZIP -cd "$BUILD_DIR" -zip -r "$ZIP_FILE" "temp_zip" >/dev/null - -# Remove temp folder +(cd "$BUILD_DIR" && zip -r "$ZIP_FILE" "temp_zip" >/dev/null) rm -rf "$TEMP_ZIP_DIR" -echo "[INFO] ZIP created at: $ZIP_FILE" \ No newline at end of file +echo "[INFO] Archive creee : $ZIP_FILE" \ No newline at end of file diff --git a/tools/open.command b/tools/open.command index e69de29..a1edee2 100644 --- a/tools/open.command +++ b/tools/open.command @@ -0,0 +1,51 @@ +#!/bin/bash + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +CONFIG_DIR="$PROJECT_DIR/config" +CONFIG_FILE="$PROJECT_DIR/config.json" +ENV_FILE="$PROJECT_DIR/.env" + +if [ -f "$ENV_FILE" ]; then + set -a + # shellcheck disable=SC1090 + . "$ENV_FILE" + set +a +fi + +SYSTEM_PYTHON="${PYTHON_PATH:-python3}" +if ! command -v "$SYSTEM_PYTHON" >/dev/null 2>&1; then + echo "[ERROR] Python introuvable : $SYSTEM_PYTHON" >&2 + exit 1 +fi + +ARCHITECTURE="$("$SYSTEM_PYTHON" -c "import json; print(json.load(open(r'$CONFIG_FILE', encoding='utf-8'))['architecture'])")" +if [ "$(uname -s)" = "Darwin" ]; then + ENV_NAME="MACenv_${ARCHITECTURE}" +else + ENV_NAME="LINenv_${ARCHITECTURE}" +fi +ENV_PATH="$CONFIG_DIR/$ENV_NAME" +PYTHON_IN_VENV="$ENV_PATH/bin/python" + +if [ ! -x "$PYTHON_IN_VENV" ]; then + echo "[INFO] Creation de l'environnement $ENV_NAME..." + "$SYSTEM_PYTHON" -m venv "$ENV_PATH" + "$PYTHON_IN_VENV" -m pip install --upgrade pip + "$PYTHON_IN_VENV" -m pip install -r "$CONFIG_DIR/requirements.txt" +else + echo "[INFO] Environnement $ENV_NAME trouve." +fi + +if command -v code >/dev/null 2>&1; then + code "$PROJECT_DIR" +elif [ "$(uname -s)" = "Darwin" ] && command -v open >/dev/null 2>&1; then + open -a "Visual Studio Code" "$PROJECT_DIR" +elif command -v xdg-open >/dev/null 2>&1; then + xdg-open "$PROJECT_DIR" +else + echo "[ERROR] Impossible d'ouvrir le projet : installez VS Code (commande code) ou xdg-open." >&2 + exit 1 +fi