Plotting your data#

Draw a chart from your data and let it theme itself — the starting point for everything else in this set.

A two-series line chart — light theme A two-series line chart — dark theme

How it works#

Chart embeds a matplotlib figure as a first-class widget. The recommended way to use it is the managed render path: you pass a render callback and the chart owns the rest — each redraw clears the axes, applies the theme, calls your render(ax), and draws. You just plot:

import math

def render(ax):
    xs = [i / 10 for i in range(80)]
    ax.plot(xs, [math.sin(x) for x in xs], label="sin")
    ax.plot(xs, [math.cos(x) for x in xs], label="cos")
    ax.legend()

bs.Chart(render=render, grow=True, horizontal="stretch")

You draw with plain matplotlib — Chart is not a plotting API, so you keep its full expressive power. It owns embedding, theming, and the redraw.

Multiple series are themed automatically: the first line is primary, the second success, and so on, drawn from the theme’s accent colors. The chrome — figure and axes backgrounds, spines, ticks, text — matches the active surface and flips with light/dark, so a bs.toggle_theme() recolors the whole chart with the rest of the app.

Note

matplotlib is an optional dependency. Install it with pip install bootstack[viz]. Build figures with matplotlib’s object API (Figure), never pyplot — an embedded figure must be a standalone object.

Drawing your own figure#

If you already have a Figure — or want full control over the styling — hand it over directly. The chart embeds it and recolors only its chrome to the theme; the data series are yours:

from matplotlib.figure import Figure

fig = Figure()
fig.add_subplot(111).plot([1, 2, 3], [4, 5, 6])
bs.Chart(fig, grow=True)

Reach for the managed render path whenever the plot reflects live state — it is what makes the next two guides (signals and data sources) reactive.

Example#

 1
 2
 3def render(ax):
 4    """Draw two themed series — colors come from the accent cycle."""
 5    xs = [i / 10 for i in range(80)]
 6    ax.plot(xs, [math.sin(x) for x in xs], label="sin", linewidth=2)
 7    ax.plot(xs, [math.cos(x) for x in xs], label="cos", linewidth=2)
 8    ax.set_xlabel("x")
 9    ax.set_ylabel("amplitude")
10    ax.grid(True, alpha=0.3)
11    ax.legend(loc="upper right")
12
13
14with bs.App(title="Plotting your data", size=(640, 460), padding=16, gap=12) as app:
15    bs.Label("Two series, themed to match the app", font="heading-md")
16    bs.Chart(render=render, grow=True, horizontal="stretch")
17    bs.Button("Toggle theme", on_click=bs.toggle_theme)
18
19app.run()

When to use#

This is the foundation. When the plot should update as your state changes, bind a Signal or a data source — see Live and data-driven charts. For statistical plots (bars, boxes, distributions) reach for seaborn — Statistical plots with seaborn. For continuous motion, Real-time and animated charts. The full widget reference is the Chart guide.