more examples
This commit is contained in:
@@ -0,0 +1,390 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Link Navigation Example
|
||||
|
||||
This example demonstrates:
|
||||
- Creating clickable links with LinkedWord
|
||||
- Different link types (INTERNAL, EXTERNAL, API, FUNCTION)
|
||||
- Link styling with underlines and colors
|
||||
- Link callbacks and event handling
|
||||
- Interactive link states (hover, pressed)
|
||||
- Organizing linked content in paragraphs
|
||||
|
||||
This shows how to create interactive documents with hyperlinks.
|
||||
"""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import List
|
||||
|
||||
# Add pyWebLayout to path
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent))
|
||||
|
||||
from pyWebLayout.concrete.page import Page
|
||||
from pyWebLayout.style.page_style import PageStyle
|
||||
from pyWebLayout.style.fonts import Font
|
||||
from pyWebLayout.abstract.inline import Word, LinkedWord
|
||||
from pyWebLayout.abstract.functional import LinkType
|
||||
from pyWebLayout.abstract.block import Paragraph
|
||||
from pyWebLayout.layout.document_layouter import DocumentLayouter
|
||||
|
||||
|
||||
# Track link clicks for demonstration
|
||||
link_clicks = []
|
||||
|
||||
|
||||
def link_callback(link_id: str):
|
||||
"""Callback for link clicks"""
|
||||
def callback():
|
||||
link_clicks.append(link_id)
|
||||
print(f" Link clicked: {link_id}")
|
||||
return callback
|
||||
|
||||
|
||||
def create_paragraph_with_links(
|
||||
text_parts: List[tuple],
|
||||
font_size: int = 14) -> Paragraph:
|
||||
"""
|
||||
Create a paragraph with mixed text and links.
|
||||
|
||||
Args:
|
||||
text_parts: List of tuples where each is either:
|
||||
('text', "word1 word2") for normal text
|
||||
('link', "word", location, link_type, callback_id)
|
||||
font_size: Base font size
|
||||
|
||||
Returns:
|
||||
Paragraph with words and links
|
||||
"""
|
||||
font = Font(font_size=font_size, colour=(50, 50, 50))
|
||||
paragraph = Paragraph(style=font)
|
||||
|
||||
for part in text_parts:
|
||||
if part[0] == 'text':
|
||||
# Add normal words
|
||||
for word_text in part[1].split():
|
||||
paragraph.add_word(Word(word_text, font))
|
||||
elif part[0] == 'link':
|
||||
# Add linked word
|
||||
word_text, location, link_type, callback_id = part[1:]
|
||||
callback = link_callback(callback_id)
|
||||
linked_word = LinkedWord(
|
||||
text=word_text,
|
||||
style=font,
|
||||
location=location,
|
||||
link_type=link_type,
|
||||
callback=callback,
|
||||
title=f"Click to: {location}"
|
||||
)
|
||||
paragraph.add_word(linked_word)
|
||||
|
||||
return paragraph
|
||||
|
||||
|
||||
def create_example_1_internal_links():
|
||||
"""Example 1: Internal navigation links within a document."""
|
||||
print("\n Creating Example 1: Internal links...")
|
||||
|
||||
page_style = PageStyle(
|
||||
border_width=2,
|
||||
border_color=(150, 150, 200),
|
||||
padding=(20, 30, 20, 30),
|
||||
background_color=(255, 255, 255),
|
||||
line_spacing=6
|
||||
)
|
||||
|
||||
page = Page(size=(500, 600), style=page_style)
|
||||
layouter = DocumentLayouter(page)
|
||||
|
||||
# Title
|
||||
title_font = Font(font_size=20, colour=(0, 0, 100), weight='bold')
|
||||
title = Paragraph(style=title_font)
|
||||
for word in "Internal Navigation Links".split():
|
||||
title.add_word(Word(word, title_font))
|
||||
|
||||
# Content with internal links
|
||||
intro = create_paragraph_with_links([
|
||||
('text', "This document demonstrates"),
|
||||
('link', "internal", "#section1", LinkType.INTERNAL, "goto_section1"),
|
||||
('text', "navigation links that jump to different parts of the document."),
|
||||
])
|
||||
|
||||
section1 = create_paragraph_with_links([
|
||||
('text', "Jump to"),
|
||||
('link', "Section 2", "#section2", LinkType.INTERNAL, "goto_section2"),
|
||||
('text', "or"),
|
||||
('link', "Section 3", "#section3", LinkType.INTERNAL, "goto_section3"),
|
||||
('text', "within this document."),
|
||||
])
|
||||
|
||||
section2 = create_paragraph_with_links([
|
||||
('text', "You are in Section 2. Return to"),
|
||||
('link', "top", "#top", LinkType.INTERNAL, "goto_top"),
|
||||
('text', "or go to"),
|
||||
('link', "Section 3", "#section3", LinkType.INTERNAL, "goto_section3_from2"),
|
||||
])
|
||||
|
||||
section3 = create_paragraph_with_links([
|
||||
('text', "This is Section 3. Go back to"),
|
||||
('link', "Section 1", "#section1", LinkType.INTERNAL, "goto_section1_from3"),
|
||||
('text', "or"),
|
||||
('link', "top", "#top", LinkType.INTERNAL, "goto_top_from3"),
|
||||
])
|
||||
|
||||
# Layout content
|
||||
layouter.layout_paragraph(title)
|
||||
layouter.layout_paragraph(intro)
|
||||
layouter.layout_paragraph(section1)
|
||||
layouter.layout_paragraph(section2)
|
||||
layouter.layout_paragraph(section3)
|
||||
|
||||
return page
|
||||
|
||||
|
||||
def create_example_2_external_links():
|
||||
"""Example 2: External links to websites."""
|
||||
print(" Creating Example 2: External links...")
|
||||
|
||||
page_style = PageStyle(
|
||||
border_width=2,
|
||||
border_color=(150, 200, 150),
|
||||
padding=(20, 30, 20, 30),
|
||||
background_color=(255, 255, 255),
|
||||
line_spacing=6
|
||||
)
|
||||
|
||||
page = Page(size=(500, 600), style=page_style)
|
||||
layouter = DocumentLayouter(page)
|
||||
|
||||
# Title
|
||||
title_font = Font(font_size=20, colour=(0, 100, 0), weight='bold')
|
||||
title = Paragraph(style=title_font)
|
||||
for word in "External Web Links".split():
|
||||
title.add_word(Word(word, title_font))
|
||||
|
||||
# Content with external links
|
||||
intro = create_paragraph_with_links([
|
||||
('text', "Click"),
|
||||
('link', "here", "https://example.com", LinkType.EXTERNAL, "visit_example"),
|
||||
('text', "to visit an external website."),
|
||||
])
|
||||
|
||||
resources = create_paragraph_with_links([
|
||||
('text', "Useful resources:"),
|
||||
('link', "Documentation", "https://docs.example.com", LinkType.EXTERNAL, "visit_docs"),
|
||||
('text', "and"),
|
||||
('link', "GitHub", "https://github.com/example", LinkType.EXTERNAL, "visit_github"),
|
||||
])
|
||||
|
||||
more_links = create_paragraph_with_links([
|
||||
('text', "Learn more at"),
|
||||
('link', "Wikipedia", "https://wikipedia.org", LinkType.EXTERNAL, "visit_wiki"),
|
||||
('text', "or check out"),
|
||||
('link', "Python.org", "https://python.org", LinkType.EXTERNAL, "visit_python"),
|
||||
])
|
||||
|
||||
# Layout content
|
||||
layouter.layout_paragraph(title)
|
||||
layouter.layout_paragraph(intro)
|
||||
layouter.layout_paragraph(resources)
|
||||
layouter.layout_paragraph(more_links)
|
||||
|
||||
return page
|
||||
|
||||
|
||||
def create_example_3_api_links():
|
||||
"""Example 3: API links that trigger actions."""
|
||||
print(" Creating Example 3: API links...")
|
||||
|
||||
page_style = PageStyle(
|
||||
border_width=2,
|
||||
border_color=(200, 150, 150),
|
||||
padding=(20, 30, 20, 30),
|
||||
background_color=(255, 255, 255),
|
||||
line_spacing=6
|
||||
)
|
||||
|
||||
page = Page(size=(500, 600), style=page_style)
|
||||
layouter = DocumentLayouter(page)
|
||||
|
||||
# Title
|
||||
title_font = Font(font_size=20, colour=(150, 0, 0), weight='bold')
|
||||
title = Paragraph(style=title_font)
|
||||
for word in "API Action Links".split():
|
||||
title.add_word(Word(word, title_font))
|
||||
|
||||
# Content with API links
|
||||
settings = create_paragraph_with_links([
|
||||
('text', "Click"),
|
||||
('link', "Settings", "/api/settings", LinkType.API, "open_settings"),
|
||||
('text', "to configure the application."),
|
||||
])
|
||||
|
||||
actions = create_paragraph_with_links([
|
||||
('text', "Actions:"),
|
||||
('link', "Save", "/api/save", LinkType.API, "save_action"),
|
||||
('text', "or"),
|
||||
('link', "Export", "/api/export", LinkType.API, "export_action"),
|
||||
('text', "your data."),
|
||||
])
|
||||
|
||||
management = create_paragraph_with_links([
|
||||
('text', "Manage:"),
|
||||
('link', "Users", "/api/users", LinkType.API, "manage_users"),
|
||||
('text', "or"),
|
||||
('link', "Permissions", "/api/permissions", LinkType.API, "manage_perms"),
|
||||
])
|
||||
|
||||
# Layout content
|
||||
layouter.layout_paragraph(title)
|
||||
layouter.layout_paragraph(settings)
|
||||
layouter.layout_paragraph(actions)
|
||||
layouter.layout_paragraph(management)
|
||||
|
||||
return page
|
||||
|
||||
|
||||
def create_example_4_function_links():
|
||||
"""Example 4: Function links that execute code."""
|
||||
print(" Creating Example 4: Function links...")
|
||||
|
||||
page_style = PageStyle(
|
||||
border_width=2,
|
||||
border_color=(150, 200, 200),
|
||||
padding=(20, 30, 20, 30),
|
||||
background_color=(255, 255, 255),
|
||||
line_spacing=6
|
||||
)
|
||||
|
||||
page = Page(size=(500, 600), style=page_style)
|
||||
layouter = DocumentLayouter(page)
|
||||
|
||||
# Title
|
||||
title_font = Font(font_size=20, colour=(0, 120, 120), weight='bold')
|
||||
title = Paragraph(style=title_font)
|
||||
for word in "Function Execution Links".split():
|
||||
title.add_word(Word(word, title_font))
|
||||
|
||||
# Content with function links
|
||||
intro = create_paragraph_with_links([
|
||||
('text', "These links execute"),
|
||||
('link', "functions", "calculate()", LinkType.FUNCTION, "exec_calculate"),
|
||||
('text', "directly in the application."),
|
||||
])
|
||||
|
||||
calculations = create_paragraph_with_links([
|
||||
('text', "Run:"),
|
||||
('link', "analyze()", "analyze()", LinkType.FUNCTION, "exec_analyze"),
|
||||
('text', "or"),
|
||||
('link', "process()", "process()", LinkType.FUNCTION, "exec_process"),
|
||||
])
|
||||
|
||||
utilities = create_paragraph_with_links([
|
||||
('text', "Utilities:"),
|
||||
('link', "validate()", "validate()", LinkType.FUNCTION, "exec_validate"),
|
||||
('text', "and"),
|
||||
('link', "cleanup()", "cleanup()", LinkType.FUNCTION, "exec_cleanup"),
|
||||
])
|
||||
|
||||
# Layout content
|
||||
layouter.layout_paragraph(title)
|
||||
layouter.layout_paragraph(intro)
|
||||
layouter.layout_paragraph(calculations)
|
||||
layouter.layout_paragraph(utilities)
|
||||
|
||||
return page
|
||||
|
||||
|
||||
def combine_pages_into_grid(pages, title):
|
||||
"""Combine multiple pages into a 2x2 grid."""
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
|
||||
print("\n Combining pages into grid...")
|
||||
|
||||
# Render all pages
|
||||
images = [page.render() for page in pages]
|
||||
|
||||
# Grid layout
|
||||
padding = 20
|
||||
title_height = 40
|
||||
cols = 2
|
||||
rows = 2
|
||||
|
||||
# Calculate dimensions
|
||||
img_width = images[0].size[0]
|
||||
img_height = images[0].size[1]
|
||||
|
||||
total_width = cols * img_width + (cols + 1) * padding
|
||||
total_height = rows * img_height + (rows + 1) * padding + title_height
|
||||
|
||||
# Create combined image
|
||||
combined = Image.new('RGB', (total_width, total_height), (240, 240, 240))
|
||||
draw = ImageDraw.Draw(combined)
|
||||
|
||||
# Draw title
|
||||
try:
|
||||
title_font = ImageFont.truetype(
|
||||
"/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 18
|
||||
)
|
||||
except:
|
||||
title_font = ImageFont.load_default()
|
||||
|
||||
# Center the title
|
||||
bbox = draw.textbbox((0, 0), title, font=title_font)
|
||||
text_width = bbox[2] - bbox[0]
|
||||
title_x = (total_width - text_width) // 2
|
||||
draw.text((title_x, 10), title, fill=(50, 50, 50), font=title_font)
|
||||
|
||||
# Place pages in grid
|
||||
y_offset = title_height + padding
|
||||
for row in range(rows):
|
||||
x_offset = padding
|
||||
for col in range(cols):
|
||||
idx = row * cols + col
|
||||
if idx < len(images):
|
||||
combined.paste(images[idx], (x_offset, y_offset))
|
||||
x_offset += img_width + padding
|
||||
y_offset += img_height + padding
|
||||
|
||||
return combined
|
||||
|
||||
|
||||
def main():
|
||||
"""Demonstrate link navigation across different link types."""
|
||||
global link_clicks
|
||||
link_clicks = []
|
||||
|
||||
print("Link Navigation Example")
|
||||
print("=" * 50)
|
||||
|
||||
# Create examples for each link type
|
||||
pages = [
|
||||
create_example_1_internal_links(),
|
||||
create_example_2_external_links(),
|
||||
create_example_3_api_links(),
|
||||
create_example_4_function_links()
|
||||
]
|
||||
|
||||
# Combine into demonstration image
|
||||
combined_image = combine_pages_into_grid(
|
||||
pages,
|
||||
"Link Types: Internal | External | API | Function"
|
||||
)
|
||||
|
||||
# Save output
|
||||
output_dir = Path("docs/images")
|
||||
output_dir.mkdir(parents=True, exist_ok=True)
|
||||
output_path = output_dir / "example_09_link_navigation.png"
|
||||
combined_image.save(output_path)
|
||||
|
||||
print("\n✓ Example completed!")
|
||||
print(f" Output saved to: {output_path}")
|
||||
print(f" Image size: {combined_image.size[0]}x{combined_image.size[1]} pixels")
|
||||
print(f" Created {len(pages)} link type examples")
|
||||
print(f" Total links created: {len(link_clicks)} callbacks registered")
|
||||
|
||||
return combined_image, link_clicks
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user