render_bokeh
render_bokeh class
Reactively render a bokeh plot.
Usage Source
render_bokeh(
_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_bokeh 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 bokeh.plotting import figure
from shinywidgets import render_bokeh
@render_bokeh
def plot():
p = figure()
p.line([1, 2, 3], [1, 4, 9])
return pThe render function may return either a bokeh model (e.g., a bokeh.plotting.figure), which is coerced to a jupyter_bokeh.BokehModel widget, or a jupyter_bokeh.BokehModel widget (requires pip install 'shinywidgets[bokeh]').
A figure fills its output by default (i.e., it gets sizing_mode="stretch_both"), unless you give it a width, height, sizing_mode, or aspect_ratio, in which case bokeh’s own sizing applies. Other models (e.g., a column or row layout) fill only if their sizing_mode stretches vertically (e.g., sizing_mode="stretch_both" on the layout and its children).
Plots that share a ColumnDataSource (e.g., for linked selection) must be returned together, in one layout, from a single render function. Create bokeh models per session (e.g., inside the render function), not at the top level of a Shiny Core app, where every visitor would share them.
Note
Accessing the rendered widget: the render function gains two read-only attributes: .value, the bokeh object it most recently returned, and .widget, the rendered BokehModel instance.
Responding to user interaction: bokeh model properties aren’t widget traits, so reactive_read() can’t see them. Instead, 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:
selected(): the indices of the selected rows of the plot’sColumnDataSource(from the tap, box, or lasso select tools), andselected_rows(): those rows, as a data frame. These need a plot with exactly oneColumnDataSource.tapped(): the most recent tap, with its.xand.yin data units.zoomed(): the plot’s ranges after a zoom or pan.
How they behave:
tapped()returnsNoneuntil the user taps. That’s a normal state, not an error, so check for it, or usereq(plot.tapped())to wait for it.selected()starts at[]instead: it’s seeded with the plot’s current selection at render, so a selection made before its first read isn’t lost – but a reader can’t distinguish “nothing selected yet” from a selection that caught nothing (selected_rows()is an empty frame either way).zoomed()is also seeded at attach, from the plot’s current ranges, when those ranges hold explicit values – so it reports the displayed viewport from the start rather thanNoneuntil the first zoom or pan (an auto-fitting or categorical range still reportsNoneuntil the first gesture).- A re-render (e.g. when the render function reads an input) gives the user a new plot, but every accessor keeps its value – the last interaction still happened.
tapped()holds the old plot’s last tap until a new one happens;zoomed()seeds from the plot’s current ranges at attach, so it tracks the displayed viewport (initially the unzoomed one).selected()follows the displayed plot’s selection instead, keeping its value until the new plot reports its own (initially empty, so it becomes[]once it does). - Selecting the same points again isn’t a change, so readers of
selected()don’t re-run. Every tap, zoom, or pan re-runs readers oftapped()andzoomed(). selected()updates at most once the user pauses for 250 ms (seerate_policyandrate_delay).- They can’t be read inside this output’s own render function, which builds a new plot each time it runs. To change the plot in response, modify the bokeh models from a
@reactive.effectinstead.
For anything else, bokeh’s own callbacks (e.g., source.selected.on_change("indices", ...)) work too, and may set reactive.values.
Mutating the widget: to update the plot without re-rendering, modify the bokeh models in place (e.g., source.data = ...).
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 (seefillandfillable). fill : Optional[bool] = None-
Whether the output should grow or shrink to fit its parent container. When
None(the default), this isTrueifheightis not specified. 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. rate_policy : Optional[RatePolicy] = None-
How to rate-limit
selected()(debounced by default):"debounce"or"throttle".None(the default) keeps the debounce. rate_delay : Optional[float] = None-
The rate-limit window in seconds (default 0.25).
0disables 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
selected() |
The row indices (into the plot’s ColumnDataSource) of the current selection, from a tap, box, or lasso select tool. A tap on a line reports the row of the tapped segment’s first point; a tap on a multi_line reports its lines. (Box and lasso selects never hit line glyphs – that’s bokeh’s own behavior.) |
selected_rows() |
The selected rows (see selected()) of the plot’s ColumnDataSource, as a pandas data frame (source.to_df()). Empty while nothing is selected. Readers also re-run when source.data changes. |
tapped() |
The most recent tap on a plot, as a bokeh.events.Tap (.x and .y in data units, .model the plot). Returns None until the user taps, and keeps its value across re-renders (the payload then refers to the previous render’s plot). Not rate-limited. |
zoomed() |
The plot ranges after the most recent zoom or pan, as a bokeh.events.RangesUpdate (.x0, .x1, .y0, .y1, .model). Seeded with the plot’s current (default) ranges at attach, so a zoom made before the first read is reported; an auto-fitting or categorical range reports None until the first gesture, and extra ranges aren’t included. Not rate-limited (bokeh reports once per gesture). With several plots (a layout), the seed is emitted one event per plot in document order, so the reader sees the last plot’s ranges until the next gesture. |
selected()
render_bokeh.selected()The row indices (into the plot’s ColumnDataSource) of the current selection, from a tap, box, or lasso select tool. A tap on a line reports the row of the tapped segment’s first point; a tap on a multi_line reports its lines. (Box and lasso selects never hit line glyphs – that’s bokeh’s own behavior.)
Returns [] while nothing is selected. Seeded with the plot’s current selection at render, so a selection made before the first read is kept; as a result, “nothing selected yet” and a selection that caught nothing both read as []. Requires a plot (or layout) with exactly one ColumnDataSource. Debounced (250 ms) by default; see the rate_policy and rate_delay parameters.
The indices are positions: after a re-render (or an in-place source.data replacement) with new rows, a retained index can point at a different row. To track rows across data changes, carry an ID column in the source and map positions through it (source.data["id"][i] for each i in plot.selected()).
selected_rows()
render_bokeh.selected_rows()The selected rows (see selected()) of the plot’s ColumnDataSource, as a pandas data frame (source.to_df()). Empty while nothing is selected. Readers also re-run when source.data changes.
tapped()
render_bokeh.tapped()The most recent tap on a plot, as a bokeh.events.Tap (.x and .y in data units, .model the plot). Returns None until the user taps, and keeps its value across re-renders (the payload then refers to the previous render’s plot). Not rate-limited.
zoomed()
render_bokeh.zoomed()The plot ranges after the most recent zoom or pan, as a bokeh.events.RangesUpdate (.x0, .x1, .y0, .y1, .model). Seeded with the plot’s current (default) ranges at attach, so a zoom made before the first read is reported; an auto-fitting or categorical range reports None until the first gesture, and extra ranges aren’t included. Not rate-limited (bokeh reports once per gesture). With several plots (a layout), the seed is emitted one event per plot in document order, so the reader sees the last plot’s ranges until the next gesture.