PyPositron Docs

PositronWindowWrapper Class

The PositronWindowWrapper class is the main interface object returned by openUI(). It serves as a container that provides access to all PyPositron functionality, including DOM manipulation, window management, and Python-JavaScript integration. This object is passed as the ui parameter to your main function.

Overview

The PositronWindowWrapper acts as the central hub for your PyPositron application. Through this object, you can:

  • Access the DOM via the document property
  • Use browser window functionality via the htmlwindow property
  • Call exposed Python functions via the exposed property
  • Manage the execution context via the context property
  • Control the underlying webview window via the window property

Properties

Core Components

  • window: The underlying webview.Window object that provides low-level window control
  • context -> PositronContext: The PositronContext object for managing execution context, variables, and event handlers
  • document -> Document: The Document object for DOM manipulation and element selection
  • exposed -> ExposedFunctions: The ExposedFunctions object for interacting with Python functions exposed to JavaScript
  • htmlwindow -> HTMLWindow: The HTMLWindow object providing JavaScript window functionality (dialogs, timers, browser objects)

Threading

  • event_thread: The threading.Thread object responsible for handling events and maintaining the UI responsiveness

Usage Examples

Basic Application Structure

import py_positron

def main(ui):
    # ui is a PositronWindowWrapper instance
    
    # Access the document for DOM manipulation
    title = ui.document.getElementById("title")
    title.innerText = "Welcome to PyPositron!"
    
    # Use browser functionality
    ui.htmlwindow.alert("Application loaded successfully!")
    
    # Access exposed functions (if any were provided)
    if hasattr(ui, 'exposed'):
        # Use exposed functions here
        pass

py_positron.openUI("index.html", main=main)

Comprehensive Application Example

import py_positron
import json
import time

def save_data(data):
    """Example exposed function"""
    with open("app_data.json", "w") as f:
        json.dump(data, f)
    return "Data saved successfully"

def load_data():
    """Example exposed function"""
    try:
        with open("app_data.json", "r") as f:
            return json.load(f)
    except FileNotFoundError:
        return {}

def main(ui):
    # Initialize the application
    setup_ui(ui)
    setup_event_handlers(ui)
    load_saved_data(ui)

def setup_ui(ui):
    """Set up the user interface"""
    # Set page title
    ui.document.title = "PyPositron Data Manager"
    
    # Update header
    header = ui.document.getElementById("header")
    if header:
        header.innerText = "Data Management Application"
        header.style.textAlign = "center"
        header.style.color = "#333"
        header.style.padding = "20px"
    
    # Style the main container
    container = ui.document.getElementById("container")
    if container:
        container.style.maxWidth = "800px"
        container.style.margin = "0 auto"
        container.style.padding = "20px"

def setup_event_handlers(ui):
    """Set up event handlers for UI elements"""
    
    # Save button
    save_btn = ui.document.getElementById("saveBtn")
    if save_btn:
        def handle_save():
            try:
                # Get data from form
                data = collect_form_data(ui)
                
                # Use exposed function to save data
                message = ui.exposed.save_data(data)
                
                # Show success message
                ui.htmlwindow.alert(message)
                
                # Log to console
                ui.htmlwindow.console.log(f"Data saved: {data}")
                
            except Exception as e:
                ui.htmlwindow.alert(f"Error saving data: {str(e)}")
                ui.htmlwindow.console.error(f"Save error: {e}")
        
        save_btn.addEventListener("click", handle_save)
    
    # Load button
    load_btn = ui.document.getElementById("loadBtn")
    if load_btn:
        def handle_load():
            try:
                # Use exposed function to load data
                data = ui.exposed.load_data()
                
                # Populate form with loaded data
                populate_form_data(ui, data)
                
                # Show success message
                ui.htmlwindow.alert("Data loaded successfully!")
                
            except Exception as e:
                ui.htmlwindow.alert(f"Error loading data: {str(e)}")
        
        load_btn.addEventListener("click", handle_load)
    
    # Clear button
    clear_btn = ui.document.getElementById("clearBtn")
    if clear_btn:
        def handle_clear():
            if ui.htmlwindow.confirm("Are you sure you want to clear all data?"):
                clear_form_data(ui)
                ui.htmlwindow.console.log("Form data cleared")
        
        clear_btn.addEventListener("click", handle_clear)

