"""Écran d'import — galerie avec quantités et recadrage.""" from PyQt6.QtWidgets import ( QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLabel, QScrollArea, QGridLayout, QFrame ) 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 from ui.widgets.config_dialog import load_config, calculate_price class PhotoCard(QFrame): """Carte photo : clic sur photo = éditeur, +/- centré, pastille orange si ratio ≠ 3:2.""" 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(4, 4, 4, 4) layout.setSpacing(4) # Miniature cliquable self.thumb_btn = QPushButton() self.thumb_btn.setFixedSize(200, 140) self.thumb_btn.setCursor(Qt.CursorShape.PointingHandCursor) self.thumb_btn.setStyleSheet(""" QPushButton { background: #f8f8f8; border: none; border-radius: 6px; } QPushButton:pressed { background: #eee; } """) self.thumb_btn.clicked.connect(self._edit) # Charger une version réduite (pas l'originale en pleine résolution) full = QPixmap(self.path) if not full.isNull() and full.width() > 400: self._pixmap_original = full.scaledToWidth(400, Qt.TransformationMode.SmoothTransformation) else: self._pixmap_original = full self._update_thumbnail() layout.addWidget(self.thumb_btn) # Pastille orange (ratio ≠ 3:2) self.ratio_dot = QLabel() self.ratio_dot.setFixedSize(14, 14) self.ratio_dot.setStyleSheet(""" QLabel { background: rgba(255, 152, 0, 0.85); border-radius: 7px; } """) self.ratio_dot.hide() self.ratio_dot.setParent(self.thumb_btn) self.ratio_dot.move(182, 4) self._check_ratio() # Barre du bas : − quantité + (centré) bar = QHBoxLayout() bar.setSpacing(6) bar.addStretch() 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) bar.addStretch() layout.addLayout(bar) def _check_ratio(self): """Affiche la pastille orange si ratio ≠ 3:2.""" if self._pixmap_original.isNull(): return w = self._pixmap_original.width() h = self._pixmap_original.height() if w == 0 or h == 0: return ratio = max(w, h) / min(w, h) if abs(ratio - 1.5) > 0.075: self.ratio_dot.show() else: self.ratio_dot.hide() 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 _update_thumbnail(self): """Redimensionne la miniature et la centre sur le bouton.""" if self._pixmap_original.isNull(): return w = self.thumb_btn.width() h = self.thumb_btn.height() if w > 10 and h > 10: scaled = self._pixmap_original.scaled( w - 4, h - 4, Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.SmoothTransformation ) self.thumb_btn.setIcon(QIcon(scaled)) self.thumb_btn.setIconSize(QSize(w - 4, h - 4)) def apply_responsive(self, window_height): """Adapte les tailles au responsive.""" thumb_h = max(100, int(window_height * 0.16)) thumb_w = max(140, int(thumb_h * 1.45)) self.thumb_btn.setFixedSize(thumb_w, thumb_h) self._update_thumbnail() # Pastille orange dot_size = max(10, int(window_height * 0.018)) self.ratio_dot.setFixedSize(dot_size, dot_size) self.ratio_dot.setParent(self.thumb_btn) self.ratio_dot.move(thumb_w - dot_size - 4, 4) self.ratio_dot.setStyleSheet(f""" QLabel {{ background: rgba(255, 152, 0, 0.85); border-radius: {dot_size // 2}px; }} """) self.ratio_dot.raise_() btn_size = max(28, int(window_height * 0.04)) font_size = max(12, int(window_height * 0.02)) 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): def __init__(self, main_window): 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() 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) layout.addLayout(header) # 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(8) scroll.setWidget(self.grid_widget) 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.""" 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) 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) 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: config = load_config() pricing = config.get("pricing", {}) if pricing.get("enabled", False): price = calculate_price(total, config) self.summary_label.setText( f"{total} impression{'s' if total > 1 else ''} — {price:.2f} €" ) else: self.summary_label.setText( f"{selected} photo{'s' if selected > 1 else ''} — {total} impression{'s' if total > 1 else ''}" ) 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;") def _validate(self): """Envoie les photos sélectionnées à l'écran d'impression.""" to_print = [] for card in self.cards: if card.quantity > 0: to_print.append((card.path, card.quantity)) if not to_print: return self.main_window.print_screen.set_print_list(to_print) self.main_window.show_print() 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]