Skip to content

Backends

cplots ships four backends. Each translates a PlotSpec into a native figure object without knowing anything about the plot type that produced it.

Trace type matplotlib plotly altair ultraplot
Trace, BandTrace ✓ ✓ ✓ ✓
PolylineCollectionTrace, MarkerTrace (phase portraits) ✓ ✓
TriangleTrace (standalone only) ✓

A backend raises NotImplementedError naming a supporting backend for anything it can't draw.

All four backends apply every Style field (fonts, foreground and background, palette, line width, grid, legend frame), including the default style cplots.DEFAULT; see Styling.

Selecting a backend

Backend selection follows a three-level priority:

Priority Method Example
1 (highest) Per-call kwarg plot.render(backend="plotly")
2 Context manager with cplots.backend("altair"): ...
3 (lowest) Global default cplots.set_backend("matplotlib")

The default global backend is "matplotlib".

matplotlib

Produces matplotlib.figure.Figure objects.

pip install cplots[matplotlib]

fig = plot.render(backend="matplotlib")  # returns matplotlib.figure.Figure
fig.savefig("figure.pdf", dpi=300, bbox_inches="tight")

Best for: publication-quality static figures (PDF, EPS, PNG).

plotly

Produces plotly.graph_objects.Figure objects.

pip install cplots[plotly]
fig = plot.render(backend="plotly")
fig.write_html("figure.html")   # interactive HTML with MathJax
fig.write_image("figure.png")   # static raster (requires kaleido)

Best for: interactive HTML figures and dashboards.

LaTeX in plotly

Set latex_labels=True to pass raw LaTeX strings to plotly. MathJax will render them in the browser.

cplots.PKSpectrum(
    k, pk,
    xlabel=r"$k\ [h\,\mathrm{Mpc}^{-1}]$",
    ylabel=r"$P(k)\ [h^{-3}\,\mathrm{Mpc}^3]$",
    latex_labels=True,
).render(backend="plotly")

altair

Produces altair.Chart objects (Vega-Lite).

pip install cplots[altair]

chart = plot.render(backend="altair")
chart.save("figure.html")  # self-contained Vega-Lite HTML

Best for: interactive web visualizations and Jupyter notebooks.

Limitations

The Altair backend does not support rowspan > 1 in multi-panel layouts. Use matplotlib or plotly for spanning grid cells. Traces with explicit styling (opacity, line width or dash, markers, error bars) are drawn as separate layers without a legend entry.

ultraplot

Produces ultraplot figures (a matplotlib wrapper); x/y traces and bands only.

pip install cplots[ultraplot]

Registering a third-party backend

Any class satisfying BackendProtocol can be registered:

cplots.register_backend("mybackend", MyBackend())

Or via pyproject.toml entry points for automatic discovery:

[project.entry-points."cplots.backends"]
mybackend = "mypkg.backend:MyBackend"

Writing a custom backend

Subclassing cplots.backends._base.BaseBackend with a name registers the backend and gives it per-trace dispatch: self._dispatch(trace, ctx) calls self._draw_<snake_case_type>(trace, ctx), e.g. _draw_trace or _draw_band_trace, and raises a uniform NotImplementedError for trace types without a method. A backend that renders a whole spec at once can instead call self._require(spec, Trace, BandTrace) up front.

from cplots.backends._base import BaseBackend
from cplots.core.spec import BandTrace, PlotSpec, Trace


class MyBackend(BaseBackend, name="mybackend"):
    def render(self, spec: PlotSpec):
        canvas = make_canvas()
        for i, trace in enumerate(spec.traces):
            self._dispatch(trace, (canvas, spec, i))
        return canvas

    def _draw_trace(self, trace: Trace, ctx): ...
    def _draw_band_trace(self, trace: BandTrace, ctx): ...
    def show(self, fig, **kwargs): ...
    def save(self, fig, path, **kwargs): ...

What a backend should honour:

  • Trace style is typed: color, alpha, linewidth, linestyle ("solid", "dashed", "dotted", "none" for markers only), marker ("circle", "point", "square", "diamond", "cross", "x") and yerr. Trace.style holds extra matplotlib keyword arguments only; other backends ignore it.
  • Colour priority: the colormap colour when spec.colorbar is set and the trace has a color_value, else trace.color, else the style palette.
  • spec.colorbar (a ColorbarSpec, or None for no colour mapping) holds the colormap, label, limits, placement and show. Its pad is in backend units: a fraction of the axes for matplotlib/ultraplot, pixels for plotly.
  • spec.scene (a Scene3DSpec) makes a panel 3D; spec.dim is derived from it.
  • spec.style (a Style, possibly None or partly unset) applies to the figure being rendered only. Apply the fields that are set and leave the rest at your library's defaults. Never change process-wide state: for matplotlib, use rc_context. style.backend_options[<your name>] carries backend-specific settings.
  • spec.legend (LegendSpec or None) and spec.grid (bool or None) control the legend and grid. With None, show a legend when any trace is labelled.
  • Capabilities. Set capabilities (a frozenset of names from cplots.core.features.FEATURES) to the presentation features you render, and call self._check_features(spec) at the start of render and render_figure. Users then get one UnsupportedFeatureWarning per feature you ignore. The default declares every feature.

cplots.backends._common has the shared helpers (fmt, css_color, size_px, palette_color, background, MPL_MARKERS, mpl_line_kwargs, mpl_rc, mpl_legend_kwargs, show_legend).