def collect_form_data(ui):
    """Collect data from form elements"""
    data = {}
    
    # Text inputs
    name_input = ui.document.getElementById("nameInput")
    email_input = ui.document.getElementById("emailInput")
    
    if name_input:
        data["name"] = name_input.value
    
    if email_input:
        data["email"] = email_input.value
    
    # Select element
    category_select = ui.document.getElementById("categorySelect")
    if category_select:
        data["category"] = category_select.value
    
    # Checkboxes
    checkboxes = ui.document.getElementsByName("preferences")
    preferences = []
    for checkbox in checkboxes:
        if checkbox.checked:
            preferences.append(checkbox.value)
    data["preferences"] = preferences
    
    # Add timestamp
    data["timestamp"] = time.time()
    
    return data

def populate_form_data(ui, data):
    """Populate form elements with data"""
    if not data:
        return
    
    # Text inputs
    if "name" in data:
        name_input = ui.document.getElementById("nameInput")
        if name_input:
            name_input.value = data["name"]
    
    if "email" in data:
        email_input = ui.document.getElementById("emailInput")
        if email_input:
            email_input.value = data["email"]
    
    # Select element
    if "category" in data:
        category_select = ui.document.getElementById("categorySelect")
        if category_select:
            category_select.value = data["category"]
    
    # Checkboxes
    if "preferences" in data:
        checkboxes = ui.document.getElementsByName("preferences")
        for checkbox in checkboxes:
            checkbox.checked = checkbox.value in data["preferences"]

def clear_form_data(ui):
    """Clear all form data"""
    # Text inputs
    inputs = ui.document.getElementsByTagName("input")
    for input_field in inputs:
        if input_field.getAttribute("type") in ["text", "email", "password"]:
            input_field.value = ""
        elif input_field.getAttribute("type") == "checkbox":
            input_field.checked = False
    
    # Select elements
    selects = ui.document.getElementsByTagName("select")
    for select in selects:
        select.selectedIndex = 0

def load_saved_data(ui):
    """Load saved data on application startup"""
    try:
        data = ui.exposed.load_data()
        if data:
            populate_form_data(ui, data)
            ui.htmlwindow.console.log("Saved data loaded on startup")
    except Exception as e:
        ui.htmlwindow.console.warn(f"Could not load saved data: {e}")

def on_window_close(ui):
    """Called when the window is about to close"""
    ui.htmlwindow.console.log("Application closing...")
    
    # Optionally auto-save data before closing
    try:
        data = collect_form_data(ui)
        if any(data.values()):  # If there's any data to save
            ui.exposed.save_data(data)
            print("Data auto-saved before closing")
    except Exception as e:
        print(f"Error auto-saving data: {e}")

# Launch the application
py_positron.openUI(
    "data_manager.html",
    main=main,
    after_close=on_window_close,
    title="PyPositron Data Manager",
    width=900,
    height=700,
    functions=[save_data, load_data]
)

Window Management Example

import py_positron

def main(ui):
    # Window control buttons
    setup_window_controls(ui)
    
    # Display window information
    display_window_info(ui)
    
    # Window event handlers
    setup_window_events(ui)

