Init projet Photostation - borne impression photo tactile PyQt6
Structure : 4 écrans (accueil, import, éditeur, impression), navigation QStackedWidget, base éditeur QGraphicsScene. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
12
.gitignore
vendored
Normal file
12
.gitignore
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
*.pyo
|
||||||
|
.venv/
|
||||||
|
venv/
|
||||||
|
*.egg-info/
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
|
.env
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
51
memoire.md
Normal file
51
memoire.md
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
# Photostation
|
||||||
|
|
||||||
|
## Description
|
||||||
|
Borne d'impression photo tactile. Les utilisateurs importent leurs photos (USB, WiFi, QR code, email), les éditent (recadrage, rotation, effets, goodies, multi-pose, texte) et les impriment sur imprimante sublimation.
|
||||||
|
|
||||||
|
## Stack technique
|
||||||
|
- **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)
|
||||||
|
- **QR** : qrcode (Python)
|
||||||
|
|
||||||
|
## Matériel
|
||||||
|
- **Prototype** : Raspberry Pi 3B+ (1GB RAM) — portable vers RPi 4/5
|
||||||
|
- **Écran tactile** : à déterminer (en stock chez Jules)
|
||||||
|
- **Imprimante sublimation** : à acheter (DNP DS620A ou Mitsubishi CP-D90DW)
|
||||||
|
- **Pas de capture photo** — les utilisateurs apportent leurs propres photos
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
```
|
||||||
|
src/
|
||||||
|
main.py — Point d'entrée
|
||||||
|
ui/
|
||||||
|
main_window.py — Navigation QStackedWidget
|
||||||
|
screens/
|
||||||
|
home_screen.py — Accueil, choix du mode d'import
|
||||||
|
import_screen.py — Galerie de photos importées
|
||||||
|
editor_screen.py — Éditeur (crop, rotation, effets, goodies, multi-pose, texte)
|
||||||
|
print_screen.py — Prévisualisation + impression
|
||||||
|
assets/
|
||||||
|
goodies/ — Stickers PNG
|
||||||
|
frames/ — Cadres photo
|
||||||
|
fonts/ — Polices custom
|
||||||
|
templates/ — Templates multi-pose
|
||||||
|
config/ — Configuration (imprimante, WiFi, etc.)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Dev
|
||||||
|
- **Repo Gitea** : https://git.copydev.fr/jules/photostation
|
||||||
|
- **Dev sur** : LXC Proxmox (x86) ou PC local, déploiement sur RPi
|
||||||
|
- **Lancement** : `python3 src/main.py` (fenêtré) ou `python3 src/main.py --kiosk` (plein écran)
|
||||||
|
|
||||||
|
## Statut
|
||||||
|
- Projet initialisé le 2026-03-21
|
||||||
|
- Structure de base créée (4 écrans, navigation)
|
||||||
|
- TODO : import USB, éditeur complet, impression CUPS, modes WiFi/QR/email
|
||||||
|
|
||||||
|
## Formats d'impression
|
||||||
|
- 10x15 cm
|
||||||
|
- 13x18 cm
|
||||||
|
- 15x20 cm
|
||||||
4
requirements.txt
Normal file
4
requirements.txt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
PyQt6>=6.5
|
||||||
|
Pillow>=10.0
|
||||||
|
qrcode>=7.4
|
||||||
|
pycups>=2.0
|
||||||
0
src/__init__.py
Normal file
0
src/__init__.py
Normal file
27
src/main.py
Normal file
27
src/main.py
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Photostation - Borne d'impression photo tactile."""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
from PyQt6.QtWidgets import QApplication
|
||||||
|
from PyQt6.QtCore import Qt
|
||||||
|
from ui.main_window import MainWindow
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
app = QApplication(sys.argv)
|
||||||
|
app.setApplicationName("Photostation")
|
||||||
|
|
||||||
|
window = MainWindow()
|
||||||
|
|
||||||
|
# Mode kiosk si argument --kiosk
|
||||||
|
if "--kiosk" in sys.argv:
|
||||||
|
window.showFullScreen()
|
||||||
|
else:
|
||||||
|
window.resize(1024, 600)
|
||||||
|
window.show()
|
||||||
|
|
||||||
|
sys.exit(app.exec())
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
0
src/ui/__init__.py
Normal file
0
src/ui/__init__.py
Normal file
47
src/ui/main_window.py
Normal file
47
src/ui/main_window.py
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
"""Fenêtre principale — navigation entre les écrans."""
|
||||||
|
|
||||||
|
from PyQt6.QtWidgets import QMainWindow, QStackedWidget
|
||||||
|
from PyQt6.QtCore import Qt
|
||||||
|
from ui.screens.home_screen import HomeScreen
|
||||||
|
from ui.screens.import_screen import ImportScreen
|
||||||
|
from ui.screens.editor_screen import EditorScreen
|
||||||
|
from ui.screens.print_screen import PrintScreen
|
||||||
|
|
||||||
|
|
||||||
|
class MainWindow(QMainWindow):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.setWindowTitle("Photostation")
|
||||||
|
self.setAttribute(Qt.WidgetAttribute.WA_AcceptTouchEvents, True)
|
||||||
|
|
||||||
|
self.stack = QStackedWidget()
|
||||||
|
self.setCentralWidget(self.stack)
|
||||||
|
|
||||||
|
# Écrans
|
||||||
|
self.home_screen = HomeScreen(self)
|
||||||
|
self.import_screen = ImportScreen(self)
|
||||||
|
self.editor_screen = EditorScreen(self)
|
||||||
|
self.print_screen = PrintScreen(self)
|
||||||
|
|
||||||
|
self.stack.addWidget(self.home_screen) # 0
|
||||||
|
self.stack.addWidget(self.import_screen) # 1
|
||||||
|
self.stack.addWidget(self.editor_screen) # 2
|
||||||
|
self.stack.addWidget(self.print_screen) # 3
|
||||||
|
|
||||||
|
self.show_home()
|
||||||
|
|
||||||
|
def show_home(self):
|
||||||
|
self.stack.setCurrentIndex(0)
|
||||||
|
|
||||||
|
def show_import(self):
|
||||||
|
self.stack.setCurrentIndex(1)
|
||||||
|
|
||||||
|
def show_editor(self, image_path=None):
|
||||||
|
if image_path:
|
||||||
|
self.editor_screen.load_image(image_path)
|
||||||
|
self.stack.setCurrentIndex(2)
|
||||||
|
|
||||||
|
def show_print(self, image_path=None):
|
||||||
|
if image_path:
|
||||||
|
self.print_screen.set_image(image_path)
|
||||||
|
self.stack.setCurrentIndex(3)
|
||||||
0
src/ui/screens/__init__.py
Normal file
0
src/ui/screens/__init__.py
Normal file
144
src/ui/screens/editor_screen.py
Normal file
144
src/ui/screens/editor_screen.py
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
"""Écran éditeur — recadrage, goodies, effets, multi-pose."""
|
||||||
|
|
||||||
|
from PyQt6.QtWidgets import (
|
||||||
|
QWidget, QVBoxLayout, QHBoxLayout, QPushButton,
|
||||||
|
QLabel, QGraphicsScene, QGraphicsView, QGraphicsPixmapItem,
|
||||||
|
QToolBar
|
||||||
|
)
|
||||||
|
from PyQt6.QtCore import Qt, QRectF
|
||||||
|
from PyQt6.QtGui import QFont, QPixmap, QImage, QPainter
|
||||||
|
|
||||||
|
|
||||||
|
class EditorScreen(QWidget):
|
||||||
|
def __init__(self, main_window):
|
||||||
|
super().__init__()
|
||||||
|
self.main_window = main_window
|
||||||
|
self.current_image_path = None
|
||||||
|
self.pixmap_item = None
|
||||||
|
self._setup_ui()
|
||||||
|
|
||||||
|
def _setup_ui(self):
|
||||||
|
layout = QVBoxLayout(self)
|
||||||
|
layout.setSpacing(10)
|
||||||
|
|
||||||
|
# Header
|
||||||
|
header = QHBoxLayout()
|
||||||
|
back_btn = QPushButton("Retour")
|
||||||
|
back_btn.setFont(QFont("", 14))
|
||||||
|
back_btn.clicked.connect(self.main_window.show_import)
|
||||||
|
header.addWidget(back_btn)
|
||||||
|
|
||||||
|
title = QLabel("Éditeur")
|
||||||
|
title.setFont(QFont("", 24, QFont.Weight.Bold))
|
||||||
|
title.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
|
header.addWidget(title, 1)
|
||||||
|
|
||||||
|
print_btn = QPushButton("Imprimer")
|
||||||
|
print_btn.setFont(QFont("", 14))
|
||||||
|
print_btn.setStyleSheet("""
|
||||||
|
QPushButton {
|
||||||
|
background-color: #4CAF50;
|
||||||
|
color: white;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 10px 20px;
|
||||||
|
}
|
||||||
|
QPushButton:pressed { background-color: #388E3C; }
|
||||||
|
""")
|
||||||
|
print_btn.clicked.connect(self._go_print)
|
||||||
|
header.addWidget(print_btn)
|
||||||
|
layout.addLayout(header)
|
||||||
|
|
||||||
|
# Zone d'édition (QGraphicsScene)
|
||||||
|
self.scene = QGraphicsScene()
|
||||||
|
self.view = QGraphicsView(self.scene)
|
||||||
|
self.view.setRenderHint(QPainter.RenderHint.Antialiasing)
|
||||||
|
self.view.setRenderHint(QPainter.RenderHint.SmoothPixmapTransform)
|
||||||
|
self.view.setDragMode(QGraphicsView.DragMode.ScrollHandDrag)
|
||||||
|
self.view.setStyleSheet("background-color: #333;")
|
||||||
|
layout.addWidget(self.view, 1)
|
||||||
|
|
||||||
|
# Barre d'outils
|
||||||
|
tools = QHBoxLayout()
|
||||||
|
tools.setSpacing(10)
|
||||||
|
|
||||||
|
tool_buttons = [
|
||||||
|
("Recadrer", self._crop),
|
||||||
|
("Rotation", self._rotate),
|
||||||
|
("N&B", self._filter_grayscale),
|
||||||
|
("Sépia", self._filter_sepia),
|
||||||
|
("Goodies", self._add_goodies),
|
||||||
|
("Multi-pose", self._multipose),
|
||||||
|
("Texte", self._add_text),
|
||||||
|
]
|
||||||
|
|
||||||
|
for label, callback in tool_buttons:
|
||||||
|
btn = QPushButton(label)
|
||||||
|
btn.setFont(QFont("", 13))
|
||||||
|
btn.setMinimumHeight(50)
|
||||||
|
btn.setStyleSheet("""
|
||||||
|
QPushButton {
|
||||||
|
background-color: #555;
|
||||||
|
color: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 8px 15px;
|
||||||
|
}
|
||||||
|
QPushButton:pressed { background-color: #777; }
|
||||||
|
""")
|
||||||
|
btn.clicked.connect(callback)
|
||||||
|
tools.addWidget(btn)
|
||||||
|
|
||||||
|
layout.addLayout(tools)
|
||||||
|
|
||||||
|
def load_image(self, path):
|
||||||
|
self.current_image_path = path
|
||||||
|
self.scene.clear()
|
||||||
|
pixmap = QPixmap(path)
|
||||||
|
self.pixmap_item = self.scene.addPixmap(pixmap)
|
||||||
|
self.view.fitInView(self.pixmap_item, Qt.AspectRatioMode.KeepAspectRatio)
|
||||||
|
|
||||||
|
def _go_print(self):
|
||||||
|
# TODO: exporter le canvas édité en image
|
||||||
|
self.main_window.show_print(self.current_image_path)
|
||||||
|
|
||||||
|
def _crop(self):
|
||||||
|
# TODO: outil de recadrage interactif
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _rotate(self):
|
||||||
|
if self.pixmap_item:
|
||||||
|
self.pixmap_item.setRotation(self.pixmap_item.rotation() + 90)
|
||||||
|
|
||||||
|
def _filter_grayscale(self):
|
||||||
|
if not self.current_image_path:
|
||||||
|
return
|
||||||
|
pixmap = QPixmap(self.current_image_path)
|
||||||
|
image = pixmap.toImage().convertToFormat(QImage.Format.Format_Grayscale8)
|
||||||
|
self.scene.clear()
|
||||||
|
self.pixmap_item = self.scene.addPixmap(QPixmap.fromImage(image))
|
||||||
|
self.view.fitInView(self.pixmap_item, Qt.AspectRatioMode.KeepAspectRatio)
|
||||||
|
|
||||||
|
def _filter_sepia(self):
|
||||||
|
if not self.current_image_path:
|
||||||
|
return
|
||||||
|
from PIL import Image, ImageOps
|
||||||
|
img = Image.open(self.current_image_path)
|
||||||
|
sepia = ImageOps.grayscale(img)
|
||||||
|
sepia = ImageOps.colorize(sepia, "#704214", "#C0A080")
|
||||||
|
# Convertir PIL -> QPixmap
|
||||||
|
data = sepia.convert("RGBA").tobytes("raw", "RGBA")
|
||||||
|
qimg = QImage(data, sepia.width, sepia.height, QImage.Format.Format_RGBA8888)
|
||||||
|
self.scene.clear()
|
||||||
|
self.pixmap_item = self.scene.addPixmap(QPixmap.fromImage(qimg))
|
||||||
|
self.view.fitInView(self.pixmap_item, Qt.AspectRatioMode.KeepAspectRatio)
|
||||||
|
|
||||||
|
def _add_goodies(self):
|
||||||
|
# TODO: panneau de sélection de stickers
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _multipose(self):
|
||||||
|
# TODO: sélection de templates multi-photos
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _add_text(self):
|
||||||
|
# TODO: ajout de texte éditable sur le canvas
|
||||||
|
pass
|
||||||
77
src/ui/screens/home_screen.py
Normal file
77
src/ui/screens/home_screen.py
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
"""Écran d'accueil — choix du mode d'import."""
|
||||||
|
|
||||||
|
from PyQt6.QtWidgets import (
|
||||||
|
QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLabel
|
||||||
|
)
|
||||||
|
from PyQt6.QtCore import Qt
|
||||||
|
from PyQt6.QtGui import QFont
|
||||||
|
|
||||||
|
|
||||||
|
class HomeScreen(QWidget):
|
||||||
|
def __init__(self, main_window):
|
||||||
|
super().__init__()
|
||||||
|
self.main_window = main_window
|
||||||
|
self._setup_ui()
|
||||||
|
|
||||||
|
def _setup_ui(self):
|
||||||
|
layout = QVBoxLayout(self)
|
||||||
|
layout.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
|
layout.setSpacing(40)
|
||||||
|
|
||||||
|
# Titre
|
||||||
|
title = QLabel("Photostation")
|
||||||
|
title.setFont(QFont("", 48, QFont.Weight.Bold))
|
||||||
|
title.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
|
layout.addWidget(title)
|
||||||
|
|
||||||
|
subtitle = QLabel("Sélectionnez un mode d'import")
|
||||||
|
subtitle.setFont(QFont("", 20))
|
||||||
|
subtitle.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
|
layout.addWidget(subtitle)
|
||||||
|
|
||||||
|
# Boutons d'import
|
||||||
|
buttons_layout = QHBoxLayout()
|
||||||
|
buttons_layout.setSpacing(20)
|
||||||
|
|
||||||
|
modes = [
|
||||||
|
("USB", "Clé USB", self._import_usb),
|
||||||
|
("WiFi", "Depuis téléphone", self._import_wifi),
|
||||||
|
("QR Code", "Scanner pour envoyer", self._import_qr),
|
||||||
|
("Email", "Recevoir par mail", self._import_email),
|
||||||
|
]
|
||||||
|
|
||||||
|
for label, desc, callback in modes:
|
||||||
|
btn = self._create_mode_button(label, desc)
|
||||||
|
btn.clicked.connect(callback)
|
||||||
|
buttons_layout.addWidget(btn)
|
||||||
|
|
||||||
|
layout.addLayout(buttons_layout)
|
||||||
|
|
||||||
|
def _create_mode_button(self, label, description):
|
||||||
|
btn = QPushButton(f"{label}\n{description}")
|
||||||
|
btn.setFont(QFont("", 16))
|
||||||
|
btn.setMinimumSize(200, 150)
|
||||||
|
btn.setStyleSheet("""
|
||||||
|
QPushButton {
|
||||||
|
background-color: #2196F3;
|
||||||
|
color: white;
|
||||||
|
border-radius: 15px;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
QPushButton:pressed {
|
||||||
|
background-color: #1976D2;
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
return btn
|
||||||
|
|
||||||
|
def _import_usb(self):
|
||||||
|
self.main_window.show_import()
|
||||||
|
|
||||||
|
def _import_wifi(self):
|
||||||
|
self.main_window.show_import()
|
||||||
|
|
||||||
|
def _import_qr(self):
|
||||||
|
self.main_window.show_import()
|
||||||
|
|
||||||
|
def _import_email(self):
|
||||||
|
self.main_window.show_import()
|
||||||
87
src/ui/screens/import_screen.py
Normal file
87
src/ui/screens/import_screen.py
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
"""Écran d'import — galerie de photos importées."""
|
||||||
|
|
||||||
|
from PyQt6.QtWidgets import (
|
||||||
|
QWidget, QVBoxLayout, QHBoxLayout, QPushButton,
|
||||||
|
QLabel, QScrollArea, QGridLayout
|
||||||
|
)
|
||||||
|
from PyQt6.QtCore import Qt
|
||||||
|
from PyQt6.QtGui import QFont, QPixmap
|
||||||
|
|
||||||
|
|
||||||
|
class ImportScreen(QWidget):
|
||||||
|
def __init__(self, main_window):
|
||||||
|
super().__init__()
|
||||||
|
self.main_window = main_window
|
||||||
|
self.photos = []
|
||||||
|
self._setup_ui()
|
||||||
|
|
||||||
|
def _setup_ui(self):
|
||||||
|
layout = QVBoxLayout(self)
|
||||||
|
|
||||||
|
# Header
|
||||||
|
header = QHBoxLayout()
|
||||||
|
back_btn = QPushButton("Retour")
|
||||||
|
back_btn.setFont(QFont("", 14))
|
||||||
|
back_btn.clicked.connect(self.main_window.show_home)
|
||||||
|
header.addWidget(back_btn)
|
||||||
|
|
||||||
|
title = QLabel("Sélectionnez une photo")
|
||||||
|
title.setFont(QFont("", 24, QFont.Weight.Bold))
|
||||||
|
title.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
|
header.addWidget(title, 1)
|
||||||
|
|
||||||
|
header.addSpacing(80) # Balance le bouton retour
|
||||||
|
layout.addLayout(header)
|
||||||
|
|
||||||
|
# Grille de photos (scrollable)
|
||||||
|
scroll = QScrollArea()
|
||||||
|
scroll.setWidgetResizable(True)
|
||||||
|
scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
|
||||||
|
|
||||||
|
self.grid_widget = QWidget()
|
||||||
|
self.grid_layout = QGridLayout(self.grid_widget)
|
||||||
|
self.grid_layout.setSpacing(10)
|
||||||
|
scroll.setWidget(self.grid_widget)
|
||||||
|
|
||||||
|
layout.addWidget(scroll)
|
||||||
|
|
||||||
|
def set_photos(self, photo_paths):
|
||||||
|
"""Affiche les photos dans la grille."""
|
||||||
|
# Clear existing
|
||||||
|
while self.grid_layout.count():
|
||||||
|
item = self.grid_layout.takeAt(0)
|
||||||
|
if item.widget():
|
||||||
|
item.widget().deleteLater()
|
||||||
|
|
||||||
|
self.photos = photo_paths
|
||||||
|
cols = 4
|
||||||
|
for i, path in enumerate(photo_paths):
|
||||||
|
row, col = divmod(i, cols)
|
||||||
|
thumb = self._create_thumbnail(path)
|
||||||
|
self.grid_layout.addWidget(thumb, row, col)
|
||||||
|
|
||||||
|
def _create_thumbnail(self, path):
|
||||||
|
btn = QPushButton()
|
||||||
|
btn.setMinimumSize(180, 180)
|
||||||
|
btn.setStyleSheet("""
|
||||||
|
QPushButton {
|
||||||
|
border: 3px solid transparent;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
QPushButton:pressed {
|
||||||
|
border-color: #2196F3;
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
pixmap = QPixmap(path)
|
||||||
|
if not pixmap.isNull():
|
||||||
|
scaled = pixmap.scaled(
|
||||||
|
170, 170,
|
||||||
|
Qt.AspectRatioMode.KeepAspectRatio,
|
||||||
|
Qt.TransformationMode.SmoothTransformation
|
||||||
|
)
|
||||||
|
btn.setIcon(scaled)
|
||||||
|
from PyQt6.QtCore import QSize
|
||||||
|
btn.setIconSize(QSize(170, 170))
|
||||||
|
|
||||||
|
btn.clicked.connect(lambda checked, p=path: self.main_window.show_editor(p))
|
||||||
|
return btn
|
||||||
90
src/ui/screens/print_screen.py
Normal file
90
src/ui/screens/print_screen.py
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
"""Écran d'impression — prévisualisation et lancement impression."""
|
||||||
|
|
||||||
|
from PyQt6.QtWidgets import (
|
||||||
|
QWidget, QVBoxLayout, QHBoxLayout, QPushButton,
|
||||||
|
QLabel, QComboBox
|
||||||
|
)
|
||||||
|
from PyQt6.QtCore import Qt
|
||||||
|
from PyQt6.QtGui import QFont, QPixmap
|
||||||
|
|
||||||
|
|
||||||
|
class PrintScreen(QWidget):
|
||||||
|
FORMATS = {
|
||||||
|
"10x15 cm": (10, 15),
|
||||||
|
"13x18 cm": (13, 18),
|
||||||
|
"15x20 cm": (15, 20),
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self, main_window):
|
||||||
|
super().__init__()
|
||||||
|
self.main_window = main_window
|
||||||
|
self.image_path = None
|
||||||
|
self._setup_ui()
|
||||||
|
|
||||||
|
def _setup_ui(self):
|
||||||
|
layout = QVBoxLayout(self)
|
||||||
|
layout.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
|
layout.setSpacing(30)
|
||||||
|
|
||||||
|
# Header
|
||||||
|
header = QHBoxLayout()
|
||||||
|
back_btn = QPushButton("Retour éditeur")
|
||||||
|
back_btn.setFont(QFont("", 14))
|
||||||
|
back_btn.clicked.connect(lambda: self.main_window.show_editor())
|
||||||
|
header.addWidget(back_btn)
|
||||||
|
header.addStretch()
|
||||||
|
layout.addLayout(header)
|
||||||
|
|
||||||
|
# Prévisualisation
|
||||||
|
self.preview = QLabel()
|
||||||
|
self.preview.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
|
self.preview.setMinimumSize(400, 300)
|
||||||
|
self.preview.setStyleSheet("background-color: #333; border-radius: 10px;")
|
||||||
|
layout.addWidget(self.preview)
|
||||||
|
|
||||||
|
# Sélection format
|
||||||
|
format_layout = QHBoxLayout()
|
||||||
|
format_label = QLabel("Format :")
|
||||||
|
format_label.setFont(QFont("", 16))
|
||||||
|
format_layout.addWidget(format_label)
|
||||||
|
|
||||||
|
self.format_combo = QComboBox()
|
||||||
|
self.format_combo.setFont(QFont("", 16))
|
||||||
|
self.format_combo.addItems(self.FORMATS.keys())
|
||||||
|
self.format_combo.setMinimumHeight(50)
|
||||||
|
format_layout.addWidget(self.format_combo)
|
||||||
|
layout.addLayout(format_layout)
|
||||||
|
|
||||||
|
# Bouton imprimer
|
||||||
|
print_btn = QPushButton("IMPRIMER")
|
||||||
|
print_btn.setFont(QFont("", 24, QFont.Weight.Bold))
|
||||||
|
print_btn.setMinimumSize(300, 80)
|
||||||
|
print_btn.setStyleSheet("""
|
||||||
|
QPushButton {
|
||||||
|
background-color: #4CAF50;
|
||||||
|
color: white;
|
||||||
|
border-radius: 15px;
|
||||||
|
}
|
||||||
|
QPushButton:pressed { background-color: #388E3C; }
|
||||||
|
""")
|
||||||
|
print_btn.clicked.connect(self._print)
|
||||||
|
layout.addWidget(print_btn, alignment=Qt.AlignmentFlag.AlignCenter)
|
||||||
|
|
||||||
|
def set_image(self, path):
|
||||||
|
self.image_path = path
|
||||||
|
pixmap = QPixmap(path)
|
||||||
|
if not pixmap.isNull():
|
||||||
|
scaled = pixmap.scaled(
|
||||||
|
400, 300,
|
||||||
|
Qt.AspectRatioMode.KeepAspectRatio,
|
||||||
|
Qt.TransformationMode.SmoothTransformation
|
||||||
|
)
|
||||||
|
self.preview.setPixmap(scaled)
|
||||||
|
|
||||||
|
def _print(self):
|
||||||
|
if not self.image_path:
|
||||||
|
return
|
||||||
|
fmt = self.format_combo.currentText()
|
||||||
|
size = self.FORMATS[fmt]
|
||||||
|
# TODO: redimensionner selon le format et envoyer à CUPS
|
||||||
|
print(f"Impression {self.image_path} en {fmt} ({size[0]}x{size[1]} cm)")
|
||||||
Reference in New Issue
Block a user