Duncan Tourolle 8f9f387848
All checks were successful
Python CI / test (push) Successful in 1m31s
Lint / lint (push) Successful in 1m10s
Tests / test (3.11) (push) Successful in 1m42s
Tests / test (3.12) (push) Successful in 1m43s
Tests / test (3.13) (push) Successful in 1m36s
Tests / test (3.14) (push) Successful in 1m14s
Added gallery pane to easily add images on smaller screen devices
2025-12-13 15:30:37 +01:00

266 lines
9.4 KiB
Python

"""
View operations mixin for pyPhotoAlbum
"""
from pyPhotoAlbum.decorators import ribbon_action
class ViewOperationsMixin:
"""Mixin providing view-related operations"""
@ribbon_action(label="Zoom In", tooltip="Zoom in", tab="View", group="Zoom", shortcut="Ctrl++")
def zoom_in(self):
"""Zoom in"""
self.gl_widget.zoom_level *= 1.2
if self.gl_widget.zoom_level > 5.0:
self.gl_widget.zoom_level = 5.0
self.update_view()
self.show_status(f"Zoom: {int(self.gl_widget.zoom_level * 100)}%", 2000)
@ribbon_action(label="Zoom Out", tooltip="Zoom out", tab="View", group="Zoom", shortcut="Ctrl+-")
def zoom_out(self):
"""Zoom out"""
self.gl_widget.zoom_level /= 1.2
if self.gl_widget.zoom_level < 0.1:
self.gl_widget.zoom_level = 0.1
self.update_view()
self.show_status(f"Zoom: {int(self.gl_widget.zoom_level * 100)}%", 2000)
@ribbon_action(label="Fit to Window", tooltip="Fit page to window", tab="View", group="Zoom", shortcut="Ctrl+0")
def zoom_fit(self):
"""Fit page to window"""
if not self.project.pages:
return
current_page = self.project.pages[self.gl_widget.current_page_index]
page_width_mm = current_page.layout.size[0]
page_height_mm = current_page.layout.size[1]
# Convert to pixels
dpi = self.project.working_dpi
page_width_px = page_width_mm * dpi / 25.4
page_height_px = page_height_mm * dpi / 25.4
# Get widget size
widget_width = self.gl_widget.width() - 100 # Margins
widget_height = self.gl_widget.height() - 100
# Calculate zoom to fit
zoom_w = widget_width / page_width_px
zoom_h = widget_height / page_height_px
self.gl_widget.zoom_level = min(zoom_w, zoom_h)
self.gl_widget.zoom_level = max(0.1, min(5.0, self.gl_widget.zoom_level))
self.update_view()
self.show_status(f"Zoom: {int(self.gl_widget.zoom_level * 100)}%", 2000)
@ribbon_action(
label="Grid Snap",
tooltip="Enable/disable snapping to grid (Ctrl+G)",
tab="Insert",
group="Snapping",
shortcut="Ctrl+G",
)
def toggle_grid_snap(self):
"""Toggle grid snapping"""
if not self.project:
return
self.project.snap_to_grid = not self.project.snap_to_grid
status = "enabled" if self.project.snap_to_grid else "disabled"
self.update_view()
self.show_status(f"Grid snapping {status}", 2000)
print(f"Grid snapping {status}")
@ribbon_action(
label="Edge Snap",
tooltip="Enable/disable snapping to page edges (Ctrl+E)",
tab="Insert",
group="Snapping",
shortcut="Ctrl+E",
)
def toggle_edge_snap(self):
"""Toggle edge snapping"""
if not self.project:
return
self.project.snap_to_edges = not self.project.snap_to_edges
status = "enabled" if self.project.snap_to_edges else "disabled"
self.update_view()
self.show_status(f"Edge snapping {status}", 2000)
print(f"Edge snapping {status}")
@ribbon_action(label="Guide Snap", tooltip="Enable/disable snapping to guides", tab="Insert", group="Snapping")
def toggle_guide_snap(self):
"""Toggle guide snapping"""
if not self.project:
return
self.project.snap_to_guides = not self.project.snap_to_guides
status = "enabled" if self.project.snap_to_guides else "disabled"
self.update_view()
self.show_status(f"Guide snapping {status}", 2000)
print(f"Guide snapping {status}")
@ribbon_action(label="Show Grid", tooltip="Toggle visibility of grid lines", tab="Insert", group="Snapping")
def toggle_show_grid(self):
"""Toggle grid visibility"""
if not self.project:
return
self.project.show_grid = not self.project.show_grid
status = "visible" if self.project.show_grid else "hidden"
self.update_view()
self.show_status(f"Grid {status}", 2000)
print(f"Grid {status}")
@ribbon_action(label="Show Guides", tooltip="Toggle visibility of guide lines", tab="Insert", group="Snapping")
def toggle_snap_lines(self):
"""Toggle guide lines visibility"""
if not self.project:
return
self.project.show_snap_lines = not self.project.show_snap_lines
status = "visible" if self.project.show_snap_lines else "hidden"
self.update_view()
self.show_status(f"Guides {status}", 2000)
print(f"Guides {status}")
@ribbon_action(label="Add H Guide", tooltip="Add horizontal guide at page center", tab="View", group="Guides")
def add_horizontal_guide(self):
"""Add a horizontal guide at page center"""
current_page = self.get_current_page()
if not current_page:
return
# Add guide at vertical center (in mm)
center_y = current_page.layout.size[1] / 2.0
current_page.layout.snapping_system.add_guide(center_y, "horizontal")
self.update_view()
self.show_status(f"Added horizontal guide at {center_y:.1f} mm", 2000)
print(f"Added horizontal guide at {center_y:.1f} mm")
@ribbon_action(label="Add V Guide", tooltip="Add vertical guide at page center", tab="View", group="Guides")
def add_vertical_guide(self):
"""Add a vertical guide at page center"""
current_page = self.get_current_page()
if not current_page:
return
# Add guide at horizontal center (in mm)
center_x = current_page.layout.size[0] / 2.0
current_page.layout.snapping_system.add_guide(center_x, "vertical")
self.update_view()
self.show_status(f"Added vertical guide at {center_x:.1f} mm", 2000)
print(f"Added vertical guide at {center_x:.1f} mm")
@ribbon_action(label="Clear Guides", tooltip="Clear all guides from current page", tab="View", group="Guides")
def clear_guides(self):
"""Clear all guides from current page"""
current_page = self.get_current_page()
if not current_page:
return
guide_count = len(current_page.layout.snapping_system.guides)
current_page.layout.snapping_system.clear_guides()
self.update_view()
self.show_status(f"Cleared {guide_count} guides", 2000)
print(f"Cleared {guide_count} guides")
@ribbon_action(
label="Image Browser",
tooltip="Show/hide the image browser panel",
tab="View",
group="Panels",
shortcut="Ctrl+B"
)
def toggle_image_browser(self):
"""Toggle the thumbnail browser visibility"""
if hasattr(self, '_thumbnail_browser'):
if self._thumbnail_browser.isVisible():
self._thumbnail_browser.hide()
self.show_status("Image browser hidden", 2000)
else:
self._thumbnail_browser.show()
self.show_status("Image browser shown", 2000)
@ribbon_action(
label="Grid Settings...", tooltip="Configure grid size and snap threshold", tab="Insert", group="Snapping"
)
def set_grid_size(self):
"""Open dialog to set grid size"""
from PyQt6.QtWidgets import QDialog, QVBoxLayout, QHBoxLayout, QLabel, QDoubleSpinBox, QPushButton
if not self.project:
return
# Create dialog
dialog = QDialog(self)
dialog.setWindowTitle("Grid Settings")
dialog.setMinimumWidth(300)
layout = QVBoxLayout()
# Grid size setting
size_layout = QHBoxLayout()
size_layout.addWidget(QLabel("Grid Size:"))
size_spinbox = QDoubleSpinBox()
size_spinbox.setRange(1.0, 100.0)
size_spinbox.setValue(self.project.grid_size_mm)
size_spinbox.setSuffix(" mm")
size_spinbox.setDecimals(1)
size_spinbox.setSingleStep(1.0)
size_layout.addWidget(size_spinbox)
layout.addLayout(size_layout)
# Snap threshold setting
threshold_layout = QHBoxLayout()
threshold_layout.addWidget(QLabel("Snap Threshold:"))
threshold_spinbox = QDoubleSpinBox()
threshold_spinbox.setRange(0.5, 20.0)
threshold_spinbox.setValue(self.project.snap_threshold_mm)
threshold_spinbox.setSuffix(" mm")
threshold_spinbox.setDecimals(1)
threshold_spinbox.setSingleStep(0.5)
threshold_layout.addWidget(threshold_spinbox)
layout.addLayout(threshold_layout)
# Buttons
button_layout = QHBoxLayout()
cancel_btn = QPushButton("Cancel")
cancel_btn.clicked.connect(dialog.reject)
ok_btn = QPushButton("OK")
ok_btn.clicked.connect(dialog.accept)
ok_btn.setDefault(True)
button_layout.addStretch()
button_layout.addWidget(cancel_btn)
button_layout.addWidget(ok_btn)
layout.addLayout(button_layout)
dialog.setLayout(layout)
# Show dialog and apply if accepted
if dialog.exec() == QDialog.DialogCode.Accepted:
new_grid_size = size_spinbox.value()
new_threshold = threshold_spinbox.value()
self.project.grid_size_mm = new_grid_size
self.project.snap_threshold_mm = new_threshold
self.update_view()
self.show_status(f"Grid size: {new_grid_size:.1f}mm, Threshold: {new_threshold:.1f}mm", 2000)
print(f"Updated grid settings - Size: {new_grid_size:.1f}mm, Threshold: {new_threshold:.1f}mm")