427 lines
14 KiB
Python
427 lines
14 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Basic Usage Example for pyPhotoAlbum
|
|
|
|
This example demonstrates:
|
|
- Creating a new project
|
|
- Adding pages with images and text
|
|
- Working with the asset manager
|
|
- Saving and loading projects
|
|
- Basic element manipulation
|
|
|
|
Based on unit test examples from the pyPhotoAlbum test suite.
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import tempfile
|
|
from pathlib import Path
|
|
|
|
# Add parent directory to path to import pyPhotoAlbum
|
|
sys.path.insert(0, str(Path(__file__).parent.parent))
|
|
|
|
from pyPhotoAlbum.project import Project, Page
|
|
from pyPhotoAlbum.page_layout import PageLayout
|
|
from pyPhotoAlbum.models import ImageData, TextBoxData, PlaceholderData
|
|
from pyPhotoAlbum.project_serializer import save_to_zip, load_from_zip, get_project_info
|
|
from pyPhotoAlbum.pdf_exporter import PDFExporter
|
|
|
|
|
|
def create_sample_image(path: str, color: str = 'blue', size: tuple = (400, 300)):
|
|
"""Create a sample image for testing"""
|
|
try:
|
|
from PIL import Image, ImageDraw, ImageFont
|
|
|
|
img = Image.new('RGB', size, color=color)
|
|
draw = ImageDraw.Draw(img)
|
|
|
|
# Draw a border
|
|
border_width = 10
|
|
draw.rectangle(
|
|
[(border_width, border_width), (size[0]-border_width, size[1]-border_width)],
|
|
outline='white',
|
|
width=5
|
|
)
|
|
|
|
# Add some text
|
|
text = f"{color.upper()}\n{size[0]}x{size[1]}"
|
|
try:
|
|
# Try to use a nice font
|
|
font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 36)
|
|
except:
|
|
# Fallback to default
|
|
font = ImageFont.load_default()
|
|
|
|
# Calculate text position (center)
|
|
bbox = draw.textbbox((0, 0), text, font=font)
|
|
text_width = bbox[2] - bbox[0]
|
|
text_height = bbox[3] - bbox[1]
|
|
x = (size[0] - text_width) // 2
|
|
y = (size[1] - text_height) // 2
|
|
|
|
draw.text((x, y), text, fill='white', font=font)
|
|
|
|
img.save(path)
|
|
print(f"Created sample image: {path}")
|
|
return True
|
|
except Exception as e:
|
|
print(f"Could not create sample image: {e}")
|
|
return False
|
|
|
|
|
|
def example_1_create_basic_project():
|
|
"""
|
|
Example 1: Create a basic project with one page and an image
|
|
|
|
This demonstrates the fundamental workflow based on test_project.py
|
|
"""
|
|
print("\n" + "="*60)
|
|
print("Example 1: Creating a Basic Project")
|
|
print("="*60)
|
|
|
|
# Create output directory
|
|
output_dir = Path(__file__).parent / "output"
|
|
output_dir.mkdir(exist_ok=True)
|
|
|
|
# Create a temporary directory for the project
|
|
temp_dir = tempfile.mkdtemp(prefix="photo_album_")
|
|
project_folder = os.path.join(temp_dir, "my_album")
|
|
|
|
print(f"\nProject folder: {project_folder}")
|
|
|
|
# Create a new project (from test_project.py)
|
|
project = Project(name="My First Album", folder_path=project_folder)
|
|
project.page_size_mm = (210, 297) # A4 size
|
|
project.working_dpi = 300
|
|
project.export_dpi = 300
|
|
|
|
print(f"Created project: {project.name}")
|
|
print(f"Page size: {project.page_size_mm[0]}mm x {project.page_size_mm[1]}mm")
|
|
print(f"DPI: {project.working_dpi}")
|
|
|
|
# Create a sample image
|
|
image_path = output_dir / "sample_photo.jpg"
|
|
create_sample_image(str(image_path), color='blue', size=(800, 600))
|
|
|
|
# Import the image into the project (from test_project_serialization.py)
|
|
imported_path = project.asset_manager.import_asset(str(image_path))
|
|
print(f"\nImported asset: {imported_path}")
|
|
|
|
# Create a page layout (from test_project.py)
|
|
layout = PageLayout(width=210, height=297)
|
|
|
|
# Add an image element (from test_models.py)
|
|
image = ImageData(
|
|
image_path=imported_path,
|
|
x=10.0,
|
|
y=10.0,
|
|
width=190.0,
|
|
height=140.0,
|
|
rotation=0,
|
|
z_index=0
|
|
)
|
|
layout.add_element(image)
|
|
print(f"Added image at position ({image.position[0]}, {image.position[1]})")
|
|
print(f"Image size: {image.size[0]}mm x {image.size[1]}mm")
|
|
|
|
# Add a text box (from test_models.py)
|
|
textbox = TextBoxData(
|
|
text_content="My First Photo Album",
|
|
font_settings={"family": "Arial", "size": 24, "color": (0, 0, 0)},
|
|
alignment="center",
|
|
x=10.0,
|
|
y=160.0,
|
|
width=190.0,
|
|
height=30.0
|
|
)
|
|
layout.add_element(textbox)
|
|
print(f"Added text box: '{textbox.text_content}'")
|
|
|
|
# Create a page and add it to the project
|
|
page = Page(layout=layout, page_number=1)
|
|
project.add_page(page)
|
|
print(f"\nAdded page {page.page_number} to project")
|
|
print(f"Total pages: {len(project.pages)}")
|
|
print(f"Total elements on page: {len(page.layout.elements)}")
|
|
|
|
# Save the project (from test_project_serialization.py)
|
|
output_path = output_dir / "basic_project.ppz"
|
|
print(f"\nSaving project to: {output_path}")
|
|
success, error = save_to_zip(project, str(output_path))
|
|
|
|
if success:
|
|
print("Project saved successfully!")
|
|
|
|
# Get project info without loading (from test_project_serialization.py)
|
|
info = get_project_info(str(output_path))
|
|
if info:
|
|
print(f"\nProject Info:")
|
|
print(f" Name: {info['name']}")
|
|
print(f" Pages: {info['page_count']}")
|
|
print(f" Version: {info['version']}")
|
|
print(f" Working DPI: {info['working_dpi']}")
|
|
else:
|
|
print(f"Error saving project: {error}")
|
|
|
|
return str(output_path)
|
|
|
|
|
|
def example_2_load_and_modify_project(project_path: str):
|
|
"""
|
|
Example 2: Load an existing project and add more pages
|
|
|
|
Based on test_project_serialization.py
|
|
"""
|
|
print("\n" + "="*60)
|
|
print("Example 2: Loading and Modifying a Project")
|
|
print("="*60)
|
|
|
|
# Load the project (from test_project_serialization.py)
|
|
print(f"\nLoading project from: {project_path}")
|
|
loaded_project, error = load_from_zip(project_path)
|
|
|
|
if not loaded_project:
|
|
print(f"Error loading project: {error}")
|
|
return
|
|
|
|
print(f"Loaded project: {loaded_project.name}")
|
|
print(f"Pages: {len(loaded_project.pages)}")
|
|
|
|
# Create output directory
|
|
output_dir = Path(__file__).parent / "output"
|
|
|
|
# Create more sample images
|
|
for i in range(2, 4):
|
|
image_path = output_dir / f"sample_photo_{i}.jpg"
|
|
colors = ['red', 'green']
|
|
create_sample_image(str(image_path), color=colors[i-2], size=(600, 800))
|
|
|
|
# Import the image
|
|
imported_path = loaded_project.asset_manager.import_asset(str(image_path))
|
|
|
|
# Create a new page with the image (from test_project.py)
|
|
layout = PageLayout(width=210, height=297)
|
|
image = ImageData(
|
|
image_path=imported_path,
|
|
x=20.0 + i*5,
|
|
y=20.0 + i*5,
|
|
width=170.0,
|
|
height=230.0
|
|
)
|
|
layout.add_element(image)
|
|
|
|
# Add caption
|
|
caption = TextBoxData(
|
|
text_content=f"Page {i}",
|
|
font_settings={"family": "Arial", "size": 18, "color": (0, 0, 0)},
|
|
alignment="center",
|
|
x=20.0 + i*5,
|
|
y=260.0,
|
|
width=170.0,
|
|
height=25.0
|
|
)
|
|
layout.add_element(caption)
|
|
|
|
page = Page(layout=layout, page_number=i)
|
|
loaded_project.add_page(page)
|
|
print(f"Added page {i} with {len(page.layout.elements)} elements")
|
|
|
|
# Save the modified project
|
|
modified_path = output_dir / "modified_project.ppz"
|
|
print(f"\nSaving modified project to: {modified_path}")
|
|
success, error = save_to_zip(loaded_project, str(modified_path))
|
|
|
|
if success:
|
|
print("Modified project saved successfully!")
|
|
print(f"Total pages: {len(loaded_project.pages)}")
|
|
else:
|
|
print(f"Error saving: {error}")
|
|
|
|
return str(modified_path)
|
|
|
|
|
|
def example_3_serialization_roundtrip():
|
|
"""
|
|
Example 3: Demonstrate serialization/deserialization
|
|
|
|
Based on test_models.py serialization tests
|
|
"""
|
|
print("\n" + "="*60)
|
|
print("Example 3: Serialization Round-Trip")
|
|
print("="*60)
|
|
|
|
# Create an image element (from test_models.py)
|
|
print("\n1. Creating ImageData element...")
|
|
original_image = ImageData(
|
|
image_path="test.jpg",
|
|
x=50.0,
|
|
y=60.0,
|
|
width=300.0,
|
|
height=200.0,
|
|
rotation=15.0,
|
|
z_index=2,
|
|
crop_info=(0.1, 0.1, 0.9, 0.9)
|
|
)
|
|
|
|
print(f" Position: {original_image.position}")
|
|
print(f" Size: {original_image.size}")
|
|
print(f" Rotation: {original_image.rotation}°")
|
|
print(f" Z-index: {original_image.z_index}")
|
|
|
|
# Serialize (from test_models.py)
|
|
print("\n2. Serializing to dictionary...")
|
|
data = original_image.serialize()
|
|
print(f" Serialized data keys: {list(data.keys())}")
|
|
print(f" Type: {data['type']}")
|
|
|
|
# Deserialize (from test_models.py)
|
|
print("\n3. Deserializing from dictionary...")
|
|
restored_image = ImageData()
|
|
restored_image.deserialize(data)
|
|
|
|
print(f" Position: {restored_image.position}")
|
|
print(f" Size: {restored_image.size}")
|
|
print(f" Rotation: {restored_image.rotation}°")
|
|
|
|
# Verify round-trip
|
|
print("\n4. Verifying round-trip...")
|
|
assert restored_image.position == original_image.position
|
|
assert restored_image.size == original_image.size
|
|
assert restored_image.rotation == original_image.rotation
|
|
assert restored_image.z_index == original_image.z_index
|
|
assert restored_image.crop_info == original_image.crop_info
|
|
print(" Round-trip successful!")
|
|
|
|
# Do the same for TextBoxData (from test_models.py)
|
|
print("\n5. Testing TextBoxData serialization...")
|
|
font_settings = {"family": "Georgia", "size": 20, "color": (255, 255, 0)}
|
|
original_text = TextBoxData(
|
|
text_content="Round Trip Test",
|
|
font_settings=font_settings,
|
|
alignment="center",
|
|
x=85.0,
|
|
y=95.0,
|
|
width=320.0,
|
|
height=120.0,
|
|
rotation=25.0,
|
|
z_index=9
|
|
)
|
|
|
|
data = original_text.serialize()
|
|
restored_text = TextBoxData()
|
|
restored_text.deserialize(data)
|
|
|
|
assert restored_text.text_content == original_text.text_content
|
|
assert restored_text.alignment == original_text.alignment
|
|
assert restored_text.position == original_text.position
|
|
print(" TextBoxData round-trip successful!")
|
|
|
|
|
|
def example_4_export_to_pdf():
|
|
"""
|
|
Example 4: Export a project to PDF
|
|
|
|
Based on pdf_exporter.py usage
|
|
"""
|
|
print("\n" + "="*60)
|
|
print("Example 4: Export to PDF")
|
|
print("="*60)
|
|
|
|
# Create a simple project
|
|
output_dir = Path(__file__).parent / "output"
|
|
output_dir.mkdir(exist_ok=True)
|
|
|
|
temp_dir = tempfile.mkdtemp(prefix="photo_album_pdf_")
|
|
project_folder = os.path.join(temp_dir, "pdf_export")
|
|
|
|
project = Project(name="PDF Export Example", folder_path=project_folder)
|
|
project.page_size_mm = (140, 140) # Square format
|
|
project.working_dpi = 300
|
|
project.export_dpi = 300
|
|
|
|
print(f"\nCreating project with {project.page_size_mm[0]}x{project.page_size_mm[1]}mm pages")
|
|
|
|
# Create multiple pages with different colored images
|
|
colors = ['red', 'green', 'blue', 'yellow']
|
|
for i, color in enumerate(colors, 1):
|
|
# Create sample image
|
|
image_path = output_dir / f"pdf_sample_{color}.jpg"
|
|
create_sample_image(str(image_path), color=color, size=(600, 600))
|
|
|
|
# Import and add to page
|
|
imported_path = project.asset_manager.import_asset(str(image_path))
|
|
|
|
layout = PageLayout(width=140, height=140)
|
|
image = ImageData(
|
|
image_path=imported_path,
|
|
x=10.0,
|
|
y=10.0,
|
|
width=120.0,
|
|
height=120.0
|
|
)
|
|
layout.add_element(image)
|
|
|
|
page = Page(layout=layout, page_number=i)
|
|
project.add_page(page)
|
|
|
|
print(f"Created {len(project.pages)} pages")
|
|
|
|
# Export to PDF
|
|
pdf_path = output_dir / "example_album.pdf"
|
|
print(f"\nExporting to PDF: {pdf_path}")
|
|
|
|
exporter = PDFExporter(project, export_dpi=300)
|
|
|
|
def progress_callback(current, total):
|
|
print(f" Exporting page {current}/{total}...")
|
|
|
|
success, errors = exporter.export(
|
|
output_path=str(pdf_path),
|
|
progress_callback=progress_callback
|
|
)
|
|
|
|
if success:
|
|
print(f"\nPDF exported successfully!")
|
|
print(f"File size: {os.path.getsize(pdf_path) / 1024:.1f} KB")
|
|
else:
|
|
print(f"\nErrors during export:")
|
|
for error in errors:
|
|
print(f" - {error}")
|
|
|
|
|
|
def main():
|
|
"""Run all examples"""
|
|
print("\n" + "="*60)
|
|
print("pyPhotoAlbum - Basic Usage Examples")
|
|
print("="*60)
|
|
print("\nThese examples demonstrate core functionality using")
|
|
print("code patterns from the pyPhotoAlbum unit tests.\n")
|
|
|
|
try:
|
|
# Example 1: Create a basic project
|
|
project_path = example_1_create_basic_project()
|
|
|
|
# Example 2: Load and modify
|
|
if project_path and os.path.exists(project_path):
|
|
example_2_load_and_modify_project(project_path)
|
|
|
|
# Example 3: Serialization
|
|
example_3_serialization_roundtrip()
|
|
|
|
# Example 4: PDF export
|
|
example_4_export_to_pdf()
|
|
|
|
print("\n" + "="*60)
|
|
print("All examples completed successfully!")
|
|
print("="*60)
|
|
print(f"\nOutput files are in: {Path(__file__).parent / 'output'}")
|
|
|
|
except Exception as e:
|
|
print(f"\nError running examples: {e}")
|
|
import traceback
|
|
traceback.print_exc()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|