From 528e6bdfadaba6d23a38856f8143d47ce3e52132 Mon Sep 17 00:00:00 2001 From: Jules Date: Sat, 21 Mar 2026 22:16:31 +0100 Subject: [PATCH] =?UTF-8?q?Galerie=20:=20crayon=20=C3=A9dition=20+=20compt?= =?UTF-8?q?eur=20quantit=C3=A9=20+/-=20+=20bouton=20Valider?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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) --- src/ui/screens/import_screen.py | 308 +++++++++++++++++++++++++++----- 1 file changed, 265 insertions(+), 43 deletions(-) diff --git a/src/ui/screens/import_screen.py b/src/ui/screens/import_screen.py index 53eb6d2..e3c426c 100644 --- a/src/ui/screens/import_screen.py +++ b/src/ui/screens/import_screen.py @@ -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]