def setup_window_controls(ui):
    """Set up window control buttons"""
    
    # Minimize button
    minimize_btn = ui.document.getElementById("minimizeBtn")
    if minimize_btn:
        def minimize_window():
            # Access the underlying webview window
            ui.window.minimize()
        
        minimize_btn.addEventListener("click", minimize_window)
    
    # Maximize/Restore button
    maximize_btn = ui.document.getElementById("maximizeBtn")
    if maximize_btn:
        is_maximized = False
        
        def toggle_maximize():
            nonlocal is_maximized
            if is_maximized:
                ui.window.restore()
                maximize_btn.innerText = "Maximize"
                is_maximized = False
            else:
                ui.window.maximize()
                maximize_btn.innerText = "Restore"
                is_maximized = True
        
        maximize_btn.addEventListener("click", toggle_maximize)
    
    # Close button
    close_btn = ui.document.getElementById("closeBtn")
    if close_btn:
        def close_window():
            if ui.htmlwindow.confirm("Are you sure you want to close the application?"):
                ui.window.destroy()
        
        close_btn.addEventListener("click", close_window)

def display_window_info(ui):
    """Display information about the window"""
    info_div = ui.document.getElementById("windowInfo")
    if info_div:
        info_html = f"""
        <h3>Window Information</h3>
        <p><strong>Title:</strong> {ui.document.title}</p>
        <p><strong>Inner Size:</strong> {ui.htmlwindow.innerWidth} x {ui.htmlwindow.innerHeight}</p>
        <p><strong>Outer Size:</strong> {ui.htmlwindow.outerWidth} x {ui.htmlwindow.outerHeight}</p>
        <p><strong>Position:</strong> ({ui.htmlwindow.screenX}, {ui.htmlwindow.screenY})</p>
        """
        info_div.innerHTML = info_html

def setup_window_events(ui):
    """Set up window event handlers"""
    
    def on_resize():
        # Update window info when resized
        display_window_info(ui)
        ui.htmlwindow.console.log("Window resized")
    
    def on_focus():
        ui.htmlwindow.console.log("Window gained focus")
        # You could update UI to show active state
    
    def on_blur():
        ui.htmlwindow.console.log("Window lost focus")
        # You could update UI to show inactive state
    
    # Add event listeners
    ui.htmlwindow.addEventListener("resize", on_resize)
    ui.htmlwindow.addEventListener("focus", on_focus)
    ui.htmlwindow.addEventListener("blur", on_blur)

py_positron.openUI(
    "window_manager.html",
    main=main,
    title="Window Management Demo",
    width=800,
    height=600
)

Context and Execution Example

import py_positron

def main(ui):
    # Demonstrate context usage
    demonstrate_context_features(ui)

def demonstrate_context_features(ui):
    """Demonstrate PositronContext features"""
    
    # Access the context
    context = ui.context
    
    # Set global variables that can be accessed in <py> tags
    context.globals['app_name'] = 'PyPositron Demo'
    context.globals['version'] = '1.0.0'
    context.globals['user_data'] = {'name': 'User', 'role': 'Developer'}
    
    # Register a custom event handler
    def custom_handler():
        ui.htmlwindow.alert("Custom event triggered!")
        ui.htmlwindow.console.log("Custom handler executed")
    
    # Store the handler in context
    context.event_handlers['custom_event'] = custom_handler
    
    # Set up a button to trigger the custom event
    trigger_btn = ui.document.getElementById("triggerBtn")
    if trigger_btn:
        trigger_btn.addEventListener("click", custom_handler)
    
    # Execute Python code dynamically
    execute_btn = ui.document.getElementById("executeBtn")
    if execute_btn:
        def execute_dynamic_code():
            code_input = ui.document.getElementById("codeInput")
            if code_input and code_input.value.strip():
                code = code_input.value
                
                try:
                    # Execute the code in the PyPositron context
                    success, error = context.execute(code)
                    
                    result_div = ui.document.getElementById("result")
                    if success:
                        result_div.innerText = "Code executed successfully!"
                        result_div.style.color = "green"
                        ui.htmlwindow.console.log(f"Executed: {code}")
                    else:
                        result_div.innerText = f"Error: {error}"
                        result_div.style.color = "red"
                        ui.htmlwindow.console.error(f"Execution error: {error}")
                        
                except Exception as e:
                    ui.htmlwindow.alert(f"Execution failed: {str(e)}")
        
        execute_btn.addEventListener("click", execute_dynamic_code)
    
    # Display context information
    context_info = ui.document.getElementById("contextInfo")
    if context_info:
        info_html = f"""
        <h3>Context Information</h3>
        <p><strong>Global Variables:</strong></p>
        <ul>
            <li>app_name: {context.globals.get('app_name', 'Not set')}</li>
            <li>version: {context.globals.get('version', 'Not set')}</li>
            <li>user_data: {context.globals.get('user_data', 'Not set')}</li>
        </ul>
        <p><strong>Event Handlers:</strong> {len(context.event_handlers)} registered</p>
        <p><strong>Exposed Functions:</strong> {len(context.exposed_functions)} available</p>
        """
        context_info.innerHTML = info_html

