V2.0.0 — Refonte interface complete
- Nouveau layout : menus en haut, vignettes a gauche, viewer au centre - Panneau vignettes redimensionnable (splitter) - Miniatures de chaque page - Drag & drop pour reordonner - Clic droit : supprimer page, inserer page blanche - Clic = naviguer vers la page - Viewer PDF ameliore : - Ajustement auto a l'ouverture - Roulette = defiler les pages - Ctrl+roulette = zoom - Drag & drop de fichier PDF sur la fenetre - Barre de progression pendant les traitements - Fusion de PDF (Fichier > Fusionner) - Enlever les fonds perdus (Annotations > Enlever) - Menus par categorie : Imposition, Pages, Annotations - Toolbar : Ouvrir, Enregistrer, Zoom, info page - Style sombre coherent CopyDev - Suppression du numero de telephone dans A propos Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,15 +1,18 @@
|
||||
"""Fenetre principale de Copydev Imposing Tool."""
|
||||
"""Fenetre principale de Copydev Imposing Tool — V2."""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
import shutil
|
||||
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
|
||||
QLabel, QFileDialog, QMessageBox, QStatusBar,
|
||||
QSplitter, QProgressBar, QApplication, QToolBar)
|
||||
from PyQt6.QtCore import Qt, QTimer
|
||||
from PyQt6.QtGui import QAction, QIcon, QDragEnterEvent, QDropEvent
|
||||
|
||||
from app.pdf_viewer import PdfViewer
|
||||
from app.page_panel import PagePanel
|
||||
|
||||
from app.dialogs.booklet_dialog import BookletDialog
|
||||
from app.dialogs.nup_dialog import NupDialog
|
||||
from app.dialogs.step_repeat_dialog import StepRepeatDialog
|
||||
@@ -36,172 +39,259 @@ class MainWindow(QMainWindow):
|
||||
super().__init__()
|
||||
from app.updater import VERSION
|
||||
self.setWindowTitle(f"CopyDev Imposing Tool v{VERSION} — www.copydev.fr")
|
||||
self.setMinimumSize(1000, 700)
|
||||
self.setMinimumSize(1100, 750)
|
||||
|
||||
# Icone de la fenetre
|
||||
import sys as _sys
|
||||
from PyQt6.QtGui import QIcon
|
||||
_base = _sys._MEIPASS if getattr(_sys, 'frozen', False) else os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
# Icone
|
||||
_base = sys._MEIPASS if getattr(sys, 'frozen', False) else os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
icon_path = os.path.join(_base, "resources", "logo.png")
|
||||
if os.path.exists(icon_path):
|
||||
self.setWindowIcon(QIcon(icon_path))
|
||||
|
||||
self._current_file = None
|
||||
self._result_file = None
|
||||
|
||||
# Drag & drop
|
||||
self.setAcceptDrops(True)
|
||||
|
||||
self._setup_menu()
|
||||
self._setup_toolbar()
|
||||
self._setup_ui()
|
||||
self._setup_statusbar()
|
||||
|
||||
# Style global
|
||||
self.setStyleSheet("""
|
||||
QMainWindow { background: #1a1a2e; }
|
||||
QMenuBar { background: #16213e; color: #e0e0e0; font-size: 13px; padding: 2px; }
|
||||
QMenuBar::item:selected { background: #0f3460; }
|
||||
QMenu { background: #16213e; color: #e0e0e0; border: 1px solid #333; }
|
||||
QMenu::item:selected { background: #0f3460; }
|
||||
QMenu::separator { background: #333; height: 1px; margin: 4px 10px; }
|
||||
QToolBar { background: #16213e; border: none; spacing: 4px; padding: 2px; }
|
||||
QToolBar QToolButton { color: #e0e0e0; padding: 4px 8px; border-radius: 3px; }
|
||||
QToolBar QToolButton:hover { background: #0f3460; }
|
||||
QStatusBar { background: #0f0f23; color: #888; font-size: 11px; }
|
||||
QSplitter::handle { background: #333; width: 3px; }
|
||||
""")
|
||||
|
||||
# ── Drag & Drop ──
|
||||
|
||||
def dragEnterEvent(self, event: QDragEnterEvent):
|
||||
if event.mimeData().hasUrls():
|
||||
for url in event.mimeData().urls():
|
||||
if url.toLocalFile().lower().endswith('.pdf'):
|
||||
event.acceptProposedAction()
|
||||
return
|
||||
|
||||
def dropEvent(self, event: QDropEvent):
|
||||
for url in event.mimeData().urls():
|
||||
path = url.toLocalFile()
|
||||
if path.lower().endswith('.pdf'):
|
||||
self._open_pdf(path)
|
||||
return
|
||||
|
||||
# ── Menu ──
|
||||
|
||||
def _setup_menu(self):
|
||||
menubar = self.menuBar()
|
||||
|
||||
# Menu Fichier
|
||||
# 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)
|
||||
|
||||
self._add_action(file_menu, "Ouvrir PDF...", "Ctrl+O", self._open_file)
|
||||
self._add_action(file_menu, "Enregistrer le resultat...", "Ctrl+S", self._save_result)
|
||||
file_menu.addSeparator()
|
||||
self._add_action(file_menu, "Fusionner des PDF...", "Ctrl+M", self._do_merge)
|
||||
file_menu.addSeparator()
|
||||
self._add_action(file_menu, "Quitter", "Ctrl+Q", self.close)
|
||||
|
||||
quit_act = QAction("Quitter", self)
|
||||
quit_act.setShortcut("Ctrl+Q")
|
||||
quit_act.triggered.connect(self.close)
|
||||
file_menu.addAction(quit_act)
|
||||
# Imposition
|
||||
impose_menu = menubar.addMenu("Imposition")
|
||||
self._add_action(impose_menu, "Creer un livret", None, self._do_booklet)
|
||||
self._add_action(impose_menu, "N pages par feuille", None, self._do_nup)
|
||||
self._add_action(impose_menu, "Repetition (Step && Repeat)", None, self._do_step_repeat)
|
||||
self._add_action(impose_menu, "Imposition manuelle", None, self._do_manual_impose)
|
||||
self._add_action(impose_menu, "Joindre deux pages", None, self._do_join_pages)
|
||||
|
||||
# Menu Aide
|
||||
# Pages
|
||||
pages_menu = menubar.addMenu("Pages")
|
||||
self._add_action(pages_menu, "Inserer des pages blanches", None, self._do_insert_blank)
|
||||
|
||||
# Annotations
|
||||
annot_menu = menubar.addMenu("Annotations")
|
||||
self._add_action(annot_menu, "Numerotation de pages", None, self._do_page_numbers)
|
||||
self._add_action(annot_menu, "Masquage (ruban blanc)", None, self._do_masking)
|
||||
annot_menu.addSeparator()
|
||||
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)
|
||||
|
||||
# Aide
|
||||
help_menu = menubar.addMenu("Aide")
|
||||
about_act = QAction("A propos...", self)
|
||||
about_act.triggered.connect(self._show_about)
|
||||
help_menu.addAction(about_act)
|
||||
self._add_action(help_menu, "A propos de CopyDev Imposing Tool", None, self._show_about)
|
||||
|
||||
def _add_action(self, menu, text, shortcut, callback):
|
||||
act = QAction(text, self)
|
||||
if shortcut:
|
||||
act.setShortcut(shortcut)
|
||||
act.triggered.connect(callback)
|
||||
menu.addAction(act)
|
||||
|
||||
# ── Toolbar ──
|
||||
|
||||
def _setup_toolbar(self):
|
||||
tb = QToolBar()
|
||||
tb.setMovable(False)
|
||||
self.addToolBar(tb)
|
||||
|
||||
tb.addAction("Ouvrir", self._open_file)
|
||||
tb.addAction("Enregistrer", self._save_result)
|
||||
tb.addSeparator()
|
||||
tb.addAction("Zoom +", lambda: self._viewer.zoom_in())
|
||||
tb.addAction("Zoom -", lambda: self._viewer.zoom_out())
|
||||
tb.addAction("Ajuster", lambda: self._viewer.zoom_fit())
|
||||
tb.addSeparator()
|
||||
self._page_label = QLabel(" Aucun fichier ")
|
||||
self._page_label.setStyleSheet("color: #aaa; padding: 0 10px;")
|
||||
tb.addWidget(self._page_label)
|
||||
|
||||
# ── UI principale ──
|
||||
|
||||
def _setup_ui(self):
|
||||
central = QWidget()
|
||||
self.setCentralWidget(central)
|
||||
main_layout = QHBoxLayout(central)
|
||||
layout = QVBoxLayout(central)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.setSpacing(0)
|
||||
|
||||
# === Panneau gauche : boutons ===
|
||||
left_panel = QWidget()
|
||||
left_panel.setFixedWidth(200)
|
||||
left_layout = QVBoxLayout(left_panel)
|
||||
left_layout.setContentsMargins(5, 5, 5, 5)
|
||||
# Splitter : vignettes | viewer
|
||||
splitter = QSplitter(Qt.Orientation.Horizontal)
|
||||
|
||||
# Ouvrir
|
||||
btn_open = QPushButton("Ouvrir PDF...")
|
||||
btn_open.clicked.connect(self._open_file)
|
||||
left_layout.addWidget(btn_open)
|
||||
# Panneau vignettes
|
||||
self._page_panel = PagePanel()
|
||||
self._page_panel.page_selected.connect(self._on_thumbnail_click)
|
||||
self._page_panel.page_delete_requested.connect(self._on_page_delete)
|
||||
self._page_panel.page_insert_requested.connect(self._on_page_insert)
|
||||
self._page_panel.pages_reordered.connect(self._on_pages_reordered)
|
||||
splitter.addWidget(self._page_panel)
|
||||
|
||||
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 ===
|
||||
# Viewer PDF
|
||||
self._viewer = PdfViewer()
|
||||
self._viewer.page_changed.connect(self._on_page_changed)
|
||||
main_layout.addWidget(self._viewer, 1)
|
||||
splitter.addWidget(self._viewer)
|
||||
|
||||
splitter.setSizes([160, 900])
|
||||
splitter.setStretchFactor(0, 0)
|
||||
splitter.setStretchFactor(1, 1)
|
||||
|
||||
layout.addWidget(splitter)
|
||||
|
||||
# Barre de progression
|
||||
self._progress = QProgressBar()
|
||||
self._progress.setFixedHeight(3)
|
||||
self._progress.setTextVisible(False)
|
||||
self._progress.setStyleSheet("""
|
||||
QProgressBar { background: #0f0f23; border: none; }
|
||||
QProgressBar::chunk { background: #0066cc; }
|
||||
""")
|
||||
self._progress.hide()
|
||||
layout.addWidget(self._progress)
|
||||
|
||||
# ── Statusbar ──
|
||||
|
||||
def _setup_statusbar(self):
|
||||
self._statusbar = QStatusBar()
|
||||
self.setStatusBar(self._statusbar)
|
||||
self._status_label = QLabel("Pret")
|
||||
self._status_label = QLabel("Pret — Glissez un PDF ou utilisez Fichier > Ouvrir")
|
||||
self._statusbar.addWidget(self._status_label, 1)
|
||||
self._file_label = QLabel("")
|
||||
self._statusbar.addPermanentWidget(self._file_label)
|
||||
brand = QLabel(" CopyDev — contact@copydev.fr ")
|
||||
brand.setStyleSheet("color: #64748b; font-size: 10px;")
|
||||
brand.setStyleSheet("color: #555; font-size: 10px;")
|
||||
self._statusbar.addPermanentWidget(brand)
|
||||
|
||||
# ── Callbacks ──
|
||||
|
||||
def _on_page_changed(self, page, total):
|
||||
pass
|
||||
self._page_label.setText(f" Page {page + 1} / {total} | Zoom {int(self._viewer.zoom_level * 100)}% ")
|
||||
self._page_panel.select_page(page)
|
||||
|
||||
def _on_thumbnail_click(self, index):
|
||||
self._viewer.go_to_page(index)
|
||||
|
||||
def _on_page_delete(self, index):
|
||||
if not self._current_file:
|
||||
return
|
||||
from pypdf import PdfReader, PdfWriter
|
||||
reader = PdfReader(self._current_file)
|
||||
if len(reader.pages) <= 1:
|
||||
QMessageBox.warning(self, "Erreur", "Impossible de supprimer la derniere page.")
|
||||
return
|
||||
writer = PdfWriter()
|
||||
for i, page in enumerate(reader.pages):
|
||||
if i != index:
|
||||
writer.add_page(page)
|
||||
output = self._make_temp()
|
||||
with open(output, "wb") as f:
|
||||
writer.write(f)
|
||||
self._show_result(output)
|
||||
|
||||
def _on_page_insert(self, after_index):
|
||||
if not self._current_file:
|
||||
return
|
||||
from pypdf import PdfReader, PdfWriter
|
||||
from app.engine.pdf_utils import get_page_size
|
||||
reader = PdfReader(self._current_file)
|
||||
writer = PdfWriter()
|
||||
ref_page = reader.pages[min(after_index, len(reader.pages) - 1)]
|
||||
w, h = get_page_size(ref_page)
|
||||
for i, page in enumerate(reader.pages):
|
||||
writer.add_page(page)
|
||||
if i == after_index:
|
||||
writer.add_blank_page(w, h)
|
||||
output = self._make_temp()
|
||||
with open(output, "wb") as f:
|
||||
writer.write(f)
|
||||
self._show_result(output)
|
||||
|
||||
def _on_pages_reordered(self, indices):
|
||||
if not self._current_file:
|
||||
return
|
||||
from pypdf import PdfReader, PdfWriter
|
||||
reader = PdfReader(self._current_file)
|
||||
writer = PdfWriter()
|
||||
for idx in indices:
|
||||
if 0 <= idx < len(reader.pages):
|
||||
writer.add_page(reader.pages[idx])
|
||||
output = self._make_temp()
|
||||
with open(output, "wb") as f:
|
||||
writer.write(f)
|
||||
self._current_file = output
|
||||
self._result_file = output
|
||||
|
||||
# ── Fichiers ──
|
||||
|
||||
def _open_file(self):
|
||||
path, _ = QFileDialog.getOpenFileName(
|
||||
self, "Ouvrir un fichier PDF", "", "PDF (*.pdf)"
|
||||
)
|
||||
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)}")
|
||||
self._open_pdf(path)
|
||||
|
||||
def _open_pdf(self, path):
|
||||
self._current_file = path
|
||||
self._result_file = None
|
||||
self._viewer.load_pdf(path)
|
||||
self._page_panel.load_thumbnails(self._viewer)
|
||||
self._file_label.setText(os.path.basename(path))
|
||||
self._status_label.setText(f"Fichier charge : {os.path.basename(path)}")
|
||||
# Ajuster apres affichage
|
||||
QTimer.singleShot(100, self._viewer.zoom_fit)
|
||||
|
||||
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)"
|
||||
)
|
||||
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)}")
|
||||
|
||||
@@ -216,22 +306,40 @@ class MainWindow(QMainWindow):
|
||||
os.close(fd)
|
||||
return path
|
||||
|
||||
def _show_progress(self):
|
||||
self._progress.setRange(0, 0)
|
||||
self._progress.show()
|
||||
self._status_label.setText("Traitement en cours...")
|
||||
QApplication.processEvents()
|
||||
|
||||
def _hide_progress(self):
|
||||
self._progress.hide()
|
||||
self._progress.setRange(0, 1)
|
||||
|
||||
def _show_result(self, output_path):
|
||||
self._result_file = output_path
|
||||
self._current_file = output_path
|
||||
self._viewer.load_pdf(output_path)
|
||||
self._page_panel.load_thumbnails(self._viewer)
|
||||
self._hide_progress()
|
||||
self._status_label.setText("Traitement termine")
|
||||
QTimer.singleShot(100, self._viewer.zoom_fit)
|
||||
|
||||
# ── Actions imposition ──
|
||||
|
||||
def _do_booklet(self):
|
||||
if not self._require_file():
|
||||
return
|
||||
dlg = BookletDialog(self)
|
||||
if dlg.exec():
|
||||
self._show_progress()
|
||||
settings = dlg.get_settings()
|
||||
output = self._make_temp()
|
||||
try:
|
||||
create_booklet(self._current_file, output, **settings)
|
||||
self._show_result(output)
|
||||
except Exception as e:
|
||||
self._hide_progress()
|
||||
QMessageBox.critical(self, "Erreur", str(e))
|
||||
|
||||
def _do_nup(self):
|
||||
@@ -239,12 +347,14 @@ class MainWindow(QMainWindow):
|
||||
return
|
||||
dlg = NupDialog(self)
|
||||
if dlg.exec():
|
||||
self._show_progress()
|
||||
settings = dlg.get_settings()
|
||||
output = self._make_temp()
|
||||
try:
|
||||
nup_pages(self._current_file, output, **settings)
|
||||
self._show_result(output)
|
||||
except Exception as e:
|
||||
self._hide_progress()
|
||||
QMessageBox.critical(self, "Erreur", str(e))
|
||||
|
||||
def _do_step_repeat(self):
|
||||
@@ -252,12 +362,14 @@ class MainWindow(QMainWindow):
|
||||
return
|
||||
dlg = StepRepeatDialog(self)
|
||||
if dlg.exec():
|
||||
self._show_progress()
|
||||
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:
|
||||
self._hide_progress()
|
||||
QMessageBox.critical(self, "Erreur", str(e))
|
||||
|
||||
def _do_manual_impose(self):
|
||||
@@ -267,12 +379,14 @@ class MainWindow(QMainWindow):
|
||||
reader = PdfReader(self._current_file)
|
||||
dlg = ManualImposeDialog(total_pages=len(reader.pages), parent=self)
|
||||
if dlg.exec():
|
||||
self._show_progress()
|
||||
settings = dlg.get_settings()
|
||||
output = self._make_temp()
|
||||
try:
|
||||
manual_impose(self._current_file, output, **settings)
|
||||
self._show_result(output)
|
||||
except Exception as e:
|
||||
self._hide_progress()
|
||||
QMessageBox.critical(self, "Erreur", str(e))
|
||||
|
||||
def _do_join_pages(self):
|
||||
@@ -280,12 +394,14 @@ class MainWindow(QMainWindow):
|
||||
return
|
||||
dlg = JoinPagesDialog(self)
|
||||
if dlg.exec():
|
||||
self._show_progress()
|
||||
settings = dlg.get_settings()
|
||||
output = self._make_temp()
|
||||
try:
|
||||
join_pages(self._current_file, output, **settings)
|
||||
self._show_result(output)
|
||||
except Exception as e:
|
||||
self._hide_progress()
|
||||
QMessageBox.critical(self, "Erreur", str(e))
|
||||
|
||||
def _do_insert_blank(self):
|
||||
@@ -295,12 +411,14 @@ class MainWindow(QMainWindow):
|
||||
reader = PdfReader(self._current_file)
|
||||
dlg = InsertBlankDialog(total_pages=len(reader.pages), parent=self)
|
||||
if dlg.exec():
|
||||
self._show_progress()
|
||||
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:
|
||||
self._hide_progress()
|
||||
QMessageBox.critical(self, "Erreur", str(e))
|
||||
|
||||
def _do_page_numbers(self):
|
||||
@@ -308,12 +426,14 @@ class MainWindow(QMainWindow):
|
||||
return
|
||||
dlg = PageNumbersDialog(self)
|
||||
if dlg.exec():
|
||||
self._show_progress()
|
||||
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:
|
||||
self._hide_progress()
|
||||
QMessageBox.critical(self, "Erreur", str(e))
|
||||
|
||||
def _do_masking(self):
|
||||
@@ -321,12 +441,14 @@ class MainWindow(QMainWindow):
|
||||
return
|
||||
dlg = MaskingDialog(self)
|
||||
if dlg.exec():
|
||||
self._show_progress()
|
||||
settings = dlg.get_settings()
|
||||
output = self._make_temp()
|
||||
try:
|
||||
apply_masking(self._current_file, output, **settings)
|
||||
self._show_result(output)
|
||||
except Exception as e:
|
||||
self._hide_progress()
|
||||
QMessageBox.critical(self, "Erreur", str(e))
|
||||
|
||||
def _do_bleeds(self):
|
||||
@@ -334,30 +456,75 @@ class MainWindow(QMainWindow):
|
||||
return
|
||||
dlg = BleedsDialog(self)
|
||||
if dlg.exec():
|
||||
self._show_progress()
|
||||
settings = dlg.get_settings()
|
||||
output = self._make_temp()
|
||||
try:
|
||||
define_bleeds(self._current_file, output, **settings)
|
||||
self._show_result(output)
|
||||
except Exception as e:
|
||||
self._hide_progress()
|
||||
QMessageBox.critical(self, "Erreur", str(e))
|
||||
|
||||
def _do_remove_bleeds(self):
|
||||
if not self._require_file():
|
||||
return
|
||||
from pypdf import PdfReader, PdfWriter
|
||||
self._show_progress()
|
||||
try:
|
||||
reader = PdfReader(self._current_file)
|
||||
writer = PdfWriter()
|
||||
for page in reader.pages:
|
||||
if hasattr(page, 'trimbox'):
|
||||
page.trimbox = page.mediabox
|
||||
if hasattr(page, 'bleedbox'):
|
||||
page.bleedbox = page.mediabox
|
||||
writer.add_page(page)
|
||||
output = self._make_temp()
|
||||
with open(output, "wb") as f:
|
||||
writer.write(f)
|
||||
self._show_result(output)
|
||||
except Exception as e:
|
||||
self._hide_progress()
|
||||
QMessageBox.critical(self, "Erreur", str(e))
|
||||
|
||||
def _do_merge(self):
|
||||
paths, _ = QFileDialog.getOpenFileNames(self, "Selectionner les PDF a fusionner", "", "PDF (*.pdf)")
|
||||
if not paths or len(paths) < 2:
|
||||
if paths and len(paths) == 1:
|
||||
QMessageBox.information(self, "Fusion", "Selectionnez au moins 2 fichiers PDF.")
|
||||
return
|
||||
from pypdf import PdfWriter
|
||||
self._show_progress()
|
||||
try:
|
||||
writer = PdfWriter()
|
||||
for path in paths:
|
||||
writer.append(path)
|
||||
output = self._make_temp()
|
||||
with open(output, "wb") as f:
|
||||
writer.write(f)
|
||||
self._show_result(output)
|
||||
self._status_label.setText(f"Fusion de {len(paths)} fichiers terminee")
|
||||
except Exception as e:
|
||||
self._hide_progress()
|
||||
QMessageBox.critical(self, "Erreur", str(e))
|
||||
|
||||
# ── A propos ──
|
||||
|
||||
def _show_about(self):
|
||||
from PyQt6.QtWidgets import QDialog, QVBoxLayout, QLabel, QDialogButtonBox
|
||||
from PyQt6.QtGui import QPixmap, QFont
|
||||
from PyQt6.QtCore import Qt
|
||||
|
||||
dlg = QDialog(self)
|
||||
dlg.setWindowTitle("A propos — CopyDev Imposing Tool")
|
||||
dlg.setFixedSize(520, 520)
|
||||
dlg.setFixedSize(520, 480)
|
||||
dlg.setStyleSheet("QDialog { background-color: #0f172a; }")
|
||||
layout = QVBoxLayout(dlg)
|
||||
layout.setContentsMargins(40, 30, 40, 25)
|
||||
|
||||
# Logo
|
||||
logo_label = QLabel()
|
||||
import sys as _sys
|
||||
_base = _sys._MEIPASS if getattr(_sys, 'frozen', False) else os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
_base = sys._MEIPASS if getattr(sys, 'frozen', False) else os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
logo_path = os.path.join(_base, "resources", "logo.png")
|
||||
if os.path.exists(logo_path):
|
||||
pixmap = QPixmap(logo_path).scaled(150, 150, Qt.AspectRatioMode.KeepAspectRatio,
|
||||
@@ -368,14 +535,12 @@ class MainWindow(QMainWindow):
|
||||
|
||||
layout.addSpacing(10)
|
||||
|
||||
# Titre
|
||||
title = QLabel("COPYDEV IMPOSING TOOL")
|
||||
title.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
title.setFont(QFont("Segoe UI", 20, QFont.Weight.Bold))
|
||||
title.setStyleSheet("color: white;")
|
||||
layout.addWidget(title)
|
||||
|
||||
# Version
|
||||
from app.updater import VERSION
|
||||
version = QLabel(f"Version {VERSION}")
|
||||
version.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
@@ -385,7 +550,6 @@ class MainWindow(QMainWindow):
|
||||
|
||||
layout.addSpacing(15)
|
||||
|
||||
# Description
|
||||
desc = QLabel(
|
||||
"Outil professionnel d'imposition PDF\n"
|
||||
"Livrets, n-up, repetition, imposition manuelle,\n"
|
||||
@@ -399,7 +563,6 @@ class MainWindow(QMainWindow):
|
||||
|
||||
layout.addSpacing(20)
|
||||
|
||||
# Separateur
|
||||
sep = QLabel()
|
||||
sep.setFixedHeight(1)
|
||||
sep.setStyleSheet("background-color: #1e293b;")
|
||||
@@ -407,7 +570,6 @@ class MainWindow(QMainWindow):
|
||||
|
||||
layout.addSpacing(15)
|
||||
|
||||
# Coordonnees
|
||||
contact = QLabel(
|
||||
'<p style="text-align:center; line-height: 1.6;">'
|
||||
'<span style="font-size:14px; font-weight:bold; color:white;">CopyDev</span><br>'
|
||||
@@ -415,9 +577,7 @@ class MainWindow(QMainWindow):
|
||||
'<span style="color:#94a3b8;">Email : </span>'
|
||||
'<a href="mailto:contact@copydev.fr" style="color:#38bdf8;">contact@copydev.fr</a><br>'
|
||||
'<span style="color:#94a3b8;">Web : </span>'
|
||||
'<a href="https://copydev.fr" style="color:#38bdf8;">www.copydev.fr</a><br>'
|
||||
'<span style="color:#94a3b8;">Tel : </span>'
|
||||
'<span style="color:#e2e8f0;">06 52 07 92 35</span>'
|
||||
'<a href="https://copydev.fr" style="color:#38bdf8;">www.copydev.fr</a>'
|
||||
'</p>'
|
||||
)
|
||||
contact.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
@@ -426,14 +586,12 @@ class MainWindow(QMainWindow):
|
||||
|
||||
layout.addStretch()
|
||||
|
||||
# Copyright
|
||||
copy_label = QLabel("2026 CopyDev — Tous droits reserves\nLogiciel sous licence — Reproduction interdite")
|
||||
copy_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
copy_label.setFont(QFont("Segoe UI", 8))
|
||||
copy_label.setStyleSheet("color: #334155;")
|
||||
layout.addWidget(copy_label)
|
||||
|
||||
# Bouton Fermer
|
||||
buttons = QDialogButtonBox(QDialogButtonBox.StandardButton.Close)
|
||||
buttons.setStyleSheet("QPushButton { background: #1e293b; color: white; border: 1px solid #334155; padding: 6px 20px; border-radius: 4px; } QPushButton:hover { background: #334155; }")
|
||||
buttons.rejected.connect(dlg.close)
|
||||
|
||||
109
app/page_panel.py
Normal file
109
app/page_panel.py
Normal file
@@ -0,0 +1,109 @@
|
||||
"""Panneau lateral de vignettes des pages — V2."""
|
||||
|
||||
from PyQt6.QtWidgets import (QWidget, QVBoxLayout, QListWidget, QListWidgetItem,
|
||||
QAbstractItemView, QMenu, QSizePolicy)
|
||||
from PyQt6.QtGui import QIcon, QPixmap
|
||||
from PyQt6.QtCore import Qt, pyqtSignal, QSize
|
||||
|
||||
|
||||
class PagePanel(QWidget):
|
||||
"""Panneau de vignettes avec drag & drop, suppression, insertion."""
|
||||
|
||||
page_selected = pyqtSignal(int) # page index cliquee
|
||||
pages_reordered = pyqtSignal(list) # nouvelle liste d'indices
|
||||
page_delete_requested = pyqtSignal(int) # demande suppression
|
||||
page_insert_requested = pyqtSignal(int) # demande insertion page blanche apres
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.setMinimumWidth(100)
|
||||
self.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Expanding)
|
||||
self._setup_ui()
|
||||
|
||||
def _setup_ui(self):
|
||||
layout = QVBoxLayout(self)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
self._list = QListWidget()
|
||||
self._list.setIconSize(QSize(120, 170))
|
||||
self._list.setSpacing(4)
|
||||
self._list.setStyleSheet("""
|
||||
QListWidget {
|
||||
background: #252525;
|
||||
border: none;
|
||||
padding: 4px;
|
||||
}
|
||||
QListWidget::item {
|
||||
background: #333;
|
||||
border: 1px solid #444;
|
||||
border-radius: 4px;
|
||||
padding: 4px;
|
||||
color: #ccc;
|
||||
font-size: 10px;
|
||||
}
|
||||
QListWidget::item:selected {
|
||||
background: #0066cc;
|
||||
border: 1px solid #0088ff;
|
||||
color: white;
|
||||
}
|
||||
""")
|
||||
|
||||
self._list.setDragDropMode(QAbstractItemView.DragDropMode.InternalMove)
|
||||
self._list.setDefaultDropAction(Qt.DropAction.MoveAction)
|
||||
self._list.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
|
||||
self._list.customContextMenuRequested.connect(self._context_menu)
|
||||
self._list.currentRowChanged.connect(self._on_row_changed)
|
||||
self._list.model().rowsMoved.connect(self._on_rows_moved)
|
||||
|
||||
layout.addWidget(self._list)
|
||||
|
||||
def load_thumbnails(self, viewer):
|
||||
"""Charger les miniatures depuis le viewer."""
|
||||
self._list.clear()
|
||||
for i in range(viewer.total_pages):
|
||||
thumb = viewer.get_page_thumbnail(i, 120)
|
||||
item = QListWidgetItem()
|
||||
item.setText(f" Page {i + 1}")
|
||||
item.setData(Qt.ItemDataRole.UserRole, i)
|
||||
if thumb:
|
||||
item.setIcon(QIcon(thumb))
|
||||
self._list.addItem(item)
|
||||
|
||||
def select_page(self, index):
|
||||
"""Selectionner une page sans emettre le signal."""
|
||||
self._list.blockSignals(True)
|
||||
self._list.setCurrentRow(index)
|
||||
self._list.blockSignals(False)
|
||||
|
||||
def _on_row_changed(self, row):
|
||||
if row >= 0:
|
||||
self.page_selected.emit(row)
|
||||
|
||||
def _on_rows_moved(self):
|
||||
indices = []
|
||||
for i in range(self._list.count()):
|
||||
item = self._list.item(i)
|
||||
original_index = item.data(Qt.ItemDataRole.UserRole)
|
||||
indices.append(original_index)
|
||||
self.pages_reordered.emit(indices)
|
||||
|
||||
def _context_menu(self, pos):
|
||||
item = self._list.itemAt(pos)
|
||||
if not item:
|
||||
return
|
||||
row = self._list.row(item)
|
||||
|
||||
menu = QMenu(self)
|
||||
menu.setStyleSheet("""
|
||||
QMenu { background: #333; color: #eee; border: 1px solid #555; }
|
||||
QMenu::item:selected { background: #0066cc; }
|
||||
""")
|
||||
|
||||
act_insert = menu.addAction("Inserer une page blanche apres")
|
||||
act_delete = menu.addAction("Supprimer cette page")
|
||||
|
||||
action = menu.exec(self._list.mapToGlobal(pos))
|
||||
if action == act_delete:
|
||||
self.page_delete_requested.emit(row)
|
||||
elif action == act_insert:
|
||||
self.page_insert_requested.emit(row)
|
||||
@@ -1,20 +1,21 @@
|
||||
"""Widget de visualisation PDF avec pypdfium2."""
|
||||
"""Widget de visualisation PDF avec pypdfium2 — V2."""
|
||||
|
||||
from PyQt6.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QLabel,
|
||||
QPushButton, QSpinBox, QScrollArea, QSizePolicy)
|
||||
from PyQt6.QtGui import QImage, QPixmap
|
||||
from PyQt6.QtCore import Qt, pyqtSignal
|
||||
QScrollArea, QSizePolicy, QApplication)
|
||||
from PyQt6.QtGui import QImage, QPixmap, QWheelEvent
|
||||
from PyQt6.QtCore import Qt, pyqtSignal, QTimer
|
||||
import pypdfium2 as pdfium
|
||||
|
||||
|
||||
class PdfViewer(QWidget):
|
||||
"""Widget d'apercu PDF avec navigation et zoom."""
|
||||
"""Widget d'apercu PDF avec zoom molette et navigation fluide."""
|
||||
|
||||
page_changed = pyqtSignal(int, int) # page_num, total_pages
|
||||
page_changed = pyqtSignal(int, int) # page_num (0-based), total_pages
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self._pdf = None
|
||||
self._pdf_path = None
|
||||
self._current_page = 0
|
||||
self._total_pages = 0
|
||||
self._zoom = 1.0
|
||||
@@ -23,73 +24,48 @@ class PdfViewer(QWidget):
|
||||
def _setup_ui(self):
|
||||
layout = QVBoxLayout(self)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.setSpacing(0)
|
||||
|
||||
# Zone d'affichage avec scroll
|
||||
self._scroll = QScrollArea()
|
||||
self._scroll.setWidgetResizable(True)
|
||||
self._scroll.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
self._scroll.setStyleSheet("QScrollArea { background: #1e1e1e; border: none; }")
|
||||
|
||||
self._image_label = QLabel()
|
||||
self._image_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
self._image_label.setSizePolicy(QSizePolicy.Policy.Ignored, QSizePolicy.Policy.Ignored)
|
||||
self._image_label.setStyleSheet("background: #1e1e1e;")
|
||||
self._scroll.setWidget(self._image_label)
|
||||
|
||||
# Intercepter la molette sur le scroll area
|
||||
self._scroll.wheelEvent = self._on_wheel
|
||||
|
||||
layout.addWidget(self._scroll, 1)
|
||||
|
||||
# Barre de navigation
|
||||
nav = QHBoxLayout()
|
||||
|
||||
self._btn_prev = QPushButton("<")
|
||||
self._btn_prev.setFixedWidth(40)
|
||||
self._btn_prev.clicked.connect(self._prev_page)
|
||||
nav.addWidget(self._btn_prev)
|
||||
|
||||
self._page_spin = QSpinBox()
|
||||
self._page_spin.setMinimum(1)
|
||||
self._page_spin.setMaximum(1)
|
||||
self._page_spin.valueChanged.connect(self._go_to_page)
|
||||
nav.addWidget(self._page_spin)
|
||||
|
||||
self._label_total = QLabel("/ 0")
|
||||
nav.addWidget(self._label_total)
|
||||
|
||||
self._btn_next = QPushButton(">")
|
||||
self._btn_next.setFixedWidth(40)
|
||||
self._btn_next.clicked.connect(self._next_page)
|
||||
nav.addWidget(self._btn_next)
|
||||
|
||||
nav.addSpacing(20)
|
||||
|
||||
self._btn_zoom_out = QPushButton("-")
|
||||
self._btn_zoom_out.setFixedWidth(30)
|
||||
self._btn_zoom_out.clicked.connect(self._zoom_out)
|
||||
nav.addWidget(self._btn_zoom_out)
|
||||
|
||||
self._label_zoom = QLabel("100%")
|
||||
self._label_zoom.setFixedWidth(45)
|
||||
self._label_zoom.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
nav.addWidget(self._label_zoom)
|
||||
|
||||
self._btn_zoom_in = QPushButton("+")
|
||||
self._btn_zoom_in.setFixedWidth(30)
|
||||
self._btn_zoom_in.clicked.connect(self._zoom_in)
|
||||
nav.addWidget(self._btn_zoom_in)
|
||||
|
||||
self._btn_fit = QPushButton("Ajuster")
|
||||
self._btn_fit.clicked.connect(self._zoom_fit)
|
||||
nav.addWidget(self._btn_fit)
|
||||
|
||||
nav.addStretch()
|
||||
layout.addLayout(nav)
|
||||
def _on_wheel(self, event: QWheelEvent):
|
||||
"""Molette = pages, Ctrl+Molette = zoom."""
|
||||
if event.modifiers() & Qt.KeyboardModifier.ControlModifier:
|
||||
delta = event.angleDelta().y()
|
||||
if delta > 0:
|
||||
self._zoom = min(self._zoom * 1.15, 5.0)
|
||||
else:
|
||||
self._zoom = max(self._zoom / 1.15, 0.2)
|
||||
self._render_page()
|
||||
else:
|
||||
delta = event.angleDelta().y()
|
||||
if delta < 0:
|
||||
self.next_page()
|
||||
elif delta > 0:
|
||||
self.prev_page()
|
||||
|
||||
def load_pdf(self, path):
|
||||
"""Charger un fichier PDF."""
|
||||
try:
|
||||
self._pdf_path = path
|
||||
self._pdf = pdfium.PdfDocument(path)
|
||||
self._total_pages = len(self._pdf)
|
||||
self._current_page = 0
|
||||
self._page_spin.setMaximum(self._total_pages)
|
||||
self._page_spin.setValue(1)
|
||||
self._label_total.setText(f"/ {self._total_pages}")
|
||||
self._zoom_fit()
|
||||
self._render_page()
|
||||
except Exception as e:
|
||||
self._image_label.setText(f"Erreur: {e}")
|
||||
@@ -100,9 +76,7 @@ class PdfViewer(QWidget):
|
||||
self._pdf = pdfium.PdfDocument(data)
|
||||
self._total_pages = len(self._pdf)
|
||||
self._current_page = 0
|
||||
self._page_spin.setMaximum(self._total_pages)
|
||||
self._page_spin.setValue(1)
|
||||
self._label_total.setText(f"/ {self._total_pages}")
|
||||
self._zoom_fit()
|
||||
self._render_page()
|
||||
except Exception as e:
|
||||
self._image_label.setText(f"Erreur: {e}")
|
||||
@@ -113,44 +87,17 @@ class PdfViewer(QWidget):
|
||||
return
|
||||
|
||||
page = self._pdf[self._current_page]
|
||||
# Rendu a 150 DPI * zoom
|
||||
scale = 150 / 72 * self._zoom
|
||||
bitmap = page.render(scale=scale)
|
||||
pil_image = bitmap.to_pil()
|
||||
|
||||
# Convertir PIL -> QImage -> QPixmap
|
||||
data = pil_image.tobytes("raw", "RGBA")
|
||||
qimage = QImage(data, pil_image.width, pil_image.height, QImage.Format.Format_RGBA8888)
|
||||
pixmap = QPixmap.fromImage(qimage)
|
||||
|
||||
self._image_label.setPixmap(pixmap)
|
||||
self._image_label.adjustSize()
|
||||
self._label_zoom.setText(f"{int(self._zoom * 100)}%")
|
||||
self.page_changed.emit(self._current_page + 1, self._total_pages)
|
||||
|
||||
def _prev_page(self):
|
||||
if self._current_page > 0:
|
||||
self._current_page -= 1
|
||||
self._page_spin.setValue(self._current_page + 1)
|
||||
|
||||
def _next_page(self):
|
||||
if self._current_page < self._total_pages - 1:
|
||||
self._current_page += 1
|
||||
self._page_spin.setValue(self._current_page + 1)
|
||||
|
||||
def _go_to_page(self, value):
|
||||
page = value - 1
|
||||
if 0 <= page < self._total_pages and page != self._current_page:
|
||||
self._current_page = page
|
||||
self._render_page()
|
||||
|
||||
def _zoom_in(self):
|
||||
self._zoom = min(self._zoom * 1.25, 5.0)
|
||||
self._render_page()
|
||||
|
||||
def _zoom_out(self):
|
||||
self._zoom = max(self._zoom / 1.25, 0.25)
|
||||
self._render_page()
|
||||
self.page_changed.emit(self._current_page, self._total_pages)
|
||||
|
||||
def _zoom_fit(self):
|
||||
"""Ajuster le zoom pour que la page tienne dans la zone visible."""
|
||||
@@ -160,15 +107,56 @@ class PdfViewer(QWidget):
|
||||
pw, ph = page.get_size()
|
||||
vw = self._scroll.viewport().width() - 20
|
||||
vh = self._scroll.viewport().height() - 20
|
||||
if vw <= 0 or vh <= 0:
|
||||
self._zoom = 1.0
|
||||
return
|
||||
scale_x = vw / pw
|
||||
scale_y = vh / ph
|
||||
self._zoom = min(scale_x, scale_y) * (72 / 150)
|
||||
|
||||
def prev_page(self):
|
||||
if self._current_page > 0:
|
||||
self._current_page -= 1
|
||||
self._render_page()
|
||||
|
||||
def next_page(self):
|
||||
if self._current_page < self._total_pages - 1:
|
||||
self._current_page += 1
|
||||
self._render_page()
|
||||
|
||||
def go_to_page(self, page_index):
|
||||
if 0 <= page_index < self._total_pages and page_index != self._current_page:
|
||||
self._current_page = page_index
|
||||
self._render_page()
|
||||
|
||||
def zoom_in(self):
|
||||
self._zoom = min(self._zoom * 1.25, 5.0)
|
||||
self._render_page()
|
||||
|
||||
def zoom_out(self):
|
||||
self._zoom = max(self._zoom / 1.25, 0.2)
|
||||
self._render_page()
|
||||
|
||||
def zoom_fit(self):
|
||||
self._zoom_fit()
|
||||
self._render_page()
|
||||
|
||||
def refresh(self):
|
||||
"""Rafraichir l'affichage."""
|
||||
self._render_page()
|
||||
|
||||
def get_page_thumbnail(self, page_index, width=120):
|
||||
"""Generer une miniature pour le panneau de vignettes."""
|
||||
if not self._pdf or page_index >= self._total_pages:
|
||||
return None
|
||||
page = self._pdf[page_index]
|
||||
pw, ph = page.get_size()
|
||||
scale = width / pw
|
||||
bitmap = page.render(scale=scale)
|
||||
pil_image = bitmap.to_pil()
|
||||
data = pil_image.tobytes("raw", "RGBA")
|
||||
qimage = QImage(data, pil_image.width, pil_image.height, QImage.Format.Format_RGBA8888)
|
||||
return QPixmap.fromImage(qimage)
|
||||
|
||||
@property
|
||||
def current_page(self):
|
||||
return self._current_page
|
||||
@@ -176,3 +164,11 @@ class PdfViewer(QWidget):
|
||||
@property
|
||||
def total_pages(self):
|
||||
return self._total_pages
|
||||
|
||||
@property
|
||||
def zoom_level(self):
|
||||
return self._zoom
|
||||
|
||||
@property
|
||||
def pdf_path(self):
|
||||
return self._pdf_path
|
||||
|
||||
@@ -14,7 +14,7 @@ REPO = "jules/copydev-imposing-tool"
|
||||
BRANCH = "master"
|
||||
|
||||
# Version actuelle (incrementee a chaque release)
|
||||
VERSION = "1.0.0"
|
||||
VERSION = "2.0.0"
|
||||
|
||||
|
||||
def get_remote_version():
|
||||
|
||||
Reference in New Issue
Block a user