render_drawdata

render_drawdata class

Reactively render a drawdata drawing widget.

Usage Source

render_drawdata(
    _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_drawdata is a decorator that creates a render function – a function that returns the drawing canvas to display and re-runs when the reactive values it reads change:

from drawdata import ScatterWidget

from shinywidgets import render_drawdata


@render_drawdata
def canvas():
    return ScatterWidget()

The render function should return a drawdata.ScatterWidget or drawdata.BarWidget (requires pip install 'shinywidgets[drawdata]'). The widget fills the output’s width while preserving its aspect ratio (upstream, both widgets cap their display at their width/height traits; shinywidgets lifts that cap so they scale like other outputs). The constructor’s width/height define the drawing’s data coordinate space – drawn points are stored in those coordinates, with y pointing up – and the aspect ratio, not the display size, so choose them for the coordinate range and shape you want, not for pixels.

Note

Accessing the rendered widget: the render function gains two read-only attributes: .value and .widget, both the rendered widget.

Responding to user interaction: read the drawn points reactively with canvas.read("data").

Mutating the widget: use .widget to update the widget in place. For BarWidget, re-render instead: it ignores Python-side trait updates until the next re-render (see below).

Drawn data syncs one batch behind (upstream drawdata issue #40): the first drawing batch doesn’t sync to Python; nothing is readable until a second batch is drawn (from then on, every batch syncs in full). The undo (pops one batch) and reset buttons sync immediately. BarWidget is not affected.

Also note a few BarWidget limitations (upstream drawdata issue #42): its frontend overwrites any data passed to the constructor when it initializes (all bins start at y_min), so a BarWidget output always starts empty – seeding initial data is impossible, even via a re-render – and it ignores later Python-side trait updates until the next re-render. Re-rendering (returning a new BarWidget in response to a reactive change) is the way to change n_bins, y_max, etc. Finally, avoid putting two BarWidgets on the same page: clicking one widget’s collection buttons silently switches the other’s active collection (upstream drawdata issue #43).

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 height follows from the filled width and the widget’s aspect ratio.

fill : Optional[bool] = None

Whether the output should grow or shrink to fit its parent container.

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.

Examples

from drawdata import ScatterWidget
from shiny import App, render, ui
from shinywidgets import output_widget, render_drawdata

app_ui = ui.page_fluid(
    output_widget("canvas"),
    ui.output_text_verbatim("n_points"),
)


def server(input, output, session):
    @render_drawdata
    def canvas():
        return ScatterWidget()

    @render.text
    def n_points():
        return len(canvas.read("data"))


app = App(app_ui, server)

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).