py_positron.openUI(
    "context_demo.html",
    main=main,
    title="Context and Execution Demo",
    width=900,
    height=700
)

Properties Deep Dive

window

The window property provides access to the underlying webview.Window object, which offers low-level window control:

def main(ui):
    # Window operations
    ui.window.minimize()
    ui.window.maximize() 
    ui.window.restore()
    ui.window.destroy()
    
    # Window properties (may vary by backend)
    # ui.window.width
    # ui.window.height
    # ui.window.x
    # ui.window.y

context

The context property provides access to the execution environment:

def main(ui):
    # Access global and local variables
    ui.context.globals['my_var'] = 'Hello World'
    ui.context.locals['local_var'] = 42
    
    # Execute Python code
    success, error = ui.context.execute("print('Hello from context!')")
    
    # Register event handlers
    ui.context.event_handlers['my_event'] = my_handler_function

document

The document property is your main interface for DOM manipulation:

def main(ui):
    # Element selection
    element = ui.document.getElementById("myElement")
    elements = ui.document.getElementsByClassName("myClass")
    
    # Element creation
    new_element = ui.document.createElement("div")
    
    # Document properties
    ui.document.title = "New Title"
    body = ui.document.body

exposed

The exposed property provides access to Python functions exposed to JavaScript:

def my_function(x, y):
    return x + y

def main(ui):
    # Call exposed functions
    result = ui.exposed.my_function(5, 3)
    print(result)  # 8

py_positron.openUI("app.html", main=main, functions=[my_function])

htmlwindow

The htmlwindow property provides browser window functionality:

def main(ui):
    # Dialogs
    ui.htmlwindow.alert("Hello!")
    result = ui.htmlwindow.confirm("Continue?")
    name = ui.htmlwindow.prompt("Your name?")
    
    # Timers
    timer_id = ui.htmlwindow.setTimeout(my_callback, 1000)
    ui.htmlwindow.clearTimeout(timer_id)
    
    # Browser objects
    console = ui.htmlwindow.console
    navigator = ui.htmlwindow.navigator
    location = ui.htmlwindow.location

Best Practices

  1. Cache references: Store frequently used objects in variables:

    def main(ui):
        doc = ui.document
        window = ui.htmlwindow
        # Use doc and window instead of ui.document and ui.htmlwindow
    
  2. Error handling: Always handle potential errors when accessing UI components:

    def main(ui):
        element = ui.document.getElementById("myElement")
        if element:
            element.innerText = "Found!"
        else:
            ui.htmlwindow.console.warn("Element not found")
    
  3. Clean separation: Keep different concerns separate:

    def main(ui):
        setup_ui(ui)        # UI setup
        setup_events(ui)    # Event handlers
        load_data(ui)       # Data loading
    
  4. Use context wisely: Don't overuse the context for storing application state:

    def main(ui):
        # Good: Store configuration
        ui.context.globals['config'] = load_config()
        
        # Avoid: Storing complex application state
        # Use proper Python variables instead
    

Related Documentation