Tarification + écran récap avec prix

Config admin :
- Tarification activable avec paliers (ex: 1-5 = 0.50€, 6-10 = 0.40€)
- Fonction calculate_price() utilitaire

Galerie :
- Affiche "X impressions — Y.YY €" quand tarification activée

Écran impression/récap :
- Galerie des photos sélectionnées avec miniatures + quantité (xN)
- Prix total en haut
- Choix du format d'impression
- Bouton IMPRIMER

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-21 23:54:10 +01:00
parent 2f96447919
commit c2c27b1892
5 changed files with 277 additions and 175 deletions

View File

@@ -7,86 +7,109 @@ Borne d'impression photo tactile. Les utilisateurs importent leurs photos (USB,
Ce n'est PAS un photobooth (pas de capture photo). C'est une station de transfert/impression : les utilisateurs arrivent avec leurs propres photos et la borne permet de les éditer et imprimer.
## Stack technique
- **Interface** : PyQt6 / QGraphicsScene (natif, léger, tactile — choisi au lieu de Chromium pour économiser la RAM sur RPi 3)
- **Interface** : PyQt6 / QGraphicsScene (natif, léger, tactile)
- **Traitement image** : Pillow (effets, redimensionnement, export)
- **Impression** : CUPS via pycups
- **Hotspot WiFi** : hostapd + dnsmasq + Flask (mini serveur upload)
- **Serveur upload** : Flask (thread daemon, page upload mobile-friendly)
- **Email** : IMAP polling (thread daemon)
- **QR** : qrcode (Python)
## Matériel
- **Prototype** : Raspberry Pi 3B+ (1GB RAM) — en stock chez Jules
- **Cible** : Portable vers RPi 4 ou 5 sans aucun changement de code
- **Écran tactile** : en stock chez Jules (modèle à déterminer)
- **Écran tactile** : en stock, 10" 4/3 visé (interface responsive)
- **Imprimante sublimation** : à acheter (pistes : DNP DS620A, Mitsubishi CP-D90DW)
- **Pas de capture photo** — les utilisateurs apportent leurs propres photos
## Architecture
```
src/
main.py — Point d'entrée (--kiosk pour plein écran)
main.py — Point d'entrée (--kiosk pour plein écran)
services/
usb_watcher.py — Détection USB par polling (findmnt + scan récursif)
usb_watcher.py — Détection USB par polling
upload_server.py — Serveur Flask d'upload (WiFi + QR)
imap_watcher.py — Polling IMAP pour réception email
ui/
main_window.py — Navigation QStackedWidget (4 écrans)
style.py — Styles responsive (tailles en % de la fenêtre)
main_window.py — Navigation QStackedWidget (4 écrans)
screens/
home_screen.py — Accueil, 4 boutons import, status USB live
import_screen.py — Galerie de photos (grille scrollable)
editor_screen.py — Éditeur complet (voir fonctionnalités ci-dessous)
print_screen.py — Prévisualisation + choix format + impression
home_screen.py — Accueil, 4 modes, config admin (appui long 5s)
import_screen.py — Galerie photos (cartes, quantités, pastille ratio)
editor_screen.py — Éditeur (crop ratio, rotation, filtres, goodies, multi-pose, texte)
print_screen.py — Choix format, recadrage ratio, impression
widgets/
crop_overlay.py — Rectangle de crop avec poignées tactiles + grille des tiers
goodies_panel.py — Panneau latéral stickers/cadres (charge depuis assets/)
multipose_dialog.py — 5 templates multi-pose avec prévisualisation
assets/
goodies/ — Stickers PNG (à peupler)
frames/ — Cadres photo PNG (à peupler)
fonts/ — Polices custom
data/
photos/ — Photos importées (gitignored)
exports/ — Exports PNG (gitignored)
config/ — Configuration (imprimante, WiFi, etc.)
crop_overlay.py — Rectangle de crop ratio verrouillé, bord à bord
goodies_panel.py — Panneau stickers/cadres
multipose_dialog.py — Templates multi-pose
config_dialog.py — Config admin complète
```
## Fonctionnalités implémentées (2026-03-21)
- **Import USB** : polling montages, scan récursif photos (jpg/png/bmp/tiff/webp/heic), bouton vert + compteur quand clé détectée
- **Galerie** : grille 4 colonnes avec miniatures cliquables
- **Recadrage** : overlay interactif, grille des tiers, poignées tactiles aux coins, zone assombrie
- **Rotation** : 90° par clic (transformation réelle, pas juste visuelle)
- **Filtres Pillow** : N&B, sépia, vintage (désaturation+contraste), contraste
- **Goodies** : panneau latéral toggle, stickers draggable (20% taille scène), cadres overlay
- **Multi-pose** : 5 templates (2h, 2v, 3 gauche, 4 grille, 3 bande), remplissage auto depuis galerie
- **Texte** : éditable + draggable, police 32pt blanche
- **Annuler** : reset à l'image originale
- **Export** : scène complète → PNG haute résolution (fichier temporaire)
- **Impression** : écran prévisualisation + sélection format (10x15, 13x18, 15x20 cm)
### Modes d'import
- **USB** : polling montages, scan récursif, bouton vert + compteur
- **QR Code** : mini QR sur le bouton, serveur Flask upload, bouton vert quand photos reçues → galerie directe
- **WiFi** : même serveur Flask, affiche nom AP + mot de passe
- **Email** : IMAP polling configurable, affiche adresse mail
### Galerie
- Cartes à taille fixe, miniatures centrées (QPushButton + icon)
- Clic sur la photo → ouvre l'éditeur avec cadre de recadrage auto si ratio ≠ 3:2
- Boutons − / quantité / + centrés sous chaque photo
- Pastille orange (point) si ratio ≠ 3:2
- Résumé "X photos — Y impressions"
- Bouton Valider → écran impression
### Éditeur
- Recadrage ratio verrouillé 3:2 (auto si ratio ≠ 3:2), bord à bord
- Rotation 90°, filtres (N&B, sépia, vintage, contraste)
- Multi-pose (5 templates, même photo dupliquée, crop to fill)
- Goodies (stickers draggable, cadres overlay)
- Texte éditable draggable
- Export scène → PNG haute résolution
### Impression
- Formats activables par format dans la config (10x15, 13x18, 15x20)
- Imprimante CUPS assignable par format
- Cadre de recadrage au ratio du format, bord à bord, déplaçable
### Config admin (appui long 5s sur "Photostation")
- WiFi : nom AP, mot de passe
- Serveur upload : IP d'écoute, port, URL publique (QR code)
- Email IMAP : activer/désactiver, serveur, port, user, password, dossier, intervalle
- Formats d'impression : checkbox par format + imprimante CUPS
- Sauvegardé dans config/settings.json
### Interface
- Responsive : toutes tailles en % de la fenêtre
- Fonctionne en 800x480, 1024x768, 1280x1024, 1920x1080
- Mode kiosk sans WM (FramelessWindowHint + setGeometry)
- Guard anti-boucle de zoom dans resizeEvent
## Dev
- **Repo Gitea** : https://git.copydev.fr/jules/photostation
- **Token Gitea** : `4bc923c47cf363a866daa1e1a6cf22bc44a78bc5` (claude-photostation)
- **LXC dev** : ID 164, hostname `photostation`, IP `192.168.111.164`
- Debian 12, 2GB RAM, 2 cores
- Python 3 + PyQt6 6.10.2 + Pillow 12.1.1 + venv dans `/opt/photostation/venv/`
- Python 3 + PyQt6 6.10.2 + Pillow 12.1.1 + Flask 3.1.3 + venv `/opt/photostation/venv/`
- Code dans `/opt/photostation/`
- Xvfb :99 (1024x600) + VNC port 5900 (sans mot de passe)
- Lancer l'app : `source venv/bin/activate && DISPLAY=:99 PYTHONPATH=src python3 src/main.py`
- **Lancement local** : `python3 src/main.py` (fenêtré) ou `python3 src/main.py --kiosk`
- Xvfb :99 (1280x1024) + VNC port 5900 (sans mot de passe)
- 50 photos test dans `/media/usb_test/` (bind mount simulé)
- Scripts test : `test_gallery.py`, `test_editor.py`, `test_multipose.py`, `test_print.py`
- **NPM** : `photo.copydev.fr` → `192.168.111.164:8080` (proxy id=58, cert wildcard *.copydev.fr)
- **Git config** : user `Jules`, email `contact@copydev.fr`
- **Credentials git** : stockés dans `/tmp/.git-cred-photostation`
## TODO
- [ ] Peupler assets/goodies/ et assets/frames/ avec des stickers et cadres
- [ ] Impression CUPS réelle (pycups, détection imprimante, envoi job)
- [ ] WiFi hotspot (hostapd + dnsmasq + Flask page upload)
- [ ] QR code (génération lien vers page upload)
- [ ] Email (réception SMTP)
- [ ] Thème/style plus abouti
- [ ] Tarification : prix en € dans la galerie, tarif unitaire + paliers configurables
- [ ] Écran récap après Valider : photos sélectionnées + quantités + prix total
- [ ] Impression CUPS réelle
- [ ] Hotspot WiFi captif (hostapd + dnsmasq — RPi spécifique)
- [ ] Peupler assets/goodies/ et assets/frames/
- [ ] Tests sur RPi 3 réel
## Formats d'impression
- 10x15 cm
- 13x18 cm
- 15x20 cm
## Notes
- Fichiers `backend/` et `frontend/` dans le repo = version alternative (probablement générée par un autre onglet Claude). Notre approche est dans `src/` (PyQt6).
- Les libs xcb nécessaires sur le LXC : libxcb-cursor0, libxcb-xinerama0, libxcb-icccm4, libxcb-keysyms1, libxcb-render-util0, libxcb-shape0, libxcb-xfixes0, libxkbcommon-x11-0, libfontconfig1
| Format | Ratio | Dimensions cm |
|--------|-------|---------------|
| 10x15 | 1.5 (3:2) | 10 × 15 |
| 13x18 | 1.385 | 13 × 18 |
| 15x20 | 1.333 (4:3) | 15 × 20 |