From cd8f2dfed3b187947d71319d1cae1bfae3129321 Mon Sep 17 00:00:00 2001 From: Jules Date: Sat, 21 Mar 2026 22:19:07 +0100 Subject: [PATCH] =?UTF-8?q?Multi-pose=20:=20m=C3=AAme=20photo=20dupliqu?= =?UTF-8?q?=C3=A9e=20+=20crop=20to=20fill?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - La photo en cours est dupliquée dans toutes les zones du template - Crop to fill : l'image remplit chaque zone au maximum (pas de bords) - Fonctionne avec l'image éditée (filtres, etc.) pas juste l'originale Co-Authored-By: Claude Opus 4.6 (1M context) --- src/ui/screens/editor_screen.py | 100 +++++++++++++++----------------- 1 file changed, 47 insertions(+), 53 deletions(-) diff --git a/src/ui/screens/editor_screen.py b/src/ui/screens/editor_screen.py index 9179ecd..5c7807f 100644 --- a/src/ui/screens/editor_screen.py +++ b/src/ui/screens/editor_screen.py @@ -313,79 +313,73 @@ class EditorScreen(QWidget): if not zones: return + # Utilise la photo en cours (ou l'image éditée du canvas) + source_pixmap = None + if self.pixmap_item: + source_pixmap = self.pixmap_item.pixmap() + elif self.current_image_path: + source_pixmap = QPixmap(self.current_image_path) + + if not source_pixmap or source_pixmap.isNull(): + return + self.multipose_zones = zones - self.multipose_images = {} + self.multipose_source = source_pixmap - # Demander les photos pour chaque zone - sr = self.scene.sceneRect() - canvas_w, canvas_h = int(sr.width()) or 1800, int(sr.height()) or 1200 + # Taille canvas = format d'impression (ex: 1800x1200 pour 15x10) + canvas_w = 1800 + canvas_h = 1200 + self._render_multipose(source_pixmap, zones, canvas_w, canvas_h) + + def _crop_to_fill(self, pixmap, target_w, target_h): + """Crop centré pour remplir exactement target_w x target_h.""" + src_w = pixmap.width() + src_h = pixmap.height() + src_ratio = src_w / src_h + target_ratio = target_w / target_h + + if src_ratio > target_ratio: + # Image trop large → crop horizontal + new_w = int(src_h * target_ratio) + x_offset = (src_w - new_w) // 2 + cropped = pixmap.copy(x_offset, 0, new_w, src_h) + else: + # Image trop haute → crop vertical + new_h = int(src_w / target_ratio) + y_offset = (src_h - new_h) // 2 + cropped = pixmap.copy(0, y_offset, src_w, new_h) + + return cropped.scaled(target_w, target_h, + Qt.AspectRatioMode.IgnoreAspectRatio, + Qt.TransformationMode.SmoothTransformation) + + def _render_multipose(self, source_pixmap, zones, canvas_w, canvas_h): + """Dessine le multi-pose : même image dupliquée, crop to fill.""" self.scene.clear() self.pixmap_item = None - # Canvas blanc - white = QImage(canvas_w, canvas_h, QImage.Format.Format_ARGB32) - white.fill(QColor(255, 255, 255)) - bg = self.scene.addPixmap(QPixmap.fromImage(white)) - self.scene.setSceneRect(QRectF(0, 0, canvas_w, canvas_h)) - - # Dessiner les zones avec numéros - for i, (zx, zy, zw, zh) in enumerate(zones): - rx = int(zx * canvas_w) - ry = int(zy * canvas_h) - rw = int(zw * canvas_w) - rh = int(zh * canvas_h) - - # Bordure de zone - from PyQt6.QtWidgets import QGraphicsRectItem - from PyQt6.QtGui import QPen, QBrush - rect = self.scene.addRect(rx + 2, ry + 2, rw - 4, rh - 4, - QPen(QColor(100, 100, 100), 2), - QBrush(QColor(240, 240, 240))) - - # Numéro - text = self.scene.addText(str(i + 1), QFont("", 40)) - text.setDefaultTextColor(QColor(150, 150, 150)) - text.setPos(rx + rw / 2 - 20, ry + rh / 2 - 30) - - self.view.fitInView(self.scene.sceneRect(), Qt.AspectRatioMode.KeepAspectRatio) - - # Charger les photos depuis la galerie d'import - import_photos = self.main_window.import_screen.photos - if import_photos: - self._fill_multipose_from_list(import_photos, zones, canvas_w, canvas_h) - - def _fill_multipose_from_list(self, photos, zones, canvas_w, canvas_h): - """Remplit les zones multi-pose avec les premières photos disponibles.""" - self.scene.clear() - # Canvas blanc white = QImage(canvas_w, canvas_h, QImage.Format.Format_ARGB32) white.fill(QColor(255, 255, 255)) self.pixmap_item = self.scene.addPixmap(QPixmap.fromImage(white)) self.scene.setSceneRect(QRectF(0, 0, canvas_w, canvas_h)) - gap = 4 # espace entre les photos + gap = 6 # espace entre les zones for i, (zx, zy, zw, zh) in enumerate(zones): - if i >= len(photos): - break rx = int(zx * canvas_w) + gap ry = int(zy * canvas_h) + gap rw = int(zw * canvas_w) - gap * 2 rh = int(zh * canvas_h) - gap * 2 - pixmap = QPixmap(photos[i]) - if pixmap.isNull(): + if rw <= 0 or rh <= 0: continue - scaled = pixmap.scaled(rw, rh, - Qt.AspectRatioMode.KeepAspectRatio, - Qt.TransformationMode.SmoothTransformation) - item = self.scene.addPixmap(scaled) - # Centrer dans la zone - offset_x = rx + (rw - scaled.width()) / 2 - offset_y = ry + (rh - scaled.height()) / 2 - item.setPos(offset_x, offset_y) + + # Crop to fill : l'image remplit toute la zone + filled = self._crop_to_fill(source_pixmap, rw, rh) + item = self.scene.addPixmap(filled) + item.setPos(rx, ry) self.view.fitInView(self.scene.sceneRect(), Qt.AspectRatioMode.KeepAspectRatio)