"""Dialogue Recadrer les pages.""" from PyQt6.QtWidgets import (QDialog, QVBoxLayout, QHBoxLayout, QLabel, QGroupBox, QDoubleSpinBox, QComboBox, QRadioButton, QDialogButtonBox, QLineEdit) from app.engine.pdf_utils import mm_to_pt, pt_to_mm class CropDialog(QDialog): def __init__(self, page_width_pt=595, page_height_pt=842, total_pages=1, current_page=0, parent=None): super().__init__(parent) self.setWindowTitle("Recadrer") self.setMinimumWidth(450) self._page_w = page_width_pt self._page_h = page_height_pt self._total_pages = total_pages self._current_page = current_page self._setup_ui() self._update_preview() def _setup_ui(self): layout = QVBoxLayout(self) # Info page actuelle pw_mm = pt_to_mm(self._page_w) ph_mm = pt_to_mm(self._page_h) info = QLabel(f"Page actuelle : {pw_mm:.1f} x {ph_mm:.1f} mm") info.setStyleSheet("color: #888; font-size: 11px;") layout.addWidget(info) # Mode grp_mode = QGroupBox("Mode de recadrage") ml = QVBoxLayout(grp_mode) self.rb_margins = QRadioButton("Par marges (retirer de chaque cote)") self.rb_margins.setChecked(True) self.rb_size = QRadioButton("Par taille finale") ml.addWidget(self.rb_margins) ml.addWidget(self.rb_size) layout.addWidget(grp_mode) self.rb_margins.toggled.connect(self._on_mode_changed) self.rb_size.toggled.connect(self._on_mode_changed) # Marges de recadrage self.grp_margins_input = QGroupBox("Marges a retirer (mm)") mgl = QVBoxLayout(self.grp_margins_input) self.margin_spins = {} row1 = QHBoxLayout() row1.addStretch() row1.addWidget(QLabel("Haut :")) self.margin_spins["haut"] = QDoubleSpinBox() self.margin_spins["haut"].setRange(0, 500) self.margin_spins["haut"].setSuffix(" mm") self.margin_spins["haut"].valueChanged.connect(self._update_preview) row1.addWidget(self.margin_spins["haut"]) row1.addStretch() mgl.addLayout(row1) row2 = QHBoxLayout() row2.addWidget(QLabel("Gauche :")) self.margin_spins["gauche"] = QDoubleSpinBox() self.margin_spins["gauche"].setRange(0, 500) self.margin_spins["gauche"].setSuffix(" mm") self.margin_spins["gauche"].valueChanged.connect(self._update_preview) row2.addWidget(self.margin_spins["gauche"]) row2.addStretch() row2.addWidget(QLabel("Droite :")) self.margin_spins["droite"] = QDoubleSpinBox() self.margin_spins["droite"].setRange(0, 500) self.margin_spins["droite"].setSuffix(" mm") self.margin_spins["droite"].valueChanged.connect(self._update_preview) row2.addWidget(self.margin_spins["droite"]) mgl.addLayout(row2) row3 = QHBoxLayout() row3.addStretch() row3.addWidget(QLabel("Bas :")) self.margin_spins["bas"] = QDoubleSpinBox() self.margin_spins["bas"].setRange(0, 500) self.margin_spins["bas"].setSuffix(" mm") self.margin_spins["bas"].valueChanged.connect(self._update_preview) row3.addWidget(self.margin_spins["bas"]) row3.addStretch() mgl.addLayout(row3) layout.addWidget(self.grp_margins_input) # Taille finale self.grp_size_input = QGroupBox("Taille finale (mm)") sgl = QHBoxLayout(self.grp_size_input) sgl.addWidget(QLabel("Largeur :")) self.spin_final_w = QDoubleSpinBox() self.spin_final_w.setRange(10, 5000) self.spin_final_w.setValue(pt_to_mm(self._page_w)) self.spin_final_w.setSuffix(" mm") self.spin_final_w.valueChanged.connect(self._update_preview) sgl.addWidget(self.spin_final_w) sgl.addWidget(QLabel("Hauteur :")) self.spin_final_h = QDoubleSpinBox() self.spin_final_h.setRange(10, 5000) self.spin_final_h.setValue(pt_to_mm(self._page_h)) self.spin_final_h.setSuffix(" mm") self.spin_final_h.valueChanged.connect(self._update_preview) sgl.addWidget(self.spin_final_h) layout.addWidget(self.grp_size_input) self.grp_size_input.setVisible(False) # Apercu dimension finale self.lbl_preview = QLabel() self.lbl_preview.setStyleSheet("font-weight: bold; font-size: 13px; color: #0066cc; padding: 8px;") layout.addWidget(self.lbl_preview) # Appliquer a grp_apply = QGroupBox("Appliquer a") al = QVBoxLayout(grp_apply) self.rb_current = QRadioButton(f"Page courante ({self._current_page + 1})") self.rb_current.setChecked(True) self.rb_all = QRadioButton("Toutes les pages") self.rb_even = QRadioButton("Pages paires") self.rb_odd = QRadioButton("Pages impaires") self.rb_range = QRadioButton("Plage de pages :") al.addWidget(self.rb_current) al.addWidget(self.rb_all) al.addWidget(self.rb_even) al.addWidget(self.rb_odd) range_layout = QHBoxLayout() range_layout.addWidget(self.rb_range) self.edit_range = QLineEdit() self.edit_range.setPlaceholderText("ex: 1-3, 5, 8-10") self.edit_range.setEnabled(False) range_layout.addWidget(self.edit_range) al.addLayout(range_layout) layout.addWidget(grp_apply) self.rb_range.toggled.connect(self.edit_range.setEnabled) # Boutons buttons = QDialogButtonBox( QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel ) buttons.accepted.connect(self.accept) buttons.rejected.connect(self.reject) layout.addWidget(buttons) def _on_mode_changed(self): self.grp_margins_input.setVisible(self.rb_margins.isChecked()) self.grp_size_input.setVisible(self.rb_size.isChecked()) self._update_preview() def _update_preview(self): if self.rb_margins.isChecked(): top = self.margin_spins["haut"].value() bottom = self.margin_spins["bas"].value() left = self.margin_spins["gauche"].value() right = self.margin_spins["droite"].value() w_mm = pt_to_mm(self._page_w) - left - right h_mm = pt_to_mm(self._page_h) - top - bottom else: w_mm = self.spin_final_w.value() h_mm = self.spin_final_h.value() if w_mm <= 0 or h_mm <= 0: self.lbl_preview.setText("Dimensions invalides !") self.lbl_preview.setStyleSheet("font-weight: bold; font-size: 13px; color: red; padding: 8px;") else: self.lbl_preview.setText(f"Resultat : {w_mm:.1f} x {h_mm:.1f} mm") self.lbl_preview.setStyleSheet("font-weight: bold; font-size: 13px; color: #0066cc; padding: 8px;") def _parse_range(self): """Parser la plage de pages. Retourne une liste d'indices (0-based).""" text = self.edit_range.text().strip() if not text: return [] indices = [] for part in text.split(","): part = part.strip() if "-" in part: try: start, end = part.split("-", 1) for p in range(int(start), int(end) + 1): if 1 <= p <= self._total_pages: indices.append(p - 1) except ValueError: pass else: try: p = int(part) if 1 <= p <= self._total_pages: indices.append(p - 1) except ValueError: pass return sorted(set(indices)) def get_settings(self): # Mode if self.rb_margins.isChecked(): mode = "margins" crop_top = mm_to_pt(self.margin_spins["haut"].value()) crop_bottom = mm_to_pt(self.margin_spins["bas"].value()) crop_left = mm_to_pt(self.margin_spins["gauche"].value()) crop_right = mm_to_pt(self.margin_spins["droite"].value()) else: mode = "size" final_w = mm_to_pt(self.spin_final_w.value()) final_h = mm_to_pt(self.spin_final_h.value()) crop_left = (self._page_w - final_w) / 2 crop_right = crop_left crop_top = (self._page_h - final_h) / 2 crop_bottom = crop_top # Pages cibles if self.rb_current.isChecked(): pages = [self._current_page] elif self.rb_all.isChecked(): pages = list(range(self._total_pages)) elif self.rb_even.isChecked(): pages = [i for i in range(self._total_pages) if (i + 1) % 2 == 0] elif self.rb_odd.isChecked(): pages = [i for i in range(self._total_pages) if (i + 1) % 2 != 0] else: pages = self._parse_range() return { "crop_top": crop_top, "crop_bottom": crop_bottom, "crop_left": crop_left, "crop_right": crop_right, "pages": pages, }