app.show() is the primary entry point for rendering content in PyWry. It accepts HTML (as a string or HtmlContent object), determines the correct rendering path for the current environment, and returns a handle you can use to interact with the result.
from pywry import PyWry
app = PyWry()
# Simplest form — just a string of HTML
app.show("<h1>Hello, world!</h1>")This single call does everything: it detects whether you're in a terminal, a Jupyter notebook, or browser mode — then opens a native window, renders an anywidget, or starts a server accordingly.
app.show(
content, # str | HtmlContent — the HTML to render
title=None, # str — window title (native mode)
width=None, # int | str — width in pixels (int) or CSS value (str, e.g. "60%")
height=None, # int — height in pixels
callbacks=None, # dict[str, Callable] — event handlers
include_plotly=False, # bool — load the Plotly.js library
include_aggrid=False, # bool — load the AG Grid library
aggrid_theme="alpine", # str — AG Grid theme name
label=None, # str — window label for multi-window targeting
watch=None, # bool — enable hot reload for CSS/JS files
toolbars=None, # list[Toolbar | dict] — toolbar configurations
modals=None, # list[Modal | dict] — modal dialog configurations
)Accepts either a plain HTML string or an HtmlContent object. When you pass a string, PyWry wraps it in a minimal document structure automatically. When you pass HtmlContent, you get control over CSS files, JS files, inline styles, JSON data injection, and hot reload watching. See the HtmlContent guide for details.
# Plain string — PyWry wraps it in a full HTML document
app.show("<div id='app'>Hello</div>")
# HtmlContent — full control over assets
from pywry import HtmlContent
content = HtmlContent(
html="<div id='app'>Hello</div>",
css_files=["styles/app.css"],
script_files=["scripts/app.js"],
inline_css="body { background: #1a1a2e; color: white; }",
json_data={"items": [1, 2, 3]},
watch=True,
)
app.show(content)Sets the title bar text in native window mode. Defaults to "PyWry". Ignored in notebook and browser modes.
app.show("<h1>Dashboard</h1>", title="Sales Dashboard")In native mode, width accepts an integer (pixels) for the OS window size. In notebook mode, width can also be a CSS string like "100%" or "500px". height is always an integer (pixels).
# Native window: 800×600 pixels
app.show(html, width=800, height=600)
# Notebook: full-width, 400px tall
app.show(html, width="100%", height=400)Defaults come from WindowConfig — 1280×720 unless overridden in your configuration.
A dictionary mapping event names to Python callback functions. These are registered before the content is rendered, so they're ready to receive events immediately.
def on_click(data, event_type, label):
selected_point = data.get("points", [{}])[0]
app.emit("pywry:set-content", {"id": "info", "text": f"x={selected_point.get('x')}"}, label)
def on_save(data, event_type, label):
app.emit("pywry:download", {"filename": "data.json", "content": "{}"}, label)
app.show(html, callbacks={
"plotly:click": on_click,
"app:save": on_save,
})See the Event System guide for the full callback signature and registration patterns.
When True, PyWry injects the Plotly.js or AG Grid JavaScript and CSS libraries into the page. You don't need to set these manually if you use app.show_plotly() or app.show_dataframe() — they set the flags automatically.
# Manual: include Plotly when using raw HTML with Plotly
app.show(plotly_html, include_plotly=True)
# Automatic: show_plotly() handles this for you
app.show_plotly(fig)In MULTI_WINDOW mode, the label identifies which window to create or update. In SINGLE_WINDOW mode, the label is fixed and this parameter is ignored. Labels are used in callbacks to target events back to a specific window.
from pywry import PyWry, WindowMode
app = PyWry(mode=WindowMode.MULTI_WINDOW)
app.show(chart_html, label="chart")
app.show(table_html, label="table")
# Later, inside a callback:
def on_filter(data, event_type, label):
app.emit("app:update", {"filter": data}, "chart") # target the chart windowWhen True, PyWry watches the CSS and JS files referenced by HtmlContent.css_files and HtmlContent.script_files for changes, and live-reloads them in the browser without a full page refresh.
content = HtmlContent(
html="<div>Dashboard</div>",
css_files=["styles/dashboard.css"],
watch=True, # or set it here
)
app.show(content, watch=True) # or override hereSee the Hot Reload guide for details.
Lists of Toolbar or Modal objects (or their dict equivalents) that wrap your content in an interactive layout. Toolbars are positioned around the content area; modals are hidden until triggered by events.
from pywry import Toolbar, Button, TextInput, Modal
toolbar = Toolbar(position="top", items=[
Button(label="Save", event="app:save"),
TextInput(label="Search", event="app:search"),
])
modal = Modal(id="settings", title="Settings", items=[
TextInput(label="Name", event="settings:name"),
Button(label="Apply", event="settings:apply"),
])
app.show(html, toolbars=[toolbar], modals=[modal])See the Toolbar System guide for the full component list and layout positions.
app.show() returns a handle that provides a consistent API regardless of rendering path:
handle = app.show(html)
# Send events to this specific window/widget
handle.emit("pywry:set-content", {"selector": "h1", "text": "Updated"})
# Register additional callbacks
handle.on("app:click", on_click)The returned type depends on the environment:
| Environment | Return Type | Transport |
|---|---|---|
| Desktop terminal | NativeWindowHandle |
stdin pipe → Tauri IPC |
| Jupyter (with anywidget) | PyWryWidget |
traitlet sync |
| Jupyter (IFrame fallback) | InlineWidget |
WebSocket |
| Browser mode | InlineWidget |
WebSocket |
All implement the BaseWidget protocol, so emit() and on() work identically.
app.show() is the general-purpose method. PyWry also provides specialized variants that configure the right options automatically:
Converts a Plotly figure to HTML, injects Plotly.js, and pre-wires chart events (plotly:click, plotly:hover, plotly:selected, plotly:relayout).
import plotly.express as px
fig = px.scatter(x=[1, 2, 3], y=[1, 4, 9])
app.show_plotly(fig, callbacks={"plotly:click": on_click})Converts a DataFrame to AG Grid HTML, injects the AG Grid library, and pre-wires grid events (grid:cell-clicked, grid:selection-changed, grid:cell-edited).
import pandas as pd
df = pd.DataFrame({"Name": ["Alice", "Bob"], "Score": [95, 87]})
app.show_dataframe(df, callbacks={"grid:cell-clicked": on_cell})Both methods accept all the same optional parameters as app.show() (title, width, height, callbacks, label, toolbars, modals).
app.show() detects the environment and selects the rendering path automatically:
flowchart TD
A["app.show(content)"] --> B{"mode = BROWSER?"}
B -- Yes --> C["InlineWidget<br>FastAPI + WebSocket<br>opens system browser"]
B -- No --> D{"In a notebook?"}
D -- Yes --> E{"include_plotly<br>or include_aggrid?"}
E -- Yes --> F["InlineWidget<br>IFrame + WebSocket<br>specialized JS"]
E -- No --> G{"anywidget<br>available?"}
G -- Yes --> H["PyWryWidget<br>anywidget + traitlet sync"]
G -- No --> I["InlineWidget<br>IFrame + WebSocket<br>fallback"]
D -- No --> J["NativeWindowHandle<br>PyTauri + OS webview"]
You can force a specific mode by setting it on the app:
from pywry import PyWry, WindowMode
# Always use native windows (even in notebooks)
app = PyWry(mode=WindowMode.SINGLE_WINDOW)
# Always use browser
app = PyWry(mode=WindowMode.BROWSER)- HtmlContent — Control CSS, JS, and data injection
- Content Assembly — How PyWry builds the final HTML document
- Event System — Two-way Python ↔ JavaScript communication
- Toolbar System — Building interactive controls