Skip to content

Plotly Charts

PyWry provides first-class Plotly support — pass a Plotly figure to show_plotly() and get a fully interactive chart with pre-wired events, automatic theming, and programmatic update capabilities.

For the complete Plotly configuration API, see PlotlyConfig. For all Plotly events and payloads, see the Event Reference.

Basic Usage

import plotly.express as px
from pywry import PyWry

app = PyWry()

fig = px.scatter(
    x=[1, 2, 3, 4, 5],
    y=[1, 4, 9, 16, 25],
    title="Quadratic Function"
)

handle = app.show_plotly(fig)

Plotly Configuration

Use PlotlyConfig to customize chart behavior:

from pywry import PlotlyConfig

config = PlotlyConfig(
    responsive=True,
    scroll_zoom=True,
    display_mode_bar=True,  # True, False, or "hover"
    mode_bar_buttons_to_remove=["lasso2d", "select2d"],
)

handle = app.show_plotly(fig, config=config)

For the full list of PlotlyConfig properties, see the API Reference.

Custom Mode Bar Buttons

Add custom buttons to the chart toolbar:

from pywry import PlotlyConfig, ModeBarButton, SvgIcon

config = PlotlyConfig(
    mode_bar_buttons_to_add=[
        ModeBarButton(
            name="custom",
            title="Custom Action",
            icon=SvgIcon(path="M10 10 L90 90", width=100, height=100),
            click="function(gd) { window.pywry.emit('app:custom', {}); }"
        )
    ],
)

Chart Events

Plotly charts emit events for user interactions:

def on_click(data, event_type, label):
    point = data["points"][0]
    x, y = point["x"], point["y"]
    app.emit("pywry:alert", {"message": f"Clicked: ({x}, {y})"}, label)

def on_hover(data, event_type, label):
    point = data["points"][0]
    app.emit("pywry:set-content", {
        "id": "status",
        "text": f"Hovering: {point['x']}, {point['y']}"
    }, label)

def on_select(data, event_type, label):
    points = data.get("points", [])
    app.emit("pywry:alert", {"message": f"Selected {len(points)} points"}, label)

handle = app.show_plotly(
    fig,
    callbacks={
        "plotly:click": on_click,
        "plotly:hover": on_hover,
        "plotly:selected": on_select,
    },
)

For the complete list of Plotly events and payload structures, see the Event Reference.

Updating Charts

Update Layout

handle.emit("plotly:update-layout", {
    "layout": {
        "title": "Updated Title",
        "showlegend": False,
    }
})

Update Traces

handle.emit("plotly:update-traces", {
    "update": {"marker.color": "red"},
    "traceIndices": [0],
})

Reset Zoom

handle.emit("plotly:reset-zoom", {})

With Toolbars

Add interactive controls:

from pywry import Toolbar, Button, Select, Option

toolbar = Toolbar(
    position="top",
    items=[
        Select(
            event="chart:dataset",
            label="Dataset",
            options=[
                Option(label="Dataset A", value="a"),
                Option(label="Dataset B", value="b"),
            ],
            selected="a",
        ),
        Button(event="chart:reset", label="Reset Zoom"),
    ],
)

def on_dataset_change(data, event_type, label):
    dataset = data.get("value")
    # Load new data and update chart

def on_reset(data, event_type, label):
    handle.emit("plotly:reset-zoom", {})

handle = app.show_plotly(
    fig,
    toolbars=[toolbar],
    callbacks={
        "chart:dataset": on_dataset_change,
        "chart:reset": on_reset,
    },
)

Multiple Charts

Display multiple charts using HTML layout:

import plotly.express as px
from pywry import PyWry

app = PyWry()

fig1 = px.line(x=[1, 2, 3], y=[1, 2, 3], title="Linear")
fig2 = px.scatter(x=[1, 2, 3], y=[1, 4, 9], title="Quadratic")

html = f"""
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px; padding: 20px;">
    <div id="chart1"></div>
    <div id="chart2"></div>
</div>
<script>
    Plotly.newPlot('chart1', {fig1.to_json()});
    Plotly.newPlot('chart2', {fig2.to_json()});
</script>
"""

app.show(html, include_plotly=True)

Theming

Charts automatically adapt to PyWry's theme:

from pywry import PyWry, ThemeMode

app = PyWry(theme=ThemeMode.LIGHT)  # or ThemeMode.DARK

fig = px.scatter(x=[1, 2, 3], y=[1, 4, 9])
app.show_plotly(fig)  # Uses appropriate Plotly template

To change theme dynamically:

handle.emit("pywry:update-theme", {"theme": "light"})

Custom Per-Theme Templates

By default, PyWry applies the built-in plotly_dark or plotly_white template based on the current theme. To customize chart colors per theme while preserving automatic switching, use template_dark and template_light on PlotlyConfig:

from pywry import PlotlyConfig

config = PlotlyConfig(
    template_dark={
        "layout": {
            "paper_bgcolor": "#1a1a2e",
            "plot_bgcolor": "#16213e",
            "font": {"color": "#e0e0e0"},
        }
    },
    template_light={
        "layout": {
            "paper_bgcolor": "#ffffff",
            "plot_bgcolor": "#f0f0f0",
            "font": {"color": "#222222"},
        }
    },
)

handle = app.show_plotly(fig, config=config)

How it works:

  • Your overrides are deep-merged on top of the built-in base template (plotly_dark or plotly_white).
  • User values always win on conflict. Anything you don't set is inherited from the base.
  • Both templates are stored on the chart and automatically selected when the theme toggles.
  • Arrays (e.g., colorways) are replaced entirely, not element-merged.

You can also set only one side — e.g., template_dark alone — and the other theme will use the unmodified base.

Tip

Use template_dark / template_light instead of setting fig.update_layout(template=...) directly. The latter gets overwritten on theme switch; the former survives toggles.

Next Steps