pyPhotoAlbum/examples/template_example.py
Duncan Tourolle 46585228fd
Some checks failed
Lint / lint (push) Failing after 2m46s
Tests / test (3.11) (push) Has been cancelled
Tests / test (3.9) (push) Has been cancelled
Tests / test (3.10) (push) Has been cancelled
first commit
2025-10-21 22:02:49 +02:00

438 lines
14 KiB
Python

#!/usr/bin/env python3
"""
Template System Example for pyPhotoAlbum
This example demonstrates:
- Creating templates from existing pages
- Using built-in templates
- Applying templates to new pages
- Template scaling modes
- Saving and loading custom templates
Based on template_manager.py and TEMPLATES_README.md
"""
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.template_manager import TemplateManager, Template
from pyPhotoAlbum.project_serializer import save_to_zip
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 text
text = f"{color.upper()}"
try:
font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 48)
except:
font = ImageFont.load_default()
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: {path}")
return True
except Exception as e:
print(f" Could not create image: {e}")
return False
def example_1_list_templates():
"""
Example 1: List available templates
From template_manager.py
"""
print("\n" + "="*60)
print("Example 1: Listing Available Templates")
print("="*60)
manager = TemplateManager()
print("\nAvailable templates:")
templates = manager.list_templates()
for i, template_name in enumerate(templates, 1):
print(f" {i}. {template_name}")
# Load and display info about each template
print("\nTemplate Details:")
for template_name in templates[:2]: # Show first 2 templates
try:
template = manager.load_template(template_name)
print(f"\n {template.name}:")
print(f" Description: {template.description}")
print(f" Page Size: {template.page_size_mm[0]}x{template.page_size_mm[1]} mm")
print(f" Elements: {len(template.elements)}")
except Exception as e:
print(f" Error loading: {e}")
def example_2_create_page_from_template():
"""
Example 2: Create a new page from a built-in template
From template_manager.py - create_page_from_template method
"""
print("\n" + "="*60)
print("Example 2: Creating Page from Template")
print("="*60)
manager = TemplateManager()
output_dir = Path(__file__).parent / "output"
output_dir.mkdir(exist_ok=True)
# Create a project
temp_dir = tempfile.mkdtemp(prefix="template_example_")
project_folder = os.path.join(temp_dir, "template_project")
project = Project(name="Template Example", folder_path=project_folder)
project.page_size_mm = (210, 297) # A4
project.working_dpi = 300
print(f"\nCreated project: {project.name}")
print(f"Page size: {project.page_size_mm[0]}x{project.page_size_mm[1]} mm")
# Create a page from Grid_2x2 template
print("\nCreating page from 'Grid_2x2' template...")
page = manager.create_page_from_template(
template_name="Grid_2x2",
target_page_size=project.page_size_mm,
page_number=1
)
print(f" Created page with {len(page.layout.elements)} placeholder elements")
# Show details of placeholders
for i, element in enumerate(page.layout.elements, 1):
if isinstance(element, PlaceholderData):
print(f" Placeholder {i}: position={element.position}, size={element.size}")
project.add_page(page)
# Now replace placeholders with actual images
print("\nReplacing placeholders with images...")
colors = ['red', 'green', 'blue', 'yellow']
for i, element in enumerate(page.layout.elements):
if isinstance(element, PlaceholderData) and i < len(colors):
# Create sample image
image_path = output_dir / f"grid_image_{colors[i]}.jpg"
create_sample_image(str(image_path), color=colors[i], size=(600, 600))
# Import to project
imported_path = project.asset_manager.import_asset(str(image_path))
# Replace placeholder with image
image = ImageData(
image_path=imported_path,
x=element.position[0],
y=element.position[1],
width=element.size[0],
height=element.size[1],
z_index=element.z_index
)
# Remove placeholder and add image
page.layout.remove_element(element)
page.layout.add_element(image)
print(f" Replaced placeholder {i+1} with {colors[i]} image")
# Save project
project_path = output_dir / "template_project.ppz"
print(f"\nSaving project to: {project_path}")
success, error = save_to_zip(project, str(project_path))
if success:
print(" Project saved successfully!")
else:
print(f" Error: {error}")
def example_3_create_custom_template():
"""
Example 3: Create a custom template from a page
From template_manager.py - create_template_from_page method
"""
print("\n" + "="*60)
print("Example 3: Creating Custom Template")
print("="*60)
manager = TemplateManager()
output_dir = Path(__file__).parent / "output"
output_dir.mkdir(exist_ok=True)
# Create a page with a custom layout
print("\nDesigning custom page layout...")
layout = PageLayout(width=210, height=297)
# Large image at top
top_image = ImageData(
image_path="dummy.jpg", # Will be converted to placeholder
x=10.0,
y=10.0,
width=190.0,
height=140.0,
z_index=0
)
layout.add_element(top_image)
print(" Added large top image")
# Two smaller images at bottom
bottom_left = ImageData(
image_path="dummy.jpg",
x=10.0,
y=160.0,
width=90.0,
height=90.0,
z_index=0
)
layout.add_element(bottom_left)
bottom_right = ImageData(
image_path="dummy.jpg",
x=110.0,
y=160.0,
width=90.0,
height=90.0,
z_index=0
)
layout.add_element(bottom_right)
print(" Added two smaller bottom images")
# Title text box
title = TextBoxData(
text_content="Title",
font_settings={"family": "Arial", "size": 20, "color": (0, 0, 0)},
alignment="center",
x=10.0,
y=260.0,
width=190.0,
height=30.0,
z_index=1
)
layout.add_element(title)
print(" Added title text box")
# Create page
page = Page(layout=layout, page_number=1)
# Create template from page
print("\nCreating template from page...")
template = manager.create_template_from_page(
page=page,
name="Custom_Large_Plus_Two",
description="One large image at top, two smaller at bottom, with title"
)
print(f" Template created: {template.name}")
print(f" Description: {template.description}")
print(f" Elements: {len(template.elements)}")
# Save template
print("\nSaving custom template...")
try:
manager.save_template(template)
print(f" Template saved successfully!")
print(f" Location: {manager._get_templates_directory()}")
except Exception as e:
print(f" Error saving template: {e}")
# Verify it's in the list
templates = manager.list_templates()
if template.name in templates:
print(f" Verified: Template appears in list")
def example_4_template_scaling_modes():
"""
Example 4: Demonstrate different template scaling modes
From template_manager.py - scale_template_elements method
"""
print("\n" + "="*60)
print("Example 4: Template Scaling Modes")
print("="*60)
manager = TemplateManager()
# Create a simple template
print("\nCreating test template (100x100mm square)...")
template = Template(name="Test Square", page_size_mm=(100, 100))
# Add a centered square element
element = PlaceholderData(
x=25.0, y=25.0,
width=50.0, height=50.0
)
template.add_element(element)
print(f" Original element: position={element.position}, size={element.size}")
# Test different target sizes
target_sizes = [
(200, 200, "Same aspect ratio (2x)"),
(200, 100, "Wider (2x wide, 1x tall)"),
(100, 200, "Taller (1x wide, 2x tall)")
]
scaling_modes = ["proportional", "stretch", "center"]
for target_w, target_h, desc in target_sizes:
print(f"\nTarget size: {target_w}x{target_h}mm ({desc})")
for mode in scaling_modes:
scaled = manager.scale_template_elements(
template.elements.copy(),
source_size=template.page_size_mm,
target_size=(target_w, target_h),
scaling=mode
)
if scaled:
scaled_element = scaled[0]
print(f" {mode:12s}: position={scaled_element.position}, size={scaled_element.size}")
def example_5_apply_template_modes():
"""
Example 5: Apply template to existing page with different modes
From template_manager.py - apply_template_to_page method
"""
print("\n" + "="*60)
print("Example 5: Applying Template to Existing Page")
print("="*60)
manager = TemplateManager()
output_dir = Path(__file__).parent / "output"
output_dir.mkdir(exist_ok=True)
# Create a page with some existing content
print("\nCreating page with existing images...")
temp_dir = tempfile.mkdtemp(prefix="template_apply_")
project_folder = os.path.join(temp_dir, "apply_project")
project = Project(name="Apply Template Test", folder_path=project_folder)
project.page_size_mm = (210, 297)
layout = PageLayout(width=210, height=297)
# Add some images
colors = ['red', 'green', 'blue']
for i, color in enumerate(colors):
image_path = output_dir / f"apply_{color}.jpg"
create_sample_image(str(image_path), color=color, size=(600, 400))
imported_path = project.asset_manager.import_asset(str(image_path))
image = ImageData(
image_path=imported_path,
x=10.0 + i*20,
y=10.0 + i*30,
width=100.0,
height=100.0
)
layout.add_element(image)
page = Page(layout=layout, page_number=1)
project.add_page(page)
print(f" Created page with {len(page.layout.elements)} images")
# Apply Grid_2x2 template with "reflow" mode
print("\nApplying Grid_2x2 template with 'reflow' mode...")
print(" This will reposition existing images to fit template slots")
try:
template = manager.load_template("Grid_2x2")
manager.apply_template_to_page(
template=template,
target_page=page,
mode="reflow", # Keep existing images, reposition them
scaling="proportional"
)
print(f" Template applied! Page now has {len(page.layout.elements)} elements")
# Show new positions
for i, element in enumerate(page.layout.elements, 1):
print(f" Element {i}: position={element.position}, size={element.size}")
except Exception as e:
print(f" Error applying template: {e}")
# Save result
project_path = output_dir / "template_applied.ppz"
print(f"\nSaving result to: {project_path}")
success, error = save_to_zip(project, str(project_path))
if success:
print(" Saved successfully!")
def main():
"""Run all template examples"""
print("\n" + "="*60)
print("pyPhotoAlbum - Template System Examples")
print("="*60)
print("\nDemonstrating the template system using code from")
print("template_manager.py and TEMPLATES_README.md\n")
try:
# Example 1: List templates
example_1_list_templates()
# Example 2: Create page from template
example_2_create_page_from_template()
# Example 3: Create custom template
example_3_create_custom_template()
# Example 4: Scaling modes
example_4_template_scaling_modes()
# Example 5: Apply template
example_5_apply_template_modes()
print("\n" + "="*60)
print("All template examples completed!")
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()