#!/usr/bin/env python3 """ Enhanced HTML Browser using pyWebLayout with Viewport System This browser uses the new viewport system to enable efficient scrolling within HTML pages, only rendering the visible portion of large documents. """ import re import tkinter as tk from tkinter import ttk, messagebox, filedialog, simpledialog from PIL import Image, ImageTk, ImageDraw from typing import Dict, List, Optional, Tuple, Any import webbrowser import os from urllib.parse import urljoin, urlparse import requests from io import BytesIO import pyperclip # Import pyWebLayout components including the new viewport system from pyWebLayout.concrete import ( Page, Container, Box, Text, RenderableImage, RenderableLink, RenderableButton, RenderableForm, RenderableFormField, Viewport, ScrollablePageContent ) from pyWebLayout.abstract.functional import ( Link, Button, Form, FormField, LinkType, FormFieldType ) from pyWebLayout.abstract.block import Paragraph from pyWebLayout.abstract.inline import Word from pyWebLayout.style.fonts import Font, FontWeight, FontStyle, TextDecoration from pyWebLayout.style.layout import Alignment from pyWebLayout.typesetting.paragraph_layout import ParagraphLayout, ParagraphLayoutResult from pyWebLayout.io.readers.html_extraction import parse_html_string class HTMLViewportAdapter: """Adapter to convert HTML to viewport using the proper HTML extraction system""" def __init__(self): pass def parse_html_string(self, html_content: str, base_url: str = "", viewport_size: Tuple[int, int] = (800, 600)) -> Viewport: """Parse HTML string and return a Viewport object with scrollable content using the proper parser""" # Use the proper HTML extraction system base_font = Font(font_size=14) blocks = parse_html_string(html_content, base_font) # Extract title title_match = re.search(r'
This enhanced browser uses the new viewport system for efficient scrolling through large documents.
Mouse Wheel: Scroll up and down
Page Up/Down: Scroll by viewport height
Home/End: Jump to top/bottom
Arrow Keys: Scroll line by line
Scrollbar: Click and drag for precise positioning
Click and drag to select text, then use Ctrl+C to copy
Use Ctrl+A to select all visible text
This page demonstrates the viewport system. All the content above and below is efficiently managed.
Here's some additional content to demonstrate scrolling capabilities:
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium.
Totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos.
Qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet.
Consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.
The viewport system works by:
This allows handling of very large documents without performance issues.
Use the "Open File" button to load local HTML files, or enter a URL in the address bar.
""" # Get current canvas size for viewport self.root.update_idletasks() canvas_width = max(800, self.canvas.winfo_width()) canvas_height = max(600, self.canvas.winfo_height()) parser = HTMLViewportAdapter() self.current_viewport = parser.parse_html_string(html_content, viewport_size=(canvas_width, canvas_height)) # Update window title if hasattr(self.current_viewport, 'title'): self.root.title(f"pyWebLayout Browser - {self.current_viewport.title}") self.update_viewport_display() self.status_var.set("Welcome page loaded with viewport system") def navigate_to_url(self, event=None): """Navigate to the URL in the address bar""" url = self.url_var.get().strip() if not url: return self.status_var.set(f"Loading {url}...") self.root.update() # Get current canvas size for viewport self.root.update_idletasks() canvas_width = max(800, self.canvas.winfo_width()) canvas_height = max(600, self.canvas.winfo_height()) try: parser = HTMLViewportAdapter() if url.startswith(('http://', 'https://')): # Web URL response = requests.get(url, timeout=10) response.raise_for_status() html_content = response.text self.current_viewport = parser.parse_html_string(html_content, url, (canvas_width, canvas_height)) elif os.path.isfile(url): # Local file self.current_viewport = parser.parse_html_file(url, (canvas_width, canvas_height)) else: # Try to treat as a local file path if not url.startswith('file://'): url = 'file://' + os.path.abspath(url) file_path = url.replace('file://', '') if os.path.isfile(file_path): self.current_viewport = parser.parse_html_file(file_path, (canvas_width, canvas_height)) else: raise FileNotFoundError(f"File not found: {file_path}") # Update window title if hasattr(self.current_viewport, 'title'): self.root.title(f"pyWebLayout Browser - {self.current_viewport.title}") # Add to history self.add_to_history(url) self.update_viewport_display() self.status_var.set(f"Loaded {url}") except Exception as e: self.status_var.set(f"Error loading {url}: {str(e)}") messagebox.showerror("Error", f"Failed to load {url}:\n{str(e)}") def open_file(self): """Open a local HTML file""" file_path = filedialog.askopenfilename( title="Open HTML File", filetypes=[("HTML files", "*.html *.htm"), ("All files", "*.*")] ) if file_path: self.url_var.set(file_path) self.navigate_to_url() def on_click(self, event): """Handle mouse clicks on the canvas""" if not self.current_viewport: return # Convert canvas coordinates to viewport coordinates canvas_x = self.canvas.canvasx(event.x) canvas_y = self.canvas.canvasy(event.y) # Use viewport hit testing hit_element = self.current_viewport.hit_test((canvas_x, canvas_y)) if hit_element and hasattr(hit_element, '_callback'): # Handle clickable elements try: result = hit_element._callback() if result: self.status_var.set(result) # For external links, open in system browser if hasattr(hit_element, '_link') and hit_element._link.link_type == LinkType.EXTERNAL: webbrowser.open(hit_element._link.location) except Exception as e: self.status_var.set(f"Click error: {str(e)}") def on_mouse_move(self, event): """Handle mouse movement for hover effects""" if not self.current_viewport: return # Convert canvas coordinates to viewport coordinates canvas_x = self.canvas.canvasx(event.x) canvas_y = self.canvas.canvasy(event.y) # Check if mouse is over any clickable element hit_element = self.current_viewport.hit_test((canvas_x, canvas_y)) if hit_element and hasattr(hit_element, '_callback'): self.canvas.configure(cursor="hand2") else: self.canvas.configure(cursor="arrow") def add_to_history(self, url): """Add URL to navigation history""" # Remove any forward history self.history = self.history[:self.history_index + 1] # Add new URL self.history.append(url) self.history_index = len(self.history) - 1 # Update navigation buttons self.update_nav_buttons() def update_nav_buttons(self): """Update the state of navigation buttons""" self.back_btn.configure(state=tk.NORMAL if self.history_index > 0 else tk.DISABLED) self.forward_btn.configure(state=tk.NORMAL if self.history_index < len(self.history) - 1 else tk.DISABLED) def go_back(self): """Navigate back in history""" if self.history_index > 0: self.history_index -= 1 url = self.history[self.history_index] self.url_var.set(url) self.navigate_to_url() def go_forward(self): """Navigate forward in history""" if self.history_index < len(self.history) - 1: self.history_index += 1 url = self.history[self.history_index] self.url_var.set(url) self.navigate_to_url() def refresh(self): """Refresh the current page""" current_url = self.url_var.get() if current_url: self.navigate_to_url() else: self.load_default_page() def run(self): """Start the browser""" self.root.mainloop() def main(): """Main function to run the enhanced browser""" print("Starting pyWebLayout HTML Browser with Viewport System...") try: browser = ViewportBrowserWindow() browser.run() except Exception as e: print(f"Error starting browser: {e}") import traceback traceback.print_exc() if __name__ == "__main__": main()