This commit is contained in:
parent
11dc30ba8d
commit
4811367905
@ -706,6 +706,64 @@ class EbookReader:
|
||||
'font_scale': self.base_font_scale
|
||||
}
|
||||
|
||||
# ===== Settings Persistence =====
|
||||
|
||||
def get_current_settings(self) -> Dict[str, Any]:
|
||||
"""
|
||||
Get current rendering settings.
|
||||
|
||||
Returns:
|
||||
Dictionary with all current settings
|
||||
"""
|
||||
return {
|
||||
'font_scale': self.base_font_scale,
|
||||
'line_spacing': self.page_style.line_spacing if self.manager else 5,
|
||||
'inter_block_spacing': self.page_style.inter_block_spacing if self.manager else 15,
|
||||
'word_spacing': self.page_style.word_spacing if self.manager else 0
|
||||
}
|
||||
|
||||
def apply_settings(self, settings: Dict[str, Any]) -> bool:
|
||||
"""
|
||||
Apply rendering settings from a settings dictionary.
|
||||
|
||||
This should be called after loading a book to restore user preferences.
|
||||
|
||||
Args:
|
||||
settings: Dictionary with settings (font_scale, line_spacing, etc.)
|
||||
|
||||
Returns:
|
||||
True if settings applied successfully, False otherwise
|
||||
"""
|
||||
if not self.manager:
|
||||
return False
|
||||
|
||||
try:
|
||||
# Apply font scale
|
||||
font_scale = settings.get('font_scale', 1.0)
|
||||
if font_scale != self.base_font_scale:
|
||||
self.set_font_size(font_scale)
|
||||
|
||||
# Apply line spacing
|
||||
line_spacing = settings.get('line_spacing', 5)
|
||||
if line_spacing != self.page_style.line_spacing:
|
||||
self.set_line_spacing(line_spacing)
|
||||
|
||||
# Apply inter-block spacing
|
||||
inter_block_spacing = settings.get('inter_block_spacing', 15)
|
||||
if inter_block_spacing != self.page_style.inter_block_spacing:
|
||||
self.set_inter_block_spacing(inter_block_spacing)
|
||||
|
||||
# Apply word spacing
|
||||
word_spacing = settings.get('word_spacing', 0)
|
||||
if word_spacing != self.page_style.word_spacing:
|
||||
self.set_word_spacing(word_spacing)
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error applying settings: {e}")
|
||||
return False
|
||||
|
||||
# ===== Gesture Handling =====
|
||||
# All business logic for touch input is handled here
|
||||
|
||||
@ -1066,6 +1124,16 @@ class EbookReader:
|
||||
"word_spacing": self.page_style.word_spacing
|
||||
})
|
||||
|
||||
# Parse "action:command" format for other actions
|
||||
elif link_target.startswith("action:"):
|
||||
action = link_target.split(":", 1)[1]
|
||||
|
||||
if action == "back_to_library":
|
||||
# Close the overlay first
|
||||
self.close_overlay()
|
||||
# Return a special action for the application to handle
|
||||
return GestureResponse(ActionType.BACK_TO_LIBRARY, {})
|
||||
|
||||
# Not a setting control, close overlay
|
||||
self.close_overlay()
|
||||
return GestureResponse(ActionType.OVERLAY_CLOSED, {})
|
||||
|
||||
@ -126,3 +126,4 @@ class ActionType:
|
||||
OVERLAY_CLOSED = "overlay_closed"
|
||||
CHAPTER_SELECTED = "chapter_selected"
|
||||
SETTING_CHANGED = "setting_changed"
|
||||
BACK_TO_LIBRARY = "back_to_library"
|
||||
|
||||
@ -278,6 +278,12 @@ def generate_settings_overlay(
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div style="margin: 20px 0;">
|
||||
<p style="padding: 15px; margin: 5px 0; background-color: #dc3545; text-align: center; border-radius: 5px;">
|
||||
<a href="action:back_to_library" style="text-decoration: none; color: white; font-weight: bold; font-size: 14px;">◄ Back to Library</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<p style="text-align: center; margin: 15px 0 0 0; padding-top: 12px;
|
||||
border-top: 2px solid #ccc; color: #888; font-size: 11px;">
|
||||
Changes apply in real-time • Tap outside to close
|
||||
|
||||
@ -78,10 +78,11 @@ class LibraryState:
|
||||
|
||||
@dataclass
|
||||
class Settings:
|
||||
"""User settings"""
|
||||
"""User settings for rendering and display"""
|
||||
font_scale: float = 1.0
|
||||
line_spacing: int = 5
|
||||
inter_block_spacing: int = 15
|
||||
word_spacing: int = 0 # Default word spacing
|
||||
brightness: int = 8
|
||||
theme: str = "day"
|
||||
|
||||
@ -96,6 +97,7 @@ class Settings:
|
||||
font_scale=data.get('font_scale', 1.0),
|
||||
line_spacing=data.get('line_spacing', 5),
|
||||
inter_block_spacing=data.get('inter_block_spacing', 15),
|
||||
word_spacing=data.get('word_spacing', 0),
|
||||
brightness=data.get('brightness', 8),
|
||||
theme=data.get('theme', 'day')
|
||||
)
|
||||
@ -374,6 +376,18 @@ class StateManager:
|
||||
setattr(self.state.settings, key, value)
|
||||
self._dirty = True
|
||||
|
||||
def update_settings(self, settings_dict: Dict[str, Any]):
|
||||
"""
|
||||
Update multiple settings at once.
|
||||
|
||||
Args:
|
||||
settings_dict: Dictionary with setting keys and values
|
||||
"""
|
||||
for key, value in settings_dict.items():
|
||||
if hasattr(self.state.settings, key):
|
||||
setattr(self.state.settings, key, value)
|
||||
self._dirty = True
|
||||
|
||||
def get_library_state(self) -> LibraryState:
|
||||
"""Get library state"""
|
||||
return self.state.library
|
||||
|
||||
400
examples/library_reading_integration.py
Executable file
400
examples/library_reading_integration.py
Executable file
@ -0,0 +1,400 @@
|
||||
#!/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()
|
||||
171
examples/persistent_settings_example.py
Executable file
171
examples/persistent_settings_example.py
Executable file
@ -0,0 +1,171 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Example demonstrating persistent rendering settings.
|
||||
|
||||
This shows how to:
|
||||
1. Initialize StateManager to load saved settings
|
||||
2. Apply saved settings to EbookReader
|
||||
3. Modify settings during reading
|
||||
4. Save settings automatically for next session
|
||||
|
||||
The settings (font size, line spacing, etc.) will persist between
|
||||
application sessions, so the user doesn't have to reconfigure each time.
|
||||
"""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# Add parent directory to path
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent))
|
||||
|
||||
from dreader import EbookReader
|
||||
from dreader.state import StateManager, Settings
|
||||
|
||||
|
||||
def demonstrate_persistent_settings():
|
||||
"""Show how settings persist across sessions"""
|
||||
|
||||
print("=" * 70)
|
||||
print("Persistent Settings Example")
|
||||
print("=" * 70)
|
||||
|
||||
# 1. Initialize state manager (loads saved state from disk)
|
||||
state_file = Path.home() / ".config" / "dreader" / "state.json"
|
||||
state_file.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
state_manager = StateManager(state_file=state_file)
|
||||
state = state_manager.load_state()
|
||||
|
||||
print(f"\nLoaded settings from: {state_file}")
|
||||
print(f" Font scale: {state.settings.font_scale}")
|
||||
print(f" Line spacing: {state.settings.line_spacing}px")
|
||||
print(f" Inter-block spacing: {state.settings.inter_block_spacing}px")
|
||||
print(f" Word spacing: {state.settings.word_spacing}px")
|
||||
|
||||
# 2. Create reader with saved settings
|
||||
reader = EbookReader(
|
||||
page_size=(800, 1000),
|
||||
line_spacing=state.settings.line_spacing,
|
||||
inter_block_spacing=state.settings.inter_block_spacing
|
||||
)
|
||||
|
||||
# Load a book
|
||||
epub_dir = Path(__file__).parent.parent / 'tests' / 'data' / 'library-epub'
|
||||
epubs = list(epub_dir.glob('*.epub'))
|
||||
|
||||
if not epubs:
|
||||
print("\nError: No test EPUB files found!")
|
||||
print(f"Looked in: {epub_dir}")
|
||||
return
|
||||
|
||||
epub_path = epubs[0]
|
||||
print(f"\nLoading book: {epub_path.name}")
|
||||
|
||||
if not reader.load_epub(str(epub_path)):
|
||||
print("Failed to load book!")
|
||||
return
|
||||
|
||||
print(f"Loaded: {reader.book_title} by {reader.book_author}")
|
||||
|
||||
# 3. Apply saved settings to the book
|
||||
print("\nApplying saved settings to book...")
|
||||
settings_dict = state.settings.to_dict()
|
||||
reader.apply_settings(settings_dict)
|
||||
|
||||
# Render initial page
|
||||
print("\nRendering page with saved settings...")
|
||||
page = reader.get_current_page()
|
||||
reader.render_to_file("persistent_settings_before.png")
|
||||
print("✓ Saved: persistent_settings_before.png")
|
||||
|
||||
# 4. Simulate user changing settings
|
||||
print("\n" + "=" * 70)
|
||||
print("User adjusts settings...")
|
||||
print("=" * 70)
|
||||
|
||||
# Increase font size
|
||||
print("\n1. Increasing font size...")
|
||||
reader.increase_font_size()
|
||||
reader.increase_font_size()
|
||||
print(f" New font scale: {reader.base_font_scale}")
|
||||
|
||||
# Increase line spacing
|
||||
print("2. Increasing line spacing...")
|
||||
new_line_spacing = state.settings.line_spacing + 4
|
||||
reader.set_line_spacing(new_line_spacing)
|
||||
print(f" New line spacing: {new_line_spacing}px")
|
||||
|
||||
# Increase word spacing
|
||||
print("3. Increasing word spacing...")
|
||||
new_word_spacing = state.settings.word_spacing + 3
|
||||
reader.set_word_spacing(new_word_spacing)
|
||||
print(f" New word spacing: {new_word_spacing}px")
|
||||
|
||||
# Render page with new settings
|
||||
print("\nRendering page with new settings...")
|
||||
page = reader.get_current_page()
|
||||
reader.render_to_file("persistent_settings_after.png")
|
||||
print("✓ Saved: persistent_settings_after.png")
|
||||
|
||||
# 5. Save new settings to state
|
||||
print("\n" + "=" * 70)
|
||||
print("Saving settings for next session...")
|
||||
print("=" * 70)
|
||||
|
||||
current_settings = reader.get_current_settings()
|
||||
state_manager.update_settings(current_settings)
|
||||
|
||||
print(f"\nSettings to be saved:")
|
||||
print(f" Font scale: {current_settings['font_scale']}")
|
||||
print(f" Line spacing: {current_settings['line_spacing']}px")
|
||||
print(f" Inter-block spacing: {current_settings['inter_block_spacing']}px")
|
||||
print(f" Word spacing: {current_settings['word_spacing']}px")
|
||||
|
||||
# Save state to disk
|
||||
if state_manager.save_state():
|
||||
print(f"\n✓ Settings saved to: {state_file}")
|
||||
print(" These settings will be used the next time you open a book!")
|
||||
else:
|
||||
print("\n✗ Failed to save settings")
|
||||
|
||||
# 6. Demonstrate that settings are saved
|
||||
print("\n" + "=" * 70)
|
||||
print("Verification: Reloading state from disk...")
|
||||
print("=" * 70)
|
||||
|
||||
# Create new state manager to verify persistence
|
||||
verification_manager = StateManager(state_file=state_file)
|
||||
verification_state = verification_manager.load_state()
|
||||
|
||||
print(f"\nVerified saved settings:")
|
||||
print(f" Font scale: {verification_state.settings.font_scale}")
|
||||
print(f" Line spacing: {verification_state.settings.line_spacing}px")
|
||||
print(f" Inter-block spacing: {verification_state.settings.inter_block_spacing}px")
|
||||
print(f" Word spacing: {verification_state.settings.word_spacing}px")
|
||||
|
||||
if (verification_state.settings.font_scale == current_settings['font_scale'] and
|
||||
verification_state.settings.line_spacing == current_settings['line_spacing'] and
|
||||
verification_state.settings.word_spacing == current_settings['word_spacing']):
|
||||
print("\n✓ Settings successfully persisted!")
|
||||
else:
|
||||
print("\n✗ Settings mismatch!")
|
||||
|
||||
# Cleanup
|
||||
reader.close()
|
||||
|
||||
print("\n" + "=" * 70)
|
||||
print("Demo Complete!")
|
||||
print("=" * 70)
|
||||
print("\nKey Points:")
|
||||
print(" • Settings are automatically loaded from ~/.config/dreader/state.json")
|
||||
print(" • Use reader.apply_settings() to apply saved settings after loading a book")
|
||||
print(" • Use reader.get_current_settings() to get current settings")
|
||||
print(" • Use state_manager.update_settings() to save new settings")
|
||||
print(" • Settings persist across application restarts")
|
||||
print("\nGenerated files:")
|
||||
print(" • persistent_settings_before.png - Page with original settings")
|
||||
print(" • persistent_settings_after.png - Page with modified settings")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
demonstrate_persistent_settings()
|
||||
Loading…
x
Reference in New Issue
Block a user