401 lines
14 KiB
Python
Executable File
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()
|