V2.1.0 — Formats personnalises + reperes de coupe

- Formats personnalises dans tous les menus deroulants de format :
  - "Personnalise..." : saisie libre largeur x hauteur en mm
  - "Ajouter un format..." : sauvegarder un format custom (persistant)
  - Formats custom affiches dans le dropdown avec separateur
- Reperes de coupe (Annotations > Reperes de coupe) :
  - Longueur, distance du bord, epaisseur configurables
  - Couleur : noir, gris, CMYK
  - Reperes de centrage optionnels
  - Appliquer a : toutes, paires, impaires, page courante
  - Enlever les reperes de coupe (menu)
- Step & Repeat : ajout des marges (haut, bas, gauche, droite)
- Tous les dialogues de format migres vers format_combo

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jules
2026-03-21 08:00:55 +01:00
parent 9cc256ff51
commit c9212a30ea
11 changed files with 457 additions and 69 deletions

View File

@@ -0,0 +1,104 @@
"""Dialogue pour saisir un format personnalise."""
import os
import sys
import json
from PyQt6.QtWidgets import (QDialog, QVBoxLayout, QHBoxLayout, QLabel,
QDoubleSpinBox, QLineEdit, QDialogButtonBox,
QGroupBox, QCheckBox)
from app.engine.pdf_utils import mm_to_pt
CUSTOM_SIZES_FILE = os.path.join(
os.path.dirname(sys.executable) if getattr(sys, 'frozen', False)
else os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))),
".custom_sizes.json"
)
def load_custom_sizes():
"""Charger les formats personnalises sauvegardes."""
if not os.path.exists(CUSTOM_SIZES_FILE):
return {}
try:
with open(CUSTOM_SIZES_FILE, "r") as f:
return json.load(f)
except Exception:
return {}
def save_custom_sizes(sizes):
"""Sauvegarder les formats personnalises."""
try:
with open(CUSTOM_SIZES_FILE, "w") as f:
json.dump(sizes, f, indent=2)
except Exception:
pass
class CustomSizeDialog(QDialog):
"""Dialogue pour saisir une taille personnalisee."""
def __init__(self, save_mode=False, parent=None):
super().__init__(parent)
self.setWindowTitle("Ajouter un format" if save_mode else "Format personnalise")
self.setMinimumWidth(350)
self._save_mode = save_mode
self._setup_ui()
def _setup_ui(self):
layout = QVBoxLayout(self)
if self._save_mode:
name_layout = QHBoxLayout()
name_layout.addWidget(QLabel("Nom du format :"))
self.edit_name = QLineEdit()
self.edit_name.setPlaceholderText("ex: Carte de visite")
name_layout.addWidget(self.edit_name)
layout.addLayout(name_layout)
layout.addSpacing(10)
grp = QGroupBox("Dimensions (mm)")
gl = QHBoxLayout(grp)
gl.addWidget(QLabel("Largeur :"))
self.spin_w = QDoubleSpinBox()
self.spin_w.setRange(10, 5000)
self.spin_w.setValue(210)
self.spin_w.setSuffix(" mm")
gl.addWidget(self.spin_w)
gl.addWidget(QLabel("Hauteur :"))
self.spin_h = QDoubleSpinBox()
self.spin_h.setRange(10, 5000)
self.spin_h.setValue(297)
self.spin_h.setSuffix(" mm")
gl.addWidget(self.spin_h)
layout.addWidget(grp)
self.chk_landscape = QCheckBox("Paysage (inverser largeur/hauteur)")
layout.addWidget(self.chk_landscape)
buttons = QDialogButtonBox(
QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel
)
buttons.accepted.connect(self.accept)
buttons.rejected.connect(self.reject)
layout.addWidget(buttons)
def get_size(self):
"""Retourne (largeur, hauteur) en points."""
w = mm_to_pt(self.spin_w.value())
h = mm_to_pt(self.spin_h.value())
if self.chk_landscape.isChecked():
w, h = h, w
return (w, h)
def get_name(self):
if self._save_mode:
return self.edit_name.text().strip()
return f"{self.spin_w.value():.0f}x{self.spin_h.value():.0f}mm"
def get_size_mm(self):
w = self.spin_w.value()
h = self.spin_h.value()
if self.chk_landscape.isChecked():
w, h = h, w
return (w, h)