Examples¶
All examples live under the examples/ directory. Run them with:
uv sync -U --all-groups --all-extras
cd examples && uv run python run_example.py
Or directly:
# Starlette (via daphne/uvicorn)
cd examples/starlette && uv run daphne -p 8000 draw_idle:app
# Quart
cd examples/quart && uv run python basic.py
Starlette¶
Single standalone figure (draw_idle.py)¶
Demonstrates draw_idle with an interactive keyboard event: pressing Enter
swaps the colors of two scatter plots.
"""
Shows draw_idle(...) working with webagg and webaggext --- pressing enter swaps the
colors of the two scatter plots.
Flags: USE_BASIC_WEBAGG_BACKEND
"""
import os
from matplotlib import pyplot as plt
from starlette.applications import Starlette
from starlette.routing import Route
from mplbed import mplbed_starlette
use_basic_webagg_backend = "USE_BASIC_WEBAGG_BACKEND" in os.environ
def page(request):
fig, (ax1, ax2) = plt.subplots(nrows=2)
x = [0, 1, 2]
coll1 = ax1.scatter(x, x, c=["r", "g", "b"])
coll2 = ax2.scatter(x, x, c=["b", "g", "r"])
def accept(event):
match event.key:
case "enter":
# swap facecolors
fc1 = coll1.get_facecolors()
fc2 = coll2.get_facecolors()
coll1.set_facecolors(fc2)
coll2.set_facecolors(fc1)
fig.canvas.draw_idle()
case _:
pass
fig.canvas.mpl_connect("key_press_event", accept)
return mplbed_starlette.figure_standalone(fig)
app = Starlette(
debug=True,
routes=[Route('/', page)],
)
mplbed_starlette.setup(app, use_webaggext_backend=not use_basic_webagg_backend)
Two embedded figures (embed2_raw.py)¶
Embeds two figures side-by-side in a single page using raw string templates.
"""
Creates two embedded figures using raw string templates using Starlette/mplbed/string formatting.
"""
from matplotlib.figure import Figure
from starlette.applications import Starlette
from starlette.responses import Response
from starlette.routing import Route
from mplbed import mplbed_starlette, raw_html
def homepage_template(*, head, fig1, fig2):
return f"""<!DOCTYPE html>
<html lang="en">
<head>
{head}
<title>matplotlib</title>
</head>
<body>
<div style="display: flex">
<div>
<h2>Figure 1</h2>
{fig1}
</div>
<div>
<h2>Figure 2</h2>
{fig2}
</div>
</div>
</body>
</html>
"""
def create_figure():
import numpy as np
fig = Figure()
ax = fig.add_subplot()
t = np.arange(0.0, 3.0, 0.01)
s = np.sin(2 * np.pi * t)
ax.plot(t, s)
return fig
def homepage(request):
fig1 = create_figure()
fig2 = create_figure()
return Response(
homepage_template(
head=raw_html.head_content(),
fig1=raw_html.figure_html(fig1),
fig2=raw_html.figure_html(fig2),
),
media_type='text/html'
)
app = Starlette(
debug=True,
routes=[Route('/', homepage)],
)
mplbed_starlette.setup(app)
Popup figure (demo_popup.py)¶
A button on the main figure spawns a popup figure in a modal dialog.
"""
Creates a figures which spawns a popup figure when a button is pressed.
Flags: REUSE_POPUP
"""
import os
from matplotlib import pyplot as plt
from starlette.applications import Starlette
from starlette.responses import Response
from starlette.routing import Route
from mplbed import raw_html
from mplbed.integration.starlette import setup
reuse_popup = "REUSE_POPUP" in os.environ
def homepage_template(*, head, fig):
return f"""<!DOCTYPE html>
<html lang="en">
<head>
{head}
<title>matplotlib</title>
</head>
<body>
<div style="display: flex">
<div>
<h2>Figure</h2>
{fig}
</div>
</div>
</body>
</html>
"""
class PopupDemoMpl:
def __init__(self):
from matplotlib.widgets import Button
self.fig = plt.figure()
self.ax = self.fig.add_subplot()
self.button = Button(self.ax, "Create popup global")
self.button.on_clicked(self.create_popup)
self.popup_fig = None
def create_popup(self, event):
if not reuse_popup or self.popup_fig is None:
self.popup_fig = plt.figure()
ax = self.popup_fig.add_axes((0.01, 0.01, 0.98, 0.98))
ax.set_axis_off()
ax.text(0.42, 0.5, "Hello from the popup", ma="left", ha="left")
self.popup_fig.show()
def into_html(self):
return raw_html.figure_html(self.fig, self)
def homepage(request):
demo = PopupDemoMpl()
return Response(
homepage_template(
head=raw_html.head_content(),
fig=demo.into_html()
),
media_type='text/html'
)
app = Starlette(
debug=True,
routes=[Route('/', homepage)],
)
setup(app)
Manual routing (mount_app.py)¶
Shows how to manually create the mplbed Starlette sub-app and mount it at a
custom prefix instead of using setup().
"""
Shows manual creation and routing of the `mplbed` Starlette app.
"""
from matplotlib.figure import Figure
from starlette.applications import Starlette
from starlette.responses import Response
from starlette.routing import Route, Mount
from mplbed import raw_html, mplbed_starlette, mplbed_app_factory
def homepage_template(*, head, fig1):
return f"""<!DOCTYPE html>
<html lang="en">
<head>
{head}
<title>Sine wave plot</title>
</head>
<body>
<h2>Sine wave plot</h2>
{fig1}
</body>
</html>
"""
def create_figure():
import numpy as np
fig = Figure()
ax = fig.add_subplot()
t = np.arange(0.0, 3.0, 0.01)
s = np.sin(2 * np.pi * t)
ax.plot(t, s)
return fig
def homepage(request):
fig1 = create_figure()
return Response(
homepage_template(
head=raw_html.head_content(),
fig1=raw_html.figure_html(fig1),
),
media_type='text/html'
)
mplbed_app = mplbed_app_factory()
MPLBED_PREFIX = "/mymplbedprefix"
app = Starlette(
debug=True,
routes=[Route('/', homepage), Mount(MPLBED_PREFIX, app=mplbed_app, name="webagg")],
)
mplbed_starlette.setup(app, prefix=MPLBED_PREFIX, mplbed_starlette_app=mplbed_app, manage_routing=False)
MNE integration (integrate_mne.py)¶
Embeds an interactive MNE raw-data plot in a Starlette page.
"""
Shows how an existing application can be integrated with mplbed. This example
uses MNE, an EEG/MNE library, to create a figure and then embeds it in a
Starlette application.
"""
from starlette.applications import Starlette
from starlette.responses import Response
from starlette.routing import Route
from mplbed import mplbed_starlette, raw_html
import mne
def homepage_template(*, head, fig):
return f"""<!DOCTYPE html>
<html lang="en">
<head>
{head}
<title>matplotlib</title>
</head>
<body>
<div style="display: flex">
<div>
<h2>Figure</h2>
{fig}
</div>
</div>
</body>
</html>
"""
def homepage(request):
sample_data_folder = mne.datasets.sample.data_path()
sample_data_raw_file = (
sample_data_folder / "MEG" / "sample" / "sample_audvis_filt-0-40_raw.fif"
)
raw = mne.io.read_raw_fif(sample_data_raw_file)
fig = raw.plot(show=False)
return Response(
homepage_template(
head=raw_html.head_content(),
fig=raw_html.figure_html(fig, on_close="msg_disable")
),
media_type='text/html'
)
app = Starlette(
debug=True,
routes=[Route('/', homepage)],
)
mplbed_starlette.setup(app)
Quart¶
Inline, iframe, and popup figures (basic.py)¶
Shows three embedding styles in a single Quart/Jinja2 app: an inline figure,
a figure loaded inside an <iframe>, and a figure that opens as a modal popup.
"""
Shows inline figures, external figures and popups using mplbed/Quart/jinja2.
"""
from quart import Quart, render_template
from mplbed import mplbed_quart
from mplbed import safe_html
app = Quart(__name__)
mplbed_quart.setup(app)
def mk_plot():
import matplotlib.pyplot as plt
import numpy as np
t = np.arange(0.0, 2 * np.pi, 0.01)
s = np.sin(t)
fig, ax = plt.subplots()
ax.plot(t, s)
return fig
@app.route('/')
async def index():
return await render_template(
"index.html",
inline_fig=safe_html.figure_html(mk_plot()),
external_iframe=mplbed_quart.iframe_for("figure"),
open_inline_popup=safe_html.figure_html(mk_plot(), target="modal")
)
@app.route('/figure')
async def figure():
return mplbed_quart.figure_standalone(mk_plot())
if __name__ == "__main__":
import os
app.run(debug=True, port=int(os.environ.get("PORT", 8000)))
The Jinja2 templates used by this example:
templates/base.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4" crossorigin="anonymous"></script>
<title>{% block title %}{% endblock %}</title>
{{ mplbed_head(False) }}
</head>
<body style="padding: 1em; font-family: sans-serif;">
<div id="content">
{% block content %}{% endblock %}
</div>
</body>
</html>
templates/index.html
{% extends "base.html" %}
{% block title %}Home{% endblock %}
{% block content %}
<h2 style="font-size: 2em;">Inline figure</h2>
{{ inline_fig }}
<hr style="margin: 5em 0;">
<h2 style="font-size: 2em;">External iframe</h2>
{{ external_iframe }}
<hr style="margin: 5em 0;">
<h2 style="font-size: 2em;">Open inline popup</h2>
{{ open_inline_popup }}
{% endblock %}