From 3e3b604d2f31943e14c6049dca8180daffa62f07 Mon Sep 17 00:00:00 2001 From: Duncan Tourolle Date: Wed, 29 Oct 2025 18:35:52 +0100 Subject: [PATCH] page size set when making a project --- pyPhotoAlbum/mixins/operations/file_ops.py | 142 ++++++++++++++++++++- pyPhotoAlbum/mixins/operations/page_ops.py | 102 ++++++++++++--- test_page_setup.py | 90 +++++++++++++ 3 files changed, 315 insertions(+), 19 deletions(-) create mode 100644 test_page_setup.py diff --git a/pyPhotoAlbum/mixins/operations/file_ops.py b/pyPhotoAlbum/mixins/operations/file_ops.py index 5555a7e..02e7ac8 100644 --- a/pyPhotoAlbum/mixins/operations/file_ops.py +++ b/pyPhotoAlbum/mixins/operations/file_ops.py @@ -18,10 +18,144 @@ class FileOperationsMixin: shortcut="Ctrl+N" ) def new_project(self): - """Create a new project""" - self.project = Project("New Project") - self.show_status("New project created") - print("New project created") + """Create a new project with initial setup dialog""" + from PyQt6.QtWidgets import QDialog, QVBoxLayout, QHBoxLayout, QLabel, QDoubleSpinBox, QSpinBox, QPushButton, QGroupBox, QLineEdit + + # Create new project setup dialog + dialog = QDialog(self) + dialog.setWindowTitle("New Project Setup") + dialog.setMinimumWidth(450) + + layout = QVBoxLayout() + + # Project name group + name_group = QGroupBox("Project Name") + name_layout = QVBoxLayout() + name_input = QLineEdit() + name_input.setText("New Project") + name_input.selectAll() + name_layout.addWidget(name_input) + name_group.setLayout(name_layout) + layout.addWidget(name_group) + + # Default page size group + size_group = QGroupBox("Default Page Size") + size_layout = QVBoxLayout() + + info_label = QLabel("This will be the default size for all new pages in this project.") + info_label.setWordWrap(True) + info_label.setStyleSheet("font-size: 9pt; color: gray;") + size_layout.addWidget(info_label) + + # Width + width_layout = QHBoxLayout() + width_layout.addWidget(QLabel("Width:")) + width_spinbox = QDoubleSpinBox() + width_spinbox.setRange(10, 1000) + width_spinbox.setValue(140) # Default 14cm + width_spinbox.setSuffix(" mm") + width_layout.addWidget(width_spinbox) + size_layout.addLayout(width_layout) + + # Height + height_layout = QHBoxLayout() + height_layout.addWidget(QLabel("Height:")) + height_spinbox = QDoubleSpinBox() + height_spinbox.setRange(10, 1000) + height_spinbox.setValue(140) # Default 14cm + height_spinbox.setSuffix(" mm") + height_layout.addWidget(height_spinbox) + size_layout.addLayout(height_layout) + + # Add common size presets + presets_layout = QHBoxLayout() + presets_layout.addWidget(QLabel("Presets:")) + + def set_preset(w, h): + width_spinbox.setValue(w) + height_spinbox.setValue(h) + + preset_a4 = QPushButton("A4 (210×297)") + preset_a4.clicked.connect(lambda: set_preset(210, 297)) + presets_layout.addWidget(preset_a4) + + preset_a5 = QPushButton("A5 (148×210)") + preset_a5.clicked.connect(lambda: set_preset(148, 210)) + presets_layout.addWidget(preset_a5) + + preset_square = QPushButton("Square (200×200)") + preset_square.clicked.connect(lambda: set_preset(200, 200)) + presets_layout.addWidget(preset_square) + + presets_layout.addStretch() + size_layout.addLayout(presets_layout) + + size_group.setLayout(size_layout) + layout.addWidget(size_group) + + # DPI settings group + dpi_group = QGroupBox("DPI Settings") + dpi_layout = QVBoxLayout() + + # Working DPI + working_dpi_layout = QHBoxLayout() + working_dpi_layout.addWidget(QLabel("Working DPI:")) + working_dpi_spinbox = QSpinBox() + working_dpi_spinbox.setRange(72, 1200) + working_dpi_spinbox.setValue(300) + working_dpi_layout.addWidget(working_dpi_spinbox) + dpi_layout.addLayout(working_dpi_layout) + + # Export DPI + export_dpi_layout = QHBoxLayout() + export_dpi_layout.addWidget(QLabel("Export DPI:")) + export_dpi_spinbox = QSpinBox() + export_dpi_spinbox.setRange(72, 1200) + export_dpi_spinbox.setValue(300) + export_dpi_layout.addWidget(export_dpi_spinbox) + dpi_layout.addLayout(export_dpi_layout) + + dpi_group.setLayout(dpi_layout) + layout.addWidget(dpi_group) + + # Buttons + button_layout = QHBoxLayout() + cancel_btn = QPushButton("Cancel") + cancel_btn.clicked.connect(dialog.reject) + create_btn = QPushButton("Create Project") + create_btn.clicked.connect(dialog.accept) + create_btn.setDefault(True) + + button_layout.addStretch() + button_layout.addWidget(cancel_btn) + button_layout.addWidget(create_btn) + layout.addLayout(button_layout) + + dialog.setLayout(layout) + + # Show dialog + if dialog.exec() == QDialog.DialogCode.Accepted: + # Get values + project_name = name_input.text().strip() or "New Project" + width_mm = width_spinbox.value() + height_mm = height_spinbox.value() + working_dpi = working_dpi_spinbox.value() + export_dpi = export_dpi_spinbox.value() + + # Create project with custom settings + self.project = Project(project_name) + self.project.page_size_mm = (width_mm, height_mm) + self.project.working_dpi = working_dpi + self.project.export_dpi = export_dpi + + # Update view + self.update_view() + + self.show_status(f"New project created: {project_name} ({width_mm}×{height_mm} mm)") + print(f"New project created: {project_name}, default page size: {width_mm}×{height_mm} mm") + else: + # User cancelled - keep current project + print("New project creation cancelled") @ribbon_action( label="Open", diff --git a/pyPhotoAlbum/mixins/operations/page_ops.py b/pyPhotoAlbum/mixins/operations/page_ops.py index 7eb9291..e4f9d8e 100644 --- a/pyPhotoAlbum/mixins/operations/page_ops.py +++ b/pyPhotoAlbum/mixins/operations/page_ops.py @@ -43,20 +43,41 @@ class PageOperationsMixin: ) def page_setup(self): """Open page setup dialog""" - from PyQt6.QtWidgets import QDialog, QVBoxLayout, QHBoxLayout, QLabel, QDoubleSpinBox, QSpinBox, QPushButton, QGroupBox + from PyQt6.QtWidgets import QDialog, QVBoxLayout, QHBoxLayout, QLabel, QDoubleSpinBox, QSpinBox, QPushButton, QGroupBox, QComboBox, QCheckBox - # Use first page as reference + # Check if we have pages if not self.project.pages: return - current_page = self.project.pages[0] # Create dialog dialog = QDialog(self) dialog.setWindowTitle("Page Setup") - dialog.setMinimumWidth(400) + dialog.setMinimumWidth(450) layout = QVBoxLayout() + # Page selection group + page_select_group = QGroupBox("Select Page") + page_select_layout = QVBoxLayout() + + page_combo = QComboBox() + for i, page in enumerate(self.project.pages): + page_label = f"Page {page.page_number}" + if page.is_double_spread: + page_label += f" (Double Spread: {page.page_number}-{page.page_number + 1})" + if page.manually_sized: + page_label += " *" + page_combo.addItem(page_label, i) + page_select_layout.addWidget(page_combo) + + # Add info label + info_label = QLabel("* = Manually sized page") + info_label.setStyleSheet("font-size: 9pt; color: gray;") + page_select_layout.addWidget(info_label) + + page_select_group.setLayout(page_select_layout) + layout.addWidget(page_select_group) + # Page size group size_group = QGroupBox("Page Size") size_layout = QVBoxLayout() @@ -66,7 +87,6 @@ class PageOperationsMixin: width_layout.addWidget(QLabel("Width:")) width_spinbox = QDoubleSpinBox() width_spinbox.setRange(10, 1000) - width_spinbox.setValue(current_page.layout.size[0]) width_spinbox.setSuffix(" mm") width_layout.addWidget(width_spinbox) size_layout.addLayout(width_layout) @@ -76,11 +96,15 @@ class PageOperationsMixin: height_layout.addWidget(QLabel("Height:")) height_spinbox = QDoubleSpinBox() height_spinbox.setRange(10, 1000) - height_spinbox.setValue(current_page.layout.size[1]) height_spinbox.setSuffix(" mm") height_layout.addWidget(height_spinbox) size_layout.addLayout(height_layout) + # Set as default checkbox + set_default_checkbox = QCheckBox("Set as default for new pages") + set_default_checkbox.setToolTip("Update project default page size for future pages") + size_layout.addWidget(set_default_checkbox) + size_group.setLayout(size_layout) layout.addWidget(size_group) @@ -109,6 +133,24 @@ class PageOperationsMixin: dpi_group.setLayout(dpi_layout) layout.addWidget(dpi_group) + # Function to update displayed values when page selection changes + def on_page_changed(index): + selected_page = self.project.pages[index] + # Get base width (accounting for double spreads) + if selected_page.is_double_spread: + display_width = selected_page.layout.base_width if hasattr(selected_page.layout, 'base_width') else selected_page.layout.size[0] / 2 + else: + display_width = selected_page.layout.size[0] + + width_spinbox.setValue(display_width) + height_spinbox.setValue(selected_page.layout.size[1]) + + # Connect page selection change + page_combo.currentIndexChanged.connect(on_page_changed) + + # Initialize with first page + on_page_changed(0) + # Buttons button_layout = QHBoxLayout() cancel_btn = QPushButton("Cancel") @@ -126,24 +168,54 @@ class PageOperationsMixin: # Show dialog if dialog.exec() == QDialog.DialogCode.Accepted: - # Apply changes + # Get selected page + selected_index = page_combo.currentData() + selected_page = self.project.pages[selected_index] + + # Get new values width_mm = width_spinbox.value() height_mm = height_spinbox.value() # Check if size actually changed - old_size = current_page.layout.size - if old_size != (width_mm, height_mm): - # Mark page as manually sized - current_page.manually_sized = True - current_page.layout.size = (width_mm, height_mm) - print(f"Page {current_page.page_number} marked as manually sized") + # For double spreads, compare with base width + if selected_page.is_double_spread: + old_base_width = selected_page.layout.base_width if hasattr(selected_page.layout, 'base_width') else selected_page.layout.size[0] / 2 + old_height = selected_page.layout.size[1] + size_changed = (old_base_width != width_mm or old_height != height_mm) + + if size_changed: + # Update double spread + selected_page.layout.base_width = width_mm + selected_page.layout.size = (width_mm * 2, height_mm) + selected_page.manually_sized = True + print(f"Page {selected_page.page_number} (double spread) updated to {width_mm}×{height_mm} mm per page") + else: + old_size = selected_page.layout.size + size_changed = (old_size != (width_mm, height_mm)) + + if size_changed: + # Update single page + selected_page.layout.size = (width_mm, height_mm) + selected_page.layout.base_width = width_mm + selected_page.manually_sized = True + print(f"Page {selected_page.page_number} updated to {width_mm}×{height_mm} mm") + # Update DPI settings self.project.working_dpi = working_dpi_spinbox.value() self.project.export_dpi = export_dpi_spinbox.value() + # Set as default if checkbox is checked + if set_default_checkbox.isChecked(): + self.project.page_size_mm = (width_mm, height_mm) + print(f"Project default page size set to {width_mm}×{height_mm} mm") + self.update_view() - self.show_status(f"Page size updated: {width_mm}x{height_mm} mm", 2000) - print(f"Page setup updated: {width_mm}x{height_mm} mm, Working DPI: {self.project.working_dpi}, Export DPI: {self.project.export_dpi}") + + # Build status message + status_msg = f"Page {selected_page.page_number} size: {width_mm}×{height_mm} mm" + if set_default_checkbox.isChecked(): + status_msg += " (set as default)" + self.show_status(status_msg, 2000) @ribbon_action( label="Toggle Spread", diff --git a/test_page_setup.py b/test_page_setup.py new file mode 100644 index 0000000..ecc101e --- /dev/null +++ b/test_page_setup.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python3 +""" +Test script to verify Page Setup functionality +""" + +import sys +from PyQt6.QtWidgets import QApplication +from pyPhotoAlbum.project import Project, Page +from pyPhotoAlbum.page_layout import PageLayout + + +def test_page_setup_behavior(): + """Test that page setup works correctly with multiple pages""" + + # Create a project with default size + project = Project("Test Project") + print(f"Initial project default page size: {project.page_size_mm}") + + # Add first page - should use project default + page1_layout = PageLayout(width=project.page_size_mm[0], height=project.page_size_mm[1]) + page1 = Page(layout=page1_layout, page_number=1) + page1.manually_sized = False + project.add_page(page1) + print(f"Page 1 size: {page1.layout.size}, manually_sized: {page1.manually_sized}") + + # Add second page - should also use project default + page2_layout = PageLayout(width=project.page_size_mm[0], height=project.page_size_mm[1]) + page2 = Page(layout=page2_layout, page_number=2) + page2.manually_sized = False + project.add_page(page2) + print(f"Page 2 size: {page2.layout.size}, manually_sized: {page2.manually_sized}") + + # Simulate changing page 1 size without setting as default + print("\n--- Simulating Page Setup on Page 1 (without setting as default) ---") + page1.layout.size = (200, 200) + page1.layout.base_width = 200 + page1.manually_sized = True + print(f"Page 1 size after change: {page1.layout.size}, manually_sized: {page1.manually_sized}") + print(f"Project default still: {project.page_size_mm}") + print(f"Page 2 unchanged: {page2.layout.size}, manually_sized: {page2.manually_sized}") + + # Add third page - should use original project default + page3_layout = PageLayout(width=project.page_size_mm[0], height=project.page_size_mm[1]) + page3 = Page(layout=page3_layout, page_number=3) + page3.manually_sized = False + project.add_page(page3) + print(f"Page 3 size: {page3.layout.size}, manually_sized: {page3.manually_sized}") + + # Simulate changing page 2 size AND setting as default + print("\n--- Simulating Page Setup on Page 2 (with setting as default) ---") + page2.layout.size = (250, 250) + page2.layout.base_width = 250 + page2.manually_sized = True + project.page_size_mm = (250, 250) # Set as default + print(f"Page 2 size after change: {page2.layout.size}, manually_sized: {page2.manually_sized}") + print(f"Project default updated to: {project.page_size_mm}") + + # Add fourth page - should use NEW project default + page4_layout = PageLayout(width=project.page_size_mm[0], height=project.page_size_mm[1]) + page4 = Page(layout=page4_layout, page_number=4) + page4.manually_sized = False + project.add_page(page4) + print(f"Page 4 size: {page4.layout.size}, manually_sized: {page4.manually_sized}") + + # Test double spread + print("\n--- Testing Double Spread ---") + page5_layout = PageLayout(width=project.page_size_mm[0], height=project.page_size_mm[1], is_facing_page=True) + page5 = Page(layout=page5_layout, page_number=5, is_double_spread=True) + page5.manually_sized = False + project.add_page(page5) + print(f"Page 5 (double spread) size: {page5.layout.size}, base_width: {page5.layout.base_width}") + print(f"Expected: width should be 2x base_width = {project.page_size_mm[0] * 2}") + + print("\n--- Summary ---") + for i, page in enumerate(project.pages, 1): + spread_info = " (double spread)" if page.is_double_spread else "" + manual_info = " *manually sized*" if page.manually_sized else "" + print(f"Page {i}: {page.layout.size}{spread_info}{manual_info}") + print(f"Project default: {project.page_size_mm}") + + print("\n✓ All tests passed!") + + +if __name__ == "__main__": + # Initialize Qt application (needed for PyQt6 widgets even in tests) + app = QApplication(sys.argv) + + test_page_setup_behavior() + + print("\nTest completed successfully!")