345 lines
11 KiB
Python
345 lines
11 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="Toggle Grid Snap",
|
|
tooltip="Toggle snapping to grid",
|
|
tab="View",
|
|
group="Snapping"
|
|
)
|
|
def toggle_grid_snap(self):
|
|
"""Toggle grid snapping"""
|
|
current_page = self.get_current_page()
|
|
if not current_page:
|
|
return
|
|
|
|
snap_sys = current_page.layout.snapping_system
|
|
snap_sys.snap_to_grid = not snap_sys.snap_to_grid
|
|
|
|
status = "enabled" if snap_sys.snap_to_grid else "disabled"
|
|
self.update_view()
|
|
self.show_status(f"Grid snapping {status}", 2000)
|
|
print(f"Grid snapping {status}")
|
|
|
|
@ribbon_action(
|
|
label="Toggle Edge Snap",
|
|
tooltip="Toggle snapping to page edges",
|
|
tab="View",
|
|
group="Snapping"
|
|
)
|
|
def toggle_edge_snap(self):
|
|
"""Toggle edge snapping"""
|
|
current_page = self.get_current_page()
|
|
if not current_page:
|
|
return
|
|
|
|
snap_sys = current_page.layout.snapping_system
|
|
snap_sys.snap_to_edges = not snap_sys.snap_to_edges
|
|
|
|
status = "enabled" if snap_sys.snap_to_edges else "disabled"
|
|
self.update_view()
|
|
self.show_status(f"Edge snapping {status}", 2000)
|
|
print(f"Edge snapping {status}")
|
|
|
|
@ribbon_action(
|
|
label="Toggle Guide Snap",
|
|
tooltip="Toggle snapping to guides",
|
|
tab="View",
|
|
group="Snapping"
|
|
)
|
|
def toggle_guide_snap(self):
|
|
"""Toggle guide snapping"""
|
|
current_page = self.get_current_page()
|
|
if not current_page:
|
|
return
|
|
|
|
snap_sys = current_page.layout.snapping_system
|
|
snap_sys.snap_to_guides = not snap_sys.snap_to_guides
|
|
|
|
status = "enabled" if snap_sys.snap_to_guides else "disabled"
|
|
self.update_view()
|
|
self.show_status(f"Guide snapping {status}", 2000)
|
|
print(f"Guide snapping {status}")
|
|
|
|
@ribbon_action(
|
|
label="Toggle Snap Lines",
|
|
tooltip="Toggle visibility of snap lines",
|
|
tab="View",
|
|
group="Snapping"
|
|
)
|
|
def toggle_snap_lines(self):
|
|
"""Toggle snap lines visibility"""
|
|
current_page = self.get_current_page()
|
|
if not current_page:
|
|
return
|
|
|
|
current_page.layout.show_snap_lines = not current_page.layout.show_snap_lines
|
|
|
|
status = "visible" if current_page.layout.show_snap_lines else "hidden"
|
|
self.update_view()
|
|
self.show_status(f"Snap lines {status}", 2000)
|
|
print(f"Snap lines {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="Set Grid Size...",
|
|
tooltip="Configure grid spacing for snapping",
|
|
tab="View",
|
|
group="Snapping"
|
|
)
|
|
def set_grid_size(self):
|
|
"""Open dialog to set grid size"""
|
|
from PyQt6.QtWidgets import QDialog, QVBoxLayout, QHBoxLayout, QLabel, QDoubleSpinBox, QPushButton
|
|
|
|
current_page = self.get_current_page()
|
|
if not current_page:
|
|
return
|
|
|
|
snap_sys = current_page.layout.snapping_system
|
|
|
|
# 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(snap_sys.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(snap_sys.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()
|
|
|
|
snap_sys.grid_size_mm = new_grid_size
|
|
snap_sys.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")
|
|
|
|
# ===== Layout Tab Snapping Controls =====
|
|
# These provide easy access to snapping features during layout work
|
|
|
|
@ribbon_action(
|
|
label="Grid Snap",
|
|
tooltip="Enable/disable snapping to grid (Ctrl+G)",
|
|
tab="Layout",
|
|
group="Snapping",
|
|
shortcut="Ctrl+G"
|
|
)
|
|
def layout_toggle_grid_snap(self):
|
|
"""Toggle grid snapping (Layout tab)"""
|
|
self.toggle_grid_snap()
|
|
|
|
@ribbon_action(
|
|
label="Edge Snap",
|
|
tooltip="Enable/disable snapping to page edges (Ctrl+E)",
|
|
tab="Layout",
|
|
group="Snapping",
|
|
shortcut="Ctrl+E"
|
|
)
|
|
def layout_toggle_edge_snap(self):
|
|
"""Toggle edge snapping (Layout tab)"""
|
|
self.toggle_edge_snap()
|
|
|
|
@ribbon_action(
|
|
label="Guide Snap",
|
|
tooltip="Enable/disable snapping to guides",
|
|
tab="Layout",
|
|
group="Snapping"
|
|
)
|
|
def layout_toggle_guide_snap(self):
|
|
"""Toggle guide snapping (Layout tab)"""
|
|
self.toggle_guide_snap()
|
|
|
|
@ribbon_action(
|
|
label="Show Grid",
|
|
tooltip="Toggle visibility of snap lines",
|
|
tab="Layout",
|
|
group="Snapping"
|
|
)
|
|
def layout_toggle_snap_lines(self):
|
|
"""Toggle snap lines visibility (Layout tab)"""
|
|
self.toggle_snap_lines()
|
|
|
|
@ribbon_action(
|
|
label="Grid Settings...",
|
|
tooltip="Configure grid size and snap threshold",
|
|
tab="Layout",
|
|
group="Snapping"
|
|
)
|
|
def layout_set_grid_size(self):
|
|
"""Open grid settings dialog (Layout tab)"""
|
|
self.set_grid_size()
|