Initial commit — Copydev Imposing Tool
Outil d'imposition PDF desktop (PyQt6) remplacant Quite Imposing Plus. 9 fonctions : livret, n-up, step & repeat, imposition manuelle, jointure pages, pages blanches, numerotation, masquage, fonds perdus. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
341
app/main_window.py
Normal file
341
app/main_window.py
Normal file
@@ -0,0 +1,341 @@
|
||||
"""Fenetre principale de Copydev Imposing Tool."""
|
||||
|
||||
import os
|
||||
import tempfile
|
||||
from PyQt6.QtWidgets import (QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
|
||||
QPushButton, QLabel, QFileDialog, QMessageBox,
|
||||
QGroupBox, QStatusBar, QMenuBar, QSplitter,
|
||||
QProgressBar)
|
||||
from PyQt6.QtCore import Qt
|
||||
from PyQt6.QtGui import QAction
|
||||
|
||||
from app.pdf_viewer import PdfViewer
|
||||
from app.dialogs.booklet_dialog import BookletDialog
|
||||
from app.dialogs.nup_dialog import NupDialog
|
||||
from app.dialogs.step_repeat_dialog import StepRepeatDialog
|
||||
from app.dialogs.manual_impose_dialog import ManualImposeDialog
|
||||
from app.dialogs.join_pages_dialog import JoinPagesDialog
|
||||
from app.dialogs.insert_blank_dialog import InsertBlankDialog
|
||||
from app.dialogs.page_numbers_dialog import PageNumbersDialog
|
||||
from app.dialogs.masking_dialog import MaskingDialog
|
||||
from app.dialogs.bleeds_dialog import BleedsDialog
|
||||
|
||||
from app.engine.booklet import create_booklet
|
||||
from app.engine.nup import nup_pages
|
||||
from app.engine.step_repeat import step_and_repeat
|
||||
from app.engine.manual_impose import manual_impose
|
||||
from app.engine.join_pages import join_pages
|
||||
from app.engine.insert_blank import insert_blank_pages
|
||||
from app.engine.page_numbers import add_page_numbers
|
||||
from app.engine.masking import apply_masking
|
||||
from app.engine.bleeds import define_bleeds
|
||||
|
||||
|
||||
class MainWindow(QMainWindow):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.setWindowTitle("Copydev Imposing Tool")
|
||||
self.setMinimumSize(1000, 700)
|
||||
self._current_file = None
|
||||
self._result_file = None
|
||||
self._setup_menu()
|
||||
self._setup_ui()
|
||||
self._setup_statusbar()
|
||||
|
||||
def _setup_menu(self):
|
||||
menubar = self.menuBar()
|
||||
|
||||
# Menu Fichier
|
||||
file_menu = menubar.addMenu("Fichier")
|
||||
|
||||
open_act = QAction("Ouvrir PDF...", self)
|
||||
open_act.setShortcut("Ctrl+O")
|
||||
open_act.triggered.connect(self._open_file)
|
||||
file_menu.addAction(open_act)
|
||||
|
||||
save_act = QAction("Enregistrer le resultat...", self)
|
||||
save_act.setShortcut("Ctrl+S")
|
||||
save_act.triggered.connect(self._save_result)
|
||||
file_menu.addAction(save_act)
|
||||
|
||||
file_menu.addSeparator()
|
||||
|
||||
quit_act = QAction("Quitter", self)
|
||||
quit_act.setShortcut("Ctrl+Q")
|
||||
quit_act.triggered.connect(self.close)
|
||||
file_menu.addAction(quit_act)
|
||||
|
||||
# Menu Aide
|
||||
help_menu = menubar.addMenu("Aide")
|
||||
about_act = QAction("A propos...", self)
|
||||
about_act.triggered.connect(self._show_about)
|
||||
help_menu.addAction(about_act)
|
||||
|
||||
def _setup_ui(self):
|
||||
central = QWidget()
|
||||
self.setCentralWidget(central)
|
||||
main_layout = QHBoxLayout(central)
|
||||
|
||||
# === Panneau gauche : boutons ===
|
||||
left_panel = QWidget()
|
||||
left_panel.setFixedWidth(200)
|
||||
left_layout = QVBoxLayout(left_panel)
|
||||
left_layout.setContentsMargins(5, 5, 5, 5)
|
||||
|
||||
# Ouvrir
|
||||
btn_open = QPushButton("Ouvrir PDF...")
|
||||
btn_open.clicked.connect(self._open_file)
|
||||
left_layout.addWidget(btn_open)
|
||||
|
||||
btn_save = QPushButton("Enregistrer resultat...")
|
||||
btn_save.clicked.connect(self._save_result)
|
||||
left_layout.addWidget(btn_save)
|
||||
|
||||
left_layout.addSpacing(10)
|
||||
|
||||
# Imposition
|
||||
grp_impose = QGroupBox("Imposition")
|
||||
il = QVBoxLayout(grp_impose)
|
||||
|
||||
btn_booklet = QPushButton("Creer un livret")
|
||||
btn_booklet.clicked.connect(self._do_booklet)
|
||||
il.addWidget(btn_booklet)
|
||||
|
||||
btn_nup = QPushButton("N pages par feuille")
|
||||
btn_nup.clicked.connect(self._do_nup)
|
||||
il.addWidget(btn_nup)
|
||||
|
||||
btn_repeat = QPushButton("Repetition")
|
||||
btn_repeat.clicked.connect(self._do_step_repeat)
|
||||
il.addWidget(btn_repeat)
|
||||
|
||||
btn_manual = QPushButton("Imposition manuelle")
|
||||
btn_manual.clicked.connect(self._do_manual_impose)
|
||||
il.addWidget(btn_manual)
|
||||
|
||||
btn_join = QPushButton("Joindre deux pages")
|
||||
btn_join.clicked.connect(self._do_join_pages)
|
||||
il.addWidget(btn_join)
|
||||
|
||||
left_layout.addWidget(grp_impose)
|
||||
|
||||
# Pages
|
||||
grp_pages = QGroupBox("Pages")
|
||||
pl = QVBoxLayout(grp_pages)
|
||||
|
||||
btn_blank = QPushButton("Pages blanches")
|
||||
btn_blank.clicked.connect(self._do_insert_blank)
|
||||
pl.addWidget(btn_blank)
|
||||
|
||||
left_layout.addWidget(grp_pages)
|
||||
|
||||
# Annotations
|
||||
grp_annot = QGroupBox("Annotations")
|
||||
al = QVBoxLayout(grp_annot)
|
||||
|
||||
btn_numbers = QPushButton("Numerotation")
|
||||
btn_numbers.clicked.connect(self._do_page_numbers)
|
||||
al.addWidget(btn_numbers)
|
||||
|
||||
btn_mask = QPushButton("Masquage")
|
||||
btn_mask.clicked.connect(self._do_masking)
|
||||
al.addWidget(btn_mask)
|
||||
|
||||
btn_bleeds = QPushButton("Fonds perdus")
|
||||
btn_bleeds.clicked.connect(self._do_bleeds)
|
||||
al.addWidget(btn_bleeds)
|
||||
|
||||
left_layout.addWidget(grp_annot)
|
||||
left_layout.addStretch()
|
||||
|
||||
main_layout.addWidget(left_panel)
|
||||
|
||||
# === Zone centrale : viewer PDF ===
|
||||
self._viewer = PdfViewer()
|
||||
self._viewer.page_changed.connect(self._on_page_changed)
|
||||
main_layout.addWidget(self._viewer, 1)
|
||||
|
||||
def _setup_statusbar(self):
|
||||
self._statusbar = QStatusBar()
|
||||
self.setStatusBar(self._statusbar)
|
||||
self._status_label = QLabel("Pret")
|
||||
self._statusbar.addWidget(self._status_label, 1)
|
||||
self._file_label = QLabel("")
|
||||
self._statusbar.addPermanentWidget(self._file_label)
|
||||
|
||||
def _on_page_changed(self, page, total):
|
||||
pass
|
||||
|
||||
def _open_file(self):
|
||||
path, _ = QFileDialog.getOpenFileName(
|
||||
self, "Ouvrir un fichier PDF", "", "PDF (*.pdf)"
|
||||
)
|
||||
if path:
|
||||
self._current_file = path
|
||||
self._result_file = None
|
||||
self._viewer.load_pdf(path)
|
||||
self._file_label.setText(os.path.basename(path))
|
||||
self._status_label.setText(f"Fichier charge : {os.path.basename(path)}")
|
||||
|
||||
def _save_result(self):
|
||||
source = self._result_file or self._current_file
|
||||
if not source:
|
||||
QMessageBox.warning(self, "Erreur", "Aucun fichier a enregistrer.")
|
||||
return
|
||||
|
||||
path, _ = QFileDialog.getSaveFileName(
|
||||
self, "Enregistrer le resultat", "", "PDF (*.pdf)"
|
||||
)
|
||||
if path:
|
||||
if not path.lower().endswith(".pdf"):
|
||||
path += ".pdf"
|
||||
import shutil
|
||||
shutil.copy2(source, path)
|
||||
self._status_label.setText(f"Enregistre : {os.path.basename(path)}")
|
||||
|
||||
def _require_file(self):
|
||||
if not self._current_file:
|
||||
QMessageBox.warning(self, "Erreur", "Veuillez d'abord ouvrir un fichier PDF.")
|
||||
return False
|
||||
return True
|
||||
|
||||
def _make_temp(self):
|
||||
fd, path = tempfile.mkstemp(suffix=".pdf")
|
||||
os.close(fd)
|
||||
return path
|
||||
|
||||
def _show_result(self, output_path):
|
||||
self._result_file = output_path
|
||||
self._viewer.load_pdf(output_path)
|
||||
self._status_label.setText("Traitement termine")
|
||||
|
||||
def _do_booklet(self):
|
||||
if not self._require_file():
|
||||
return
|
||||
dlg = BookletDialog(self)
|
||||
if dlg.exec():
|
||||
settings = dlg.get_settings()
|
||||
output = self._make_temp()
|
||||
try:
|
||||
create_booklet(self._current_file, output, **settings)
|
||||
self._show_result(output)
|
||||
except Exception as e:
|
||||
QMessageBox.critical(self, "Erreur", str(e))
|
||||
|
||||
def _do_nup(self):
|
||||
if not self._require_file():
|
||||
return
|
||||
dlg = NupDialog(self)
|
||||
if dlg.exec():
|
||||
settings = dlg.get_settings()
|
||||
output = self._make_temp()
|
||||
try:
|
||||
nup_pages(self._current_file, output, **settings)
|
||||
self._show_result(output)
|
||||
except Exception as e:
|
||||
QMessageBox.critical(self, "Erreur", str(e))
|
||||
|
||||
def _do_step_repeat(self):
|
||||
if not self._require_file():
|
||||
return
|
||||
dlg = StepRepeatDialog(self)
|
||||
if dlg.exec():
|
||||
settings = dlg.get_settings()
|
||||
output = self._make_temp()
|
||||
try:
|
||||
step_and_repeat(self._current_file, output, **settings)
|
||||
self._show_result(output)
|
||||
except Exception as e:
|
||||
QMessageBox.critical(self, "Erreur", str(e))
|
||||
|
||||
def _do_manual_impose(self):
|
||||
if not self._require_file():
|
||||
return
|
||||
from pypdf import PdfReader
|
||||
reader = PdfReader(self._current_file)
|
||||
dlg = ManualImposeDialog(total_pages=len(reader.pages), parent=self)
|
||||
if dlg.exec():
|
||||
settings = dlg.get_settings()
|
||||
output = self._make_temp()
|
||||
try:
|
||||
manual_impose(self._current_file, output, **settings)
|
||||
self._show_result(output)
|
||||
except Exception as e:
|
||||
QMessageBox.critical(self, "Erreur", str(e))
|
||||
|
||||
def _do_join_pages(self):
|
||||
if not self._require_file():
|
||||
return
|
||||
dlg = JoinPagesDialog(self)
|
||||
if dlg.exec():
|
||||
settings = dlg.get_settings()
|
||||
output = self._make_temp()
|
||||
try:
|
||||
join_pages(self._current_file, output, **settings)
|
||||
self._show_result(output)
|
||||
except Exception as e:
|
||||
QMessageBox.critical(self, "Erreur", str(e))
|
||||
|
||||
def _do_insert_blank(self):
|
||||
if not self._require_file():
|
||||
return
|
||||
from pypdf import PdfReader
|
||||
reader = PdfReader(self._current_file)
|
||||
dlg = InsertBlankDialog(total_pages=len(reader.pages), parent=self)
|
||||
if dlg.exec():
|
||||
settings = dlg.get_settings()
|
||||
output = self._make_temp()
|
||||
try:
|
||||
insert_blank_pages(self._current_file, output, **settings)
|
||||
self._show_result(output)
|
||||
except Exception as e:
|
||||
QMessageBox.critical(self, "Erreur", str(e))
|
||||
|
||||
def _do_page_numbers(self):
|
||||
if not self._require_file():
|
||||
return
|
||||
dlg = PageNumbersDialog(self)
|
||||
if dlg.exec():
|
||||
settings = dlg.get_settings()
|
||||
output = self._make_temp()
|
||||
try:
|
||||
add_page_numbers(self._current_file, output, **settings)
|
||||
self._show_result(output)
|
||||
except Exception as e:
|
||||
QMessageBox.critical(self, "Erreur", str(e))
|
||||
|
||||
def _do_masking(self):
|
||||
if not self._require_file():
|
||||
return
|
||||
dlg = MaskingDialog(self)
|
||||
if dlg.exec():
|
||||
settings = dlg.get_settings()
|
||||
output = self._make_temp()
|
||||
try:
|
||||
apply_masking(self._current_file, output, **settings)
|
||||
self._show_result(output)
|
||||
except Exception as e:
|
||||
QMessageBox.critical(self, "Erreur", str(e))
|
||||
|
||||
def _do_bleeds(self):
|
||||
if not self._require_file():
|
||||
return
|
||||
dlg = BleedsDialog(self)
|
||||
if dlg.exec():
|
||||
settings = dlg.get_settings()
|
||||
output = self._make_temp()
|
||||
try:
|
||||
define_bleeds(self._current_file, output, **settings)
|
||||
self._show_result(output)
|
||||
except Exception as e:
|
||||
QMessageBox.critical(self, "Erreur", str(e))
|
||||
|
||||
def _show_about(self):
|
||||
QMessageBox.about(
|
||||
self,
|
||||
"A propos",
|
||||
"Copydev Imposing Tool\n\n"
|
||||
"Outil d'imposition PDF\n"
|
||||
"Remplace Quite Imposing Plus\n\n"
|
||||
"Copydev 2026"
|
||||
)
|
||||
Reference in New Issue
Block a user