render_plotnine

render_plotnine class

Reactively render a plotnine plot as an interactive ipympl widget.

Usage Source

render_plotnine(
    _fn: Optional[ValueFn[ValueT]] = None,
    *,
    width: Optional[str] = None,
    height: Optional[str] = None,
    fill: Optional[bool] = None,
    fillable: Optional[bool] = None,
    rate_policy: Optional[RatePolicy] = None,
    rate_delay: Optional[float] = None,
)

@render_plotnine is a decorator that creates a render function – a function that returns the plot to display and re-runs when the reactive values it reads change:

from plotnine import aes, geom_point, ggplot
from plotnine.data import penguins

from shinywidgets import render_plotnine


@render_plotnine
def plot():
    return ggplot(penguins, aes("bill_length_mm", "body_mass_g")) + geom_point()

The render function may return a plotnine.ggplot or a plot composition (e.g., p1 | p2), which is drawn to a matplotlib figure and coerced to an ipympl.backend_nbagg.Canvas widget (requires pip install 'shinywidgets[plotnine]'). The returned plot itself isn’t modified (a copy is drawn). In a filling layout, the figure is resized to fit its container (overriding theme(figure_size=...)), and plotnine re-lays out the plot (e.g., legends and axis labels) at each size. With an explicit height, the figure isn’t fitted to the output (it’s drawn at its figure_size, and a larger figure is clipped), so set theme(figure_size=...) to match. With fill=False (and no height), the figure’s width still follows its container.

Note that plotnine computes axis breaks when it draws, so zooming in (e.g., with the toolbar) keeps the original ticks, and a close zoom can show none. For zoom-aware ticks, set a matplotlib locator and formatter on the axes of .widget.figure (e.g., matplotlib.ticker.MaxNLocator and ScalarFormatter, or matplotlib.dates.AutoDateLocator for dates) in an effect that reads .widget.

Note

Accessing the rendered widget: the render function gains two read-only attributes: .value, the value it most recently returned, and .widget, the rendered Canvas instance. Widget state can also be read reactively with reactive_read().

Responding to user interaction: the output has methods that read what the user does in the plot, much like input.my_slider() reads an input. Call them in a reactive function (a @render.* function, @reactive.calc, or @reactive.effect), which then re-runs whenever the interaction changes:

  • clicked(): the most recent mouse press on the plot. It returns None until the user clicks (a normal state, not an error, so check for it, or use req(plot.clicked())), and keeps its value across re-renders.
  • limits(ax=0): an axes’ x/y limits after a zoom or pan, seeded from the rendered plot and updated once the user pauses for 250 ms (debounced; see rate_policy and rate_delay).

They can’t be read inside this output’s own render function, which builds a new plot each time it runs.

Mutating the widget: use .widget to update the rendered figure in place; trait changes on the widget are synced to the browser.

Parameters

width : Optional[str] = None

A CSS length unit (e.g., "400px" or "100%") for the output’s width.

height : Optional[str] = None

A CSS length unit for the output’s height. When None (the default), the output is designed to fill the height of its parent container (see fill and fillable).

fill : Optional[bool] = None

Whether the output should grow or shrink to fit its parent container. When None (the default), this is True if height is not specified.

fillable : Optional[bool] = None

Whether the output container should fill available space (scrolling its contents on overflow). When None (the default), this is True if height is not specified.

rate_policy : Optional[RatePolicy] = None

How to rate-limit limits() (debounced by default) and reactive_read() on the widget: "debounce" or "throttle". None (the default) keeps the debounce, and debounces reactive_read() only if rate_delay is given.

rate_delay : Optional[float] = None

The rate-limit window in seconds (default 0.25). 0 disables rate limiting.

Each :
which :

See also

output_widget(): in Shiny Core, the UI needs this to hold the output (Shiny Express displays it where the render function is defined).

Methods

clicked() The most recent mouse press on the plot, as a matplotlib.backend_bases.MouseEvent.
limits() The x and y limits of axes ax (an index into .widget.figure.axes; with facet_wrap()/facet_grid(), the panels’ axes, in figure order), as ((x0, x1), (y0, y1)).

clicked()

render_plotnine.clicked()

The most recent mouse press on the plot, as a matplotlib.backend_bases.MouseEvent.

Note that .xdata/.ydata are in plotnine’s transformed coordinate space – under scale_x_log10() they’re the log10 of the data, and discrete (categorical) scales use integer positions – and with facet_wrap()/facet_grid(), .inaxes names the panel’s axes, but nothing in the event names its facet. Returns None until the user clicks, and keeps its value across re-renders (the payload then refers to the previous render’s figure), so use req(plot.clicked()) to wait for a click. Presses that start a toolbar pan/zoom drag, or land outside any axes, don’t count. Every press fires – a double-click arrives as three presses. Not rate-limited.

limits()

render_plotnine.limits(ax: int = 0)

The x and y limits of axes ax (an index into .widget.figure.axes; with facet_wrap()/facet_grid(), the panels’ axes, in figure order), as ((x0, x1), (y0, y1)).

Seeded from the plot’s current limits at render and updated after every zoom or pan. Like clicked(), the limits are in plotnine’s transformed coordinate space – under scale_x_log10() they’re the log10 of the data. Debounced (250 ms) by default; see the rate_policy and rate_delay parameters. Raises IndexError when the figure has no axes ax.