V3.2.0 — Macros, grille vignettes, progression fun, shuffle, sample doc

- Macros : enregistrer la derniere action sous un nom, rejouer, supprimer
  Menu Macros avec liste des macros enregistrees
- Grille adaptive dans le panneau de vignettes (s'adapte a la largeur)
- Barre de progression avec messages fun/rigolos
  (Pliage des pages en origami, Consultation des esprits de Gutenberg...)
- Separer paires/impaires en 2 fichiers
- Creer un document test numerote (Pages > Creer un document test)
- _last_action sauvegardee pour booklet, nup, step_repeat, trim_shift

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jules
2026-03-21 16:22:48 +01:00
parent 1d48e103e3
commit 202a4fc536
4 changed files with 188 additions and 3 deletions

View File

@@ -229,8 +229,10 @@ class MainWindow(QMainWindow):
self._add_action(pages_menu, "Pivoter -90", None, lambda: self._rotate_current(-90))
self._add_action(pages_menu, "Pivoter 180", None, lambda: self._rotate_current(180))
self._add_action(pages_menu, "Inverser l'ordre des pages", None, self._do_reverse_pages)
self._add_action(pages_menu, "Separer paires/impaires", None, self._do_shuffle_even_odd)
pages_menu.addSeparator()
self._add_action(pages_menu, "Inserer des pages blanches", None, self._do_insert_blank)
self._add_action(pages_menu, "Creer un document test...", None, self._do_create_sample)
# Annotations
annot_menu = menubar.addMenu("Annotations")
@@ -247,6 +249,13 @@ class MainWindow(QMainWindow):
self._add_action(annot_menu, "Definir les fonds perdus", None, self._do_bleeds)
self._add_action(annot_menu, "Enlever les fonds perdus", None, self._do_remove_bleeds)
# Macros
self._macros_menu = menubar.addMenu("Macros")
self._add_action(self._macros_menu, "Enregistrer la derniere action...", None, self._do_save_macro)
self._add_action(self._macros_menu, "Gerer les macros...", None, self._do_manage_macros)
self._macros_menu.addSeparator()
self._refresh_macros_menu()
# Aide
help_menu = menubar.addMenu("Aide")
self._add_action(help_menu, "Rechercher une mise a jour...", None, self._do_check_update)
@@ -501,10 +510,29 @@ class MainWindow(QMainWindow):
os.close(fd)
return path
_FUN_MESSAGES = [
"Pliage des pages en origami...",
"Consultation des esprits de Gutenberg...",
"Negociation avec les pixels...",
"Alignement des atomes de papier...",
"Invocation du dieu de l'imposition...",
"Repassage des pages froissees...",
"Calibrage du massicot virtuel...",
"Appel a un ami imprimeur...",
"Les pages font la queue...",
"Rechauffement de l'encre numerique...",
"Comptage des points par pouce...",
"Meditation sur les marges...",
"Rangement des pages par ordre alphabetique... non, numerique !",
"Verification que le papier est bien a plat...",
]
def _show_progress(self):
import random
self._progress.setRange(0, 0)
self._progress.show()
self._status_label.setText("Traitement en cours...")
msg = random.choice(self._FUN_MESSAGES)
self._status_label.setText(msg)
QApplication.processEvents()
def _hide_progress(self):
@@ -612,6 +640,7 @@ class MainWindow(QMainWindow):
output = self._make_temp()
try:
trim_and_shift(self._current_file, output, **settings)
self._last_action = {"action": "trim_shift", "settings": settings}
self._show_result(output)
except Exception as e:
self._hide_progress()
@@ -630,6 +659,7 @@ class MainWindow(QMainWindow):
try:
create_booklet(self._current_file, output, **settings)
self._show_result(output)
self._last_action = {"action": "booklet", "settings": settings}
except Exception as e:
self._hide_progress()
QMessageBox.critical(self, "Erreur", str(e))
@@ -644,6 +674,7 @@ class MainWindow(QMainWindow):
output = self._make_temp()
try:
nup_pages(self._current_file, output, **settings)
self._last_action = {"action": "nup", "settings": settings}
self._show_result(output)
except Exception as e:
self._hide_progress()
@@ -659,6 +690,7 @@ class MainWindow(QMainWindow):
output = self._make_temp()
try:
step_and_repeat(self._current_file, output, **settings)
self._last_action = {"action": "step_repeat", "settings": settings}
self._show_result(output)
except Exception as e:
self._hide_progress()
@@ -981,6 +1013,153 @@ class MainWindow(QMainWindow):
# ── A propos ──
# ── Macros ──
def _last_action_info(self):
return getattr(self, '_last_action', None)
def _do_save_macro(self):
from PyQt6.QtWidgets import QInputDialog
name, ok = QInputDialog.getText(self, "Enregistrer la macro", "Nom de la macro :")
if ok and name.strip():
info = self._last_action_info()
if info:
from app.presets.preset_manager import save_preset
save_preset(name.strip(), info["action"], info["settings"])
self._refresh_macros_menu()
self._status_label.setText(f"Macro '{name.strip()}' enregistree")
else:
QMessageBox.information(self, "Macro", "Effectuez d'abord une action a enregistrer.")
def _do_manage_macros(self):
from app.presets.preset_manager import list_presets, delete_preset
from PyQt6.QtWidgets import QDialog, QVBoxLayout, QListWidget, QPushButton, QHBoxLayout
presets = list_presets()
if not presets:
QMessageBox.information(self, "Macros", "Aucune macro enregistree.")
return
dlg = QDialog(self)
dlg.setWindowTitle("Gerer les macros")
dlg.setMinimumWidth(350)
layout = QVBoxLayout(dlg)
lst = QListWidget()
lst.addItems(presets.keys())
layout.addWidget(lst)
bl = QHBoxLayout()
btn_del = QPushButton("Supprimer")
def do_del():
item = lst.currentItem()
if item:
delete_preset(item.text())
lst.takeItem(lst.row(item))
self._refresh_macros_menu()
btn_del.clicked.connect(do_del)
bl.addWidget(btn_del)
btn_close = QPushButton("Fermer")
btn_close.clicked.connect(dlg.close)
bl.addWidget(btn_close)
layout.addLayout(bl)
dlg.exec()
def _refresh_macros_menu(self):
from app.presets.preset_manager import list_presets
# Supprimer les actions apres le separateur
actions = self._macros_menu.actions()
found_sep = False
for act in actions:
if act.isSeparator():
found_sep = True
continue
if found_sep:
self._macros_menu.removeAction(act)
# Ajouter les presets
presets = list_presets()
for name in presets:
self._macros_menu.addAction(name, lambda n=name: self._run_macro(n))
def _run_macro(self, name):
from app.presets.preset_manager import list_presets
presets = list_presets()
preset = presets.get(name)
if not preset or not self._require_file():
return
action = preset["action"]
settings = preset["settings"]
self._show_progress()
output = self._make_temp()
try:
if action == "booklet":
create_booklet(self._current_file, output, **settings)
elif action == "nup":
nup_pages(self._current_file, output, **settings)
self._last_action = {"action": "nup", "settings": settings}
elif action == "step_repeat":
step_and_repeat(self._current_file, output, **settings)
self._last_action = {"action": "step_repeat", "settings": settings}
elif action == "crop":
crop_pages(self._current_file, output, **settings)
elif action == "trim_shift":
trim_and_shift(self._current_file, output, **settings)
self._last_action = {"action": "trim_shift", "settings": settings}
else:
self._hide_progress()
QMessageBox.warning(self, "Macro", f"Action inconnue : {action}")
return
self._show_result(output)
self._status_label.setText(f"Macro '{name}' executee")
except Exception as e:
self._hide_progress()
QMessageBox.critical(self, "Erreur", str(e))
# ── Pages supplementaires ──
def _do_shuffle_even_odd(self):
"""Separer pages paires et impaires en deux documents."""
if not self._require_file():
return
from pypdf import PdfReader, PdfWriter
reader = PdfReader(self._current_file)
writer_odd = PdfWriter()
writer_even = PdfWriter()
for i, page in enumerate(reader.pages):
if (i + 1) % 2 == 0:
writer_even.add_page(page)
else:
writer_odd.add_page(page)
# Sauvegarder les deux
path_odd, _ = QFileDialog.getSaveFileName(self, "Enregistrer pages impaires", "", "PDF (*.pdf)")
if not path_odd:
return
with open(path_odd, "wb") as f:
writer_odd.write(f)
path_even, _ = QFileDialog.getSaveFileName(self, "Enregistrer pages paires", "", "PDF (*.pdf)")
if path_even:
with open(path_even, "wb") as f:
writer_even.write(f)
self._status_label.setText("Pages paires/impaires separees")
def _do_create_sample(self):
"""Creer un document test numerote."""
from PyQt6.QtWidgets import QInputDialog
num, ok = QInputDialog.getInt(self, "Document test", "Nombre de pages :", 8, 1, 200)
if not ok:
return
from reportlab.pdfgen import canvas as rl_canvas
from reportlab.lib.pagesizes import A4
output = self._make_temp()
c = rl_canvas.Canvas(output, pagesize=A4)
for i in range(1, num + 1):
c.setFont("Helvetica-Bold", 72)
c.drawCentredString(A4[0] / 2, A4[1] / 2, str(i))
c.setFont("Helvetica", 14)
c.drawCentredString(A4[0] / 2, A4[1] / 2 - 40, f"Page {i} sur {num}")
c.setFont("Helvetica", 10)
c.drawCentredString(A4[0] / 2, 30, "CopyDev Imposing Tool — Document test")
c.showPage()
c.save()
self._open_pdf(output)
self._status_label.setText(f"Document test de {num} pages cree")
def _do_check_update(self):
from app.updater import check_for_update, do_update, VERSION
self._status_label.setText("Recherche de mises a jour...")

View File

@@ -28,6 +28,12 @@ class PagePanel(QWidget):
self._list = QListWidget()
self._list.setIconSize(QSize(120, 170))
self._list.setSpacing(4)
self._list.setViewMode(QListWidget.ViewMode.IconMode)
self._list.setResizeMode(QListWidget.ResizeMode.Adjust)
self._list.setWrapping(True)
self._list.setGridSize(QSize(140, 190))
self._list.setUniformItemSizes(True)
self._list.setMovement(QListWidget.Movement.Free)
self._list.setStyleSheet("""
QListWidget {
background: #e0e0e0;

View File

@@ -14,7 +14,7 @@ REPO = "jules/copydev-imposing-tool"
BRANCH = "master"
# Version actuelle (incrementee a chaque release)
VERSION = "3.1.0"
VERSION = "3.2.0"
def get_remote_version():