Galerie : crayon édition + compteur quantité +/- + bouton Valider
- PhotoCard : miniature + crayon bleu (ouvre éditeur) + boutons -/+ - Quantité par photo, bordure verte quand qty > 0 - Résumé en haut : "X photos sélectionnées — Y impressions" - Bouton Valider en haut à droite - Responsive (tailles adaptatives) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,11 +1,185 @@
|
||||
"""Écran d'import — galerie de photos importées."""
|
||||
"""Écran d'import — galerie avec édition et quantités."""
|
||||
|
||||
from PyQt6.QtWidgets import (
|
||||
QWidget, QVBoxLayout, QHBoxLayout, QPushButton,
|
||||
QLabel, QScrollArea, QGridLayout
|
||||
QLabel, QScrollArea, QGridLayout, QFrame
|
||||
)
|
||||
from PyQt6.QtCore import Qt
|
||||
from PyQt6.QtGui import QFont, QPixmap
|
||||
from PyQt6.QtCore import Qt, QSize
|
||||
from PyQt6.QtGui import QFont, QPixmap, QIcon
|
||||
|
||||
from ui.style import scale, scaled_font, Sizes, back_btn_style, green_btn
|
||||
|
||||
|
||||
class PhotoCard(QFrame):
|
||||
"""Carte photo : miniature + crayon édition + compteur quantité."""
|
||||
|
||||
def __init__(self, path, main_window, parent=None):
|
||||
super().__init__(parent)
|
||||
self.path = path
|
||||
self.main_window = main_window
|
||||
self.quantity = 0
|
||||
self._setup_ui()
|
||||
|
||||
def _setup_ui(self):
|
||||
self.setStyleSheet("""
|
||||
PhotoCard {
|
||||
background: white;
|
||||
border: 2px solid #ddd;
|
||||
border-radius: 10px;
|
||||
}
|
||||
""")
|
||||
layout = QVBoxLayout(self)
|
||||
layout.setContentsMargins(5, 5, 5, 5)
|
||||
layout.setSpacing(4)
|
||||
|
||||
# Miniature
|
||||
self.thumb_label = QLabel()
|
||||
self.thumb_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
self.thumb_label.setMinimumSize(150, 120)
|
||||
pixmap = QPixmap(self.path)
|
||||
if not pixmap.isNull():
|
||||
scaled = pixmap.scaled(
|
||||
200, 150,
|
||||
Qt.AspectRatioMode.KeepAspectRatio,
|
||||
Qt.TransformationMode.SmoothTransformation
|
||||
)
|
||||
self.thumb_label.setPixmap(scaled)
|
||||
layout.addWidget(self.thumb_label)
|
||||
|
||||
# Barre du bas : crayon + quantité
|
||||
bar = QHBoxLayout()
|
||||
bar.setSpacing(4)
|
||||
|
||||
# Crayon édition
|
||||
self.edit_btn = QPushButton("✎")
|
||||
self.edit_btn.setFixedSize(36, 36)
|
||||
self.edit_btn.setStyleSheet("""
|
||||
QPushButton {
|
||||
background: #2196F3; color: white;
|
||||
border-radius: 18px; font-size: 18px;
|
||||
}
|
||||
QPushButton:pressed { background: #1976D2; }
|
||||
""")
|
||||
self.edit_btn.setToolTip("Éditer")
|
||||
self.edit_btn.clicked.connect(self._edit)
|
||||
bar.addWidget(self.edit_btn)
|
||||
|
||||
bar.addStretch()
|
||||
|
||||
# Compteur quantité
|
||||
self.minus_btn = QPushButton("−")
|
||||
self.minus_btn.setFixedSize(32, 32)
|
||||
self.minus_btn.setStyleSheet("""
|
||||
QPushButton {
|
||||
background: #eee; color: #333;
|
||||
border-radius: 16px; font-size: 18px; font-weight: bold;
|
||||
}
|
||||
QPushButton:pressed { background: #ccc; }
|
||||
""")
|
||||
self.minus_btn.clicked.connect(self._decrement)
|
||||
bar.addWidget(self.minus_btn)
|
||||
|
||||
self.qty_label = QLabel("0")
|
||||
self.qty_label.setFont(QFont("", 14, QFont.Weight.Bold))
|
||||
self.qty_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
self.qty_label.setMinimumWidth(30)
|
||||
bar.addWidget(self.qty_label)
|
||||
|
||||
self.plus_btn = QPushButton("+")
|
||||
self.plus_btn.setFixedSize(32, 32)
|
||||
self.plus_btn.setStyleSheet("""
|
||||
QPushButton {
|
||||
background: #4CAF50; color: white;
|
||||
border-radius: 16px; font-size: 18px; font-weight: bold;
|
||||
}
|
||||
QPushButton:pressed { background: #388E3C; }
|
||||
""")
|
||||
self.plus_btn.clicked.connect(self._increment)
|
||||
bar.addWidget(self.plus_btn)
|
||||
|
||||
layout.addLayout(bar)
|
||||
|
||||
def _edit(self):
|
||||
self.main_window.show_editor(self.path)
|
||||
|
||||
def _increment(self):
|
||||
self.quantity += 1
|
||||
self._update_qty()
|
||||
|
||||
def _decrement(self):
|
||||
if self.quantity > 0:
|
||||
self.quantity -= 1
|
||||
self._update_qty()
|
||||
|
||||
def _update_qty(self):
|
||||
self.qty_label.setText(str(self.quantity))
|
||||
if self.quantity > 0:
|
||||
self.setStyleSheet("""
|
||||
PhotoCard {
|
||||
background: white;
|
||||
border: 3px solid #4CAF50;
|
||||
border-radius: 10px;
|
||||
}
|
||||
""")
|
||||
self.qty_label.setStyleSheet("color: #4CAF50;")
|
||||
else:
|
||||
self.setStyleSheet("""
|
||||
PhotoCard {
|
||||
background: white;
|
||||
border: 2px solid #ddd;
|
||||
border-radius: 10px;
|
||||
}
|
||||
""")
|
||||
self.qty_label.setStyleSheet("color: #333;")
|
||||
|
||||
def apply_responsive(self, window_height):
|
||||
"""Adapte les tailles au responsive."""
|
||||
thumb_h = max(80, int(window_height * 0.15))
|
||||
thumb_w = max(100, int(thumb_h * 1.4))
|
||||
self.thumb_label.setMinimumSize(thumb_w, thumb_h)
|
||||
|
||||
# Re-scale la miniature
|
||||
pixmap = QPixmap(self.path)
|
||||
if not pixmap.isNull():
|
||||
scaled = pixmap.scaled(
|
||||
thumb_w, thumb_h,
|
||||
Qt.AspectRatioMode.KeepAspectRatio,
|
||||
Qt.TransformationMode.SmoothTransformation
|
||||
)
|
||||
self.thumb_label.setPixmap(scaled)
|
||||
|
||||
btn_size = max(28, int(window_height * 0.04))
|
||||
font_size = max(12, int(window_height * 0.02))
|
||||
|
||||
self.edit_btn.setFixedSize(btn_size, btn_size)
|
||||
self.edit_btn.setStyleSheet(f"""
|
||||
QPushButton {{
|
||||
background: #2196F3; color: white;
|
||||
border-radius: {btn_size//2}px; font-size: {font_size+2}px;
|
||||
}}
|
||||
QPushButton:pressed {{ background: #1976D2; }}
|
||||
""")
|
||||
|
||||
self.minus_btn.setFixedSize(btn_size, btn_size)
|
||||
self.minus_btn.setStyleSheet(f"""
|
||||
QPushButton {{
|
||||
background: #eee; color: #333;
|
||||
border-radius: {btn_size//2}px; font-size: {font_size+2}px; font-weight: bold;
|
||||
}}
|
||||
QPushButton:pressed {{ background: #ccc; }}
|
||||
""")
|
||||
|
||||
self.plus_btn.setFixedSize(btn_size, btn_size)
|
||||
self.plus_btn.setStyleSheet(f"""
|
||||
QPushButton {{
|
||||
background: #4CAF50; color: white;
|
||||
border-radius: {btn_size//2}px; font-size: {font_size+2}px; font-weight: bold;
|
||||
}}
|
||||
QPushButton:pressed {{ background: #388E3C; }}
|
||||
""")
|
||||
|
||||
self.qty_label.setFont(QFont("", font_size, QFont.Weight.Bold))
|
||||
self._update_qty()
|
||||
|
||||
|
||||
class ImportScreen(QWidget):
|
||||
@@ -13,76 +187,124 @@ class ImportScreen(QWidget):
|
||||
super().__init__()
|
||||
self.main_window = main_window
|
||||
self.photos = []
|
||||
self.cards = []
|
||||
self._setup_ui()
|
||||
|
||||
def _setup_ui(self):
|
||||
layout = QVBoxLayout(self)
|
||||
layout.setContentsMargins(10, 10, 10, 10)
|
||||
layout.setSpacing(8)
|
||||
|
||||
# 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)
|
||||
self.back_btn = QPushButton("< Retour")
|
||||
self.back_btn.clicked.connect(self.main_window.show_home)
|
||||
header.addWidget(self.back_btn)
|
||||
|
||||
self.title = QLabel("Sélectionnez vos photos")
|
||||
self.title.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
header.addWidget(self.title, 1)
|
||||
|
||||
self.validate_btn = QPushButton("Valider")
|
||||
self.validate_btn.clicked.connect(self._validate)
|
||||
header.addWidget(self.validate_btn)
|
||||
|
||||
header.addSpacing(80) # Balance le bouton retour
|
||||
layout.addLayout(header)
|
||||
|
||||
# Grille de photos (scrollable)
|
||||
# Résumé sélection
|
||||
self.summary_label = QLabel("")
|
||||
self.summary_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
self.summary_label.setStyleSheet("color: #666;")
|
||||
layout.addWidget(self.summary_label)
|
||||
|
||||
# Grille scrollable
|
||||
scroll = QScrollArea()
|
||||
scroll.setWidgetResizable(True)
|
||||
scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
|
||||
scroll.setStyleSheet("QScrollArea { border: none; }")
|
||||
|
||||
self.grid_widget = QWidget()
|
||||
self.grid_layout = QGridLayout(self.grid_widget)
|
||||
self.grid_layout.setSpacing(10)
|
||||
self.grid_layout.setSpacing(8)
|
||||
scroll.setWidget(self.grid_widget)
|
||||
|
||||
layout.addWidget(scroll)
|
||||
layout.addWidget(scroll, 1)
|
||||
|
||||
def showEvent(self, event):
|
||||
super().showEvent(event)
|
||||
from PyQt6.QtCore import QTimer
|
||||
QTimer.singleShot(0, self._apply_responsive)
|
||||
|
||||
def resizeEvent(self, event):
|
||||
super().resizeEvent(event)
|
||||
self._apply_responsive()
|
||||
|
||||
def _apply_responsive(self):
|
||||
h = self.window().height() if self.window() else 768
|
||||
|
||||
self.back_btn.setFont(scaled_font(self, Sizes.BUTTON_TEXT, bold=True))
|
||||
self.back_btn.setMinimumHeight(scale(self, Sizes.BTN_HEIGHT))
|
||||
self.back_btn.setStyleSheet(back_btn_style(self))
|
||||
|
||||
self.title.setFont(scaled_font(self, Sizes.HEADING, bold=True))
|
||||
|
||||
self.validate_btn.setFont(scaled_font(self, Sizes.BUTTON_TEXT, bold=True))
|
||||
self.validate_btn.setMinimumHeight(scale(self, Sizes.BTN_HEIGHT))
|
||||
self.validate_btn.setStyleSheet(green_btn(self))
|
||||
|
||||
self.summary_label.setFont(scaled_font(self, Sizes.HINT))
|
||||
|
||||
for card in self.cards:
|
||||
card.apply_responsive(h)
|
||||
|
||||
def set_photos(self, photo_paths):
|
||||
"""Affiche les photos dans la grille."""
|
||||
# Clear existing
|
||||
# Clear
|
||||
while self.grid_layout.count():
|
||||
item = self.grid_layout.takeAt(0)
|
||||
if item.widget():
|
||||
item.widget().deleteLater()
|
||||
|
||||
self.photos = photo_paths
|
||||
self.cards = []
|
||||
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)
|
||||
card = PhotoCard(path, self.main_window)
|
||||
card.plus_btn.clicked.connect(self._update_summary)
|
||||
card.minus_btn.clicked.connect(self._update_summary)
|
||||
self.grid_layout.addWidget(card, row, col)
|
||||
self.cards.append(card)
|
||||
|
||||
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
|
||||
self._update_summary()
|
||||
|
||||
def _update_summary(self):
|
||||
total = sum(c.quantity for c in self.cards)
|
||||
selected = sum(1 for c in self.cards if c.quantity > 0)
|
||||
if total > 0:
|
||||
self.summary_label.setText(
|
||||
f"{selected} photo{'s' if selected > 1 else ''} sélectionnée{'s' if selected > 1 else ''} — {total} impression{'s' if total > 1 else ''}"
|
||||
)
|
||||
from PyQt6.QtGui import QIcon
|
||||
from PyQt6.QtCore import QSize
|
||||
btn.setIcon(QIcon(scaled))
|
||||
btn.setIconSize(QSize(170, 170))
|
||||
self.summary_label.setStyleSheet("color: #4CAF50; font-weight: bold;")
|
||||
else:
|
||||
self.summary_label.setText("Appuyez sur + pour sélectionner des photos à imprimer")
|
||||
self.summary_label.setStyleSheet("color: #999;")
|
||||
|
||||
btn.clicked.connect(lambda checked, p=path: self.main_window.show_editor(p))
|
||||
return btn
|
||||
def _validate(self):
|
||||
"""Envoie les photos sélectionnées à l'impression."""
|
||||
to_print = []
|
||||
for card in self.cards:
|
||||
if card.quantity > 0:
|
||||
to_print.append((card.path, card.quantity))
|
||||
|
||||
if not to_print:
|
||||
return
|
||||
|
||||
# TODO: envoyer à l'écran d'impression avec les quantités
|
||||
# Pour l'instant, on envoie la première photo
|
||||
self.main_window.show_print(to_print[0][0])
|
||||
|
||||
def get_print_list(self):
|
||||
"""Retourne la liste [(path, quantity), ...] pour l'impression."""
|
||||
return [(c.path, c.quantity) for c in self.cards if c.quantity > 0]
|
||||
|
||||
Reference in New Issue
Block a user