- Logo CopyDev 500x500 + icone 64x64 - Icone affichee dans la barre de titre et la taskbar - Page A propos avec logo, version, description, coordonnees contact@copydev.fr, www.copydev.fr, copyright 2026 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
413 lines
14 KiB
Python
413 lines
14 KiB
Python
"""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)
|
|
|
|
# Icone de la fenetre
|
|
from PyQt6.QtGui import QIcon
|
|
icon_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
|
|
"resources", "logo.png")
|
|
if os.path.exists(icon_path):
|
|
self.setWindowIcon(QIcon(icon_path))
|
|
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):
|
|
from PyQt6.QtWidgets import QDialog, QVBoxLayout, QHBoxLayout, QLabel, QDialogButtonBox
|
|
from PyQt6.QtGui import QPixmap
|
|
from PyQt6.QtCore import Qt
|
|
|
|
dlg = QDialog(self)
|
|
dlg.setWindowTitle("A propos")
|
|
dlg.setFixedSize(480, 380)
|
|
layout = QVBoxLayout(dlg)
|
|
|
|
# Logo
|
|
logo_label = QLabel()
|
|
logo_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
|
|
"resources", "logo.png")
|
|
if os.path.exists(logo_path):
|
|
pixmap = QPixmap(logo_path).scaled(120, 120, Qt.AspectRatioMode.KeepAspectRatio,
|
|
Qt.TransformationMode.SmoothTransformation)
|
|
logo_label.setPixmap(pixmap)
|
|
logo_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
|
layout.addWidget(logo_label)
|
|
|
|
# Titre
|
|
title = QLabel("<h2>Copydev Imposing Tool</h2>")
|
|
title.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
|
layout.addWidget(title)
|
|
|
|
# Version
|
|
version = QLabel("Version 1.0.0")
|
|
version.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
|
version.setStyleSheet("color: #666;")
|
|
layout.addWidget(version)
|
|
|
|
layout.addSpacing(10)
|
|
|
|
# Description
|
|
desc = QLabel(
|
|
"Outil professionnel d'imposition PDF.\n"
|
|
"Creation de livrets, n-up, repetition, imposition manuelle,\n"
|
|
"numerotation, masquage et fonds perdus."
|
|
)
|
|
desc.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
|
desc.setWordWrap(True)
|
|
layout.addWidget(desc)
|
|
|
|
layout.addSpacing(15)
|
|
|
|
# Coordonnees
|
|
contact = QLabel(
|
|
'<p style="text-align:center;">'
|
|
'<b>CopyDev</b><br>'
|
|
'Jules Dubois<br>'
|
|
'<a href="mailto:contact@copydev.fr">contact@copydev.fr</a><br>'
|
|
'<a href="https://copydev.fr">www.copydev.fr</a>'
|
|
'</p>'
|
|
)
|
|
contact.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
|
contact.setOpenExternalLinks(True)
|
|
layout.addWidget(contact)
|
|
|
|
layout.addSpacing(5)
|
|
|
|
# Copyright
|
|
copy_label = QLabel("© 2026 CopyDev — Tous droits reserves")
|
|
copy_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
|
copy_label.setStyleSheet("color: #999; font-size: 11px;")
|
|
layout.addWidget(copy_label)
|
|
|
|
# Bouton Fermer
|
|
buttons = QDialogButtonBox(QDialogButtonBox.StandardButton.Close)
|
|
buttons.rejected.connect(dlg.close)
|
|
layout.addWidget(buttons)
|
|
|
|
dlg.exec()
|