Files
copydev-imposing-tool/app/main_window.py
Jules 5bd4a18660 Ajout logo CopyDev, icone et page A propos professionnelle
- 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>
2026-03-20 11:19:19 +01:00

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()