dreader-application/examples/library_reading_integration.py
2025-11-12 18:52:08 +00:00

401 lines
14 KiB
Python
Executable File

#!/usr/bin/env python3
"""
Integration demo: Library → Reading → Settings → Back to Library
This example demonstrates the complete LIBRARY ↔ READING mode transition workflow:
1. Display a library of EPUB files
2. Select a book by clicking/tapping
3. Open and read the selected book
4. Access settings overlay
5. Return to library from the settings overlay
6. Select another book
This demonstrates the full user flow for an e-reader application.
Usage:
python library_reading_integration.py path/to/library/directory
"""
import sys
import os
from pathlib import Path
# Add parent directory to path to import dreader
sys.path.insert(0, str(Path(__file__).parent.parent))
from dreader.library import LibraryManager
from dreader.application import EbookReader
from dreader.gesture import TouchEvent, GestureType, ActionType
def print_separator():
"""Print a visual separator."""
print("\n" + "="*70 + "\n")
def simulate_mode_transition_workflow(library_path: str):
"""
Simulate the complete workflow of library browsing and book reading.
Args:
library_path: Path to directory containing EPUB files
"""
print_separator()
print("INTEGRATION TEST: LIBRARY ↔ READING MODE TRANSITIONS")
print_separator()
# ===================================================================
# STEP 1: LIBRARY MODE - Display available books
# ===================================================================
print("STEP 1: LIBRARY MODE - Displaying available books")
print("-" * 70)
# Initialize library manager
library = LibraryManager(
library_path=library_path,
page_size=(800, 1200)
)
# Scan for books
books = library.scan_library()
print(f"✓ Found {len(books)} books in library")
if len(books) == 0:
print("Error: No EPUB files found in library directory")
print(f"Please add some .epub files to: {library_path}")
sys.exit(1)
# Display book list
for i, book in enumerate(books):
print(f" [{i}] {book['title']} by {book['author']}")
# Render library view
print("\nRendering library view...")
library_image = library.render_library()
library_image.save("integration_01_library.png")
print("✓ Saved library view to: integration_01_library.png")
# ===================================================================
# STEP 2: SIMULATE BOOK SELECTION - User taps on first book
# ===================================================================
print_separator()
print("STEP 2: BOOK SELECTION - Simulating tap on first book")
print("-" * 70)
# Simulate a tap on the first book row
# Row positions depend on rendering, but first book is typically near top
# We'll tap in the middle of the first book row area
tap_x, tap_y = 400, 150 # Approximate center of first book row
print(f"Simulating tap at ({tap_x}, {tap_y})...")
selected_book_path = library.handle_library_tap(tap_x, tap_y)
if not selected_book_path:
print("Warning: Tap didn't hit a book. Selecting first book directly...")
selected_book_path = books[0]['path']
print(f"✓ Selected book: {selected_book_path}")
# ===================================================================
# STEP 3: READING MODE - Open the selected book
# ===================================================================
print_separator()
print("STEP 3: READING MODE - Opening selected book")
print("-" * 70)
# Create reader
reader = EbookReader(
page_size=(800, 1200),
margin=40,
background_color=(255, 255, 255)
)
# Load the EPUB
print(f"Loading: {selected_book_path}")
success = reader.load_epub(selected_book_path)
if not success:
print("Error: Failed to load EPUB")
sys.exit(1)
print("✓ Book loaded successfully")
# Get book info
book_info = reader.get_book_info()
print(f" Title: {book_info['title']}")
print(f" Author: {book_info['author']}")
print(f" Chapters: {book_info['total_chapters']}")
# Render first page
print("\nRendering first page...")
page_image = reader.get_current_page()
page_image.save("integration_02_reading_page1.png")
print("✓ Saved first page to: integration_02_reading_page1.png")
# ===================================================================
# STEP 4: PAGE NAVIGATION - Turn some pages
# ===================================================================
print_separator()
print("STEP 4: PAGE NAVIGATION - Simulating page turns")
print("-" * 70)
# Simulate swipe left (next page)
print("Simulating SWIPE_LEFT (next page)...")
touch_event = TouchEvent(GestureType.SWIPE_LEFT, 600, 600)
response = reader.handle_touch(touch_event)
if response.action == ActionType.PAGE_TURN:
print(f"✓ Page turned: {response.data}")
page_image = reader.get_current_page()
page_image.save("integration_03_reading_page2.png")
print(" Saved to: integration_03_reading_page2.png")
# Turn another page
print("\nSimulating another SWIPE_LEFT...")
touch_event = TouchEvent(GestureType.SWIPE_LEFT, 600, 600)
response = reader.handle_touch(touch_event)
if response.action == ActionType.PAGE_TURN:
print(f"✓ Page turned: {response.data}")
# ===================================================================
# STEP 5: SETTINGS OVERLAY - Open and adjust settings
# ===================================================================
print_separator()
print("STEP 5: SETTINGS OVERLAY - Opening settings")
print("-" * 70)
# Open settings overlay
print("Opening settings overlay...")
overlay_image = reader.open_settings_overlay()
if overlay_image:
overlay_image.save("integration_04_settings_overlay.png")
print("✓ Settings overlay opened")
print(" Saved to: integration_04_settings_overlay.png")
# Simulate tapping "Increase Font Size" button
print("\nSimulating tap on 'Increase Font Size'...")
# The increase button is typically around y=250-280 in the overlay
tap_x, tap_y = 400, 270
touch_event = TouchEvent(GestureType.TAP, tap_x, tap_y)
response = reader.handle_touch(touch_event)
if response.action == ActionType.SETTING_CHANGED:
print(f"✓ Setting changed: {response.data}")
updated_overlay = reader.get_current_page()
updated_overlay.save("integration_05_settings_font_increased.png")
print(" Saved updated overlay to: integration_05_settings_font_increased.png")
# ===================================================================
# STEP 6: BACK TO LIBRARY - Use the new "Back to Library" button
# ===================================================================
print_separator()
print("STEP 6: BACK TO LIBRARY - Using 'Back to Library' button")
print("-" * 70)
# The settings overlay is 60% width x 70% height, centered
# For 800x1200: panel is 480x840, offset at (160, 180)
# The "Back to Library" button is near the bottom of the overlay panel
# Let's try scanning for it by querying multiple y-positions
print("Scanning for 'Back to Library' button...")
found_button = False
# Scan a wider range with finer granularity
# Settings overlay is 60% x 70% of 800x1200 = 480x840, centered at (160, 180)
# So overlay goes from y=180 to y=1020
# Button should be near bottom, scan from y=600 to y=1020
debug_results = []
for test_y in range(600, 1021, 20):
test_x = 400 # Center of screen
# Use the overlay manager's query method if there's an overlay open
if hasattr(reader, 'overlay_manager'):
result = reader.overlay_manager.query_overlay_pixel(test_x, test_y)
if result:
debug_results.append((test_y, result.get("link_target"), result.get("text", "")[:30]))
if result.get("is_interactive") and result.get("link_target"):
link = result["link_target"]
if link == "action:back_to_library":
print(f"✓ Found button at approximately ({test_x}, {test_y})")
tap_x, tap_y = test_x, test_y
found_button = True
break
if not found_button and debug_results:
print(f" Debug: Scanned {len(debug_results)} positions, found these links:")
for y, link, text in debug_results[-5:]: # Show last 5
if link:
print(f" y={y}: link={link}, text='{text}'")
if not found_button:
print(" Button not found via scan, using estimated position...")
# Fallback: overlay height is 840, centered at y=180
# Button is near bottom, approximately at panel_y + panel_height - 100
tap_x, tap_y = 400, 900
print(f"Simulating tap at ({tap_x}, {tap_y})...")
touch_event = TouchEvent(GestureType.TAP, tap_x, tap_y)
response = reader.handle_touch(touch_event)
if response.action == ActionType.BACK_TO_LIBRARY:
print("✓ BACK_TO_LIBRARY action received!")
print(" Application would now:")
print(" 1. Close the current book")
print(" 2. Return to library view")
print(" 3. Save reading position for resume")
# Save current position for resume
reader.save_position("__auto_resume__")
print("\n ✓ Auto-resume position saved")
# Close the reader
reader.close()
print(" ✓ Book closed")
# Re-render library
print("\n Re-rendering library view...")
library_image = library.render_library()
library_image.save("integration_06_back_to_library.png")
print(" ✓ Saved library view to: integration_06_back_to_library.png")
else:
print(f"Unexpected response: {response.action}")
print("Note: The button might be outside the overlay area or coordinates need adjustment")
# ===================================================================
# STEP 7: SELECT ANOTHER BOOK (if multiple books available)
# ===================================================================
if len(books) > 1:
print_separator()
print("STEP 7: SELECTING ANOTHER BOOK")
print("-" * 70)
# Select second book
second_book_path = books[1]['path']
print(f"Selecting second book: {second_book_path}")
# Create new reader instance
reader2 = EbookReader(
page_size=(800, 1200),
margin=40,
background_color=(255, 255, 255)
)
# Load second book
success = reader2.load_epub(second_book_path)
if success:
book_info = reader2.get_book_info()
print(f"✓ Loaded: {book_info['title']} by {book_info['author']}")
# Render first page
page_image = reader2.get_current_page()
page_image.save("integration_07_second_book.png")
print(" Saved to: integration_07_second_book.png")
reader2.close()
# ===================================================================
# STEP 8: RESUME PREVIOUS BOOK (demonstrate auto-resume)
# ===================================================================
print_separator()
print("STEP 8: AUTO-RESUME - Reopening first book at saved position")
print("-" * 70)
# Create new reader
reader3 = EbookReader(
page_size=(800, 1200),
margin=40,
background_color=(255, 255, 255)
)
# Load the book
print(f"Reloading: {selected_book_path}")
success = reader3.load_epub(selected_book_path)
if success:
# Load auto-resume position
print("Loading auto-resume position...")
page = reader3.load_position("__auto_resume__")
if page:
print("✓ Resumed at saved position!")
pos_info = reader3.get_position_info()
print(f" Progress: {pos_info['progress']*100:.1f}%")
page.save("integration_08_resumed_position.png")
print(" Saved to: integration_08_resumed_position.png")
else:
print("No saved position found (started from beginning)")
reader3.close()
# Cleanup
library.cleanup()
print_separator()
print("✓ INTEGRATION TEST COMPLETE!")
print_separator()
print("\nGenerated demonstration images:")
demo_files = [
"integration_01_library.png",
"integration_02_reading_page1.png",
"integration_03_reading_page2.png",
"integration_04_settings_overlay.png",
"integration_05_settings_font_increased.png",
"integration_06_back_to_library.png",
"integration_07_second_book.png",
"integration_08_resumed_position.png"
]
for filename in demo_files:
if os.path.exists(filename):
print(f"{filename}")
print("\nThis demonstrates the complete workflow:")
print(" 1. Library view with book selection")
print(" 2. Opening and reading a book")
print(" 3. Page navigation")
print(" 4. Settings overlay with adjustments")
print(" 5. Back to library transition")
print(" 6. Selecting another book")
print(" 7. Auto-resume functionality")
def main():
"""Main entry point."""
if len(sys.argv) < 2:
print("Usage: python library_reading_integration.py path/to/library/directory")
print("\nThis demo requires a directory containing EPUB files.")
print("\nExample:")
print(" mkdir my_library")
print(" cp tests/data/test.epub my_library/")
print(" cp tests/data/test2.epub my_library/")
print(" python library_reading_integration.py my_library/")
sys.exit(1)
library_path = sys.argv[1]
if not os.path.exists(library_path):
print(f"Error: Directory not found: {library_path}")
sys.exit(1)
if not os.path.isdir(library_path):
print(f"Error: Not a directory: {library_path}")
sys.exit(1)
try:
simulate_mode_transition_workflow(library_path)
except Exception as e:
print(f"\nError during integration test: {e}")
import traceback
traceback.print_exc()
sys.exit(1)
if __name__ == "__main__":
main()