- 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>
149 lines
5.6 KiB
Python
149 lines
5.6 KiB
Python
"""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)
|
|
pages_reordered = pyqtSignal(list)
|
|
page_delete_requested = pyqtSignal(int)
|
|
page_insert_requested = pyqtSignal(int)
|
|
page_rotate_requested = pyqtSignal(int, int) # page_index, degrees (90, -90, 180)
|
|
|
|
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.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;
|
|
border: none;
|
|
border-right: 1px solid #d0d0d0;
|
|
padding: 4px;
|
|
}
|
|
QListWidget::item {
|
|
background: #ffffff;
|
|
border: 1px solid #d0d0d0;
|
|
border-radius: 6px;
|
|
padding: 4px;
|
|
color: #555;
|
|
font-size: 10px;
|
|
margin: 2px 0;
|
|
}
|
|
QListWidget::item:hover {
|
|
background: #e0f4ff;
|
|
border-color: #00b4d8;
|
|
}
|
|
QListWidget::item:selected {
|
|
background: #d8f0f8;
|
|
border: 2px solid #00b4d8;
|
|
color: #006fa6;
|
|
}
|
|
""")
|
|
|
|
self._list.setSelectionMode(QAbstractItemView.SelectionMode.ExtendedSelection)
|
|
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 get_selected_indices(self):
|
|
"""Retourne les indices des pages selectionnees."""
|
|
return sorted(set(self._list.row(item) for item in self._list.selectedItems()))
|
|
|
|
def _context_menu(self, pos):
|
|
item = self._list.itemAt(pos)
|
|
if not item:
|
|
return
|
|
row = self._list.row(item)
|
|
selected = self.get_selected_indices()
|
|
if row not in selected:
|
|
selected = [row]
|
|
|
|
count = len(selected)
|
|
label_pages = f"{count} page(s)" if count > 1 else "cette page"
|
|
|
|
menu = QMenu(self)
|
|
menu.setStyleSheet("""
|
|
QMenu { background: #fff; color: #2d2d2d; border: 1px solid #d0d0d0; border-radius: 8px; padding: 4px; }
|
|
QMenu::item { padding: 6px 20px; border-radius: 4px; }
|
|
QMenu::item:selected { background: #e0f4ff; color: #006fa6; }
|
|
""")
|
|
|
|
act_rot_right = menu.addAction(f"Pivoter +90° ({label_pages})")
|
|
act_rot_left = menu.addAction(f"Pivoter -90° ({label_pages})")
|
|
act_rot_180 = menu.addAction(f"Pivoter 180° ({label_pages})")
|
|
menu.addSeparator()
|
|
act_insert = menu.addAction("Inserer une page blanche apres")
|
|
act_delete = menu.addAction(f"Supprimer {label_pages}")
|
|
|
|
action = menu.exec(self._list.mapToGlobal(pos))
|
|
if action == act_delete:
|
|
for idx in reversed(selected):
|
|
self.page_delete_requested.emit(idx)
|
|
elif action == act_insert:
|
|
self.page_insert_requested.emit(row)
|
|
elif action == act_rot_right:
|
|
for idx in selected:
|
|
self.page_rotate_requested.emit(idx, 90)
|
|
elif action == act_rot_left:
|
|
for idx in selected:
|
|
self.page_rotate_requested.emit(idx, -90)
|
|
elif action == act_rot_180:
|
|
for idx in selected:
|
|
self.page_rotate_requested.emit(idx, 180)
|