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"); each point is a dict with x, y, color (a hex string), and label. Note that the widget’s own data_as_X_y property reports those hex colors as y for classification (once more than one class is drawn), not the labels – build class labels from the data trait (or data_as_pandas["label"]) instead.
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 isTrueifheightis 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).