render_perspective
render_perspective class
Reactively render a perspective viewer.
Usage Source
render_perspective(
_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_perspective is a decorator that creates a render function – a function that returns the viewer to display and re-runs when the reactive values it reads change:
import pandas as pd
from shinywidgets import render_perspective
df = pd.DataFrame({"a": [1, 2, 3], "b": ["x", "y", "z"]})
@render_perspective
def grid():
return dfThe render function may return a pandas or polars DataFrame or a perspective.Table (both are coerced to a perspective.widget.PerspectiveWidget; requires pip install 'shinywidgets[perspective]'), or a perspective.widget.PerspectiveWidget widget. A pandas index becomes an index column.
Note
Accessing the rendered widget: the render function gains two read-only attributes: .value, the value it most recently returned, and .widget, the rendered PerspectiveWidget instance. Widget state can also be read reactively with .read("some_trait") (a shortcut for reactive_read() on .widget).
Responding to user interaction: read the viewer’s accessors in a reactive function (a @render.* function, @reactive.calc, or @reactive.effect), which re-runs whenever the user changes it, much like input.my_slider():
grid.filter(): the viewer’s current filters.grid.view_config(): the viewer’s structural configuration (plugin, columns, group/split by, aggregates, sort, filter, expressions).
They can’t be read inside this output’s own render function, which builds a new viewer each time it runs. For anything beyond them (e.g., theme or title), use grid.read() – but unlike the accessors (which are seeded at render), a cosmetic trait read this way can be None until the first browser sync, so guard with req().
Mutating the widget: use .widget to update the rendered viewer in place (e.g., .widget.update() streams new rows); trait changes are synced to the browser. Perspective keeps every table it hosts in memory for the life of the process, so a table created from a DataFrame is not freed when the output re-renders. To change the data, prefer updating the existing viewer in place: .widget.update(df) appends rows (or upserts them, for a table with an index), and .widget.clear() followed by .widget.update(df) swaps them out. (.widget.replace() also swaps rows, but doesn’t accept a DataFrame.)
A viewer’s browser can list, read, and write every table on its widget’s Perspective server. A DataFrame gets a server of its own, so other sessions can’t reach its data. Tables made with perspective.table() (and widgets made with PerspectiveWidget(df)) live on Perspective’s process-wide server, which every session’s viewer can reach, so only use them for data that every user may see and edit. For per-user data, create the table on a private server instead: perspective.Server().new_local_client().table(df).
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
reactive_read()on the widget:"debounce"or"throttle".None(the default) debouncesreactive_read()only ifrate_delayis given; no accessor on this renderer declares a default. 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
filter() |
The viewer’s current filters, as [[column, op, value], ...] (e.g. [["x", ">", 1]]); [] while nothing is filtered. |
view_config() |
The viewer’s current configuration as a dict with the keys plugin, columns, group_by, split_by, aggregates, sort, filter, and expressions – the subset of PerspectiveWidget.save() that determines the rows shown, so cosmetic changes (opening the settings panel, switching the theme) don’t re-fire it. plugin is viewer-only (it says how the rows are displayed, not which rows), so exclude it before reproducing the rows: widget.table.view(**{k: v for k, v in cfg.items() if k != "plugin"}). Row clicks and selections never reach Python. Not rate-limited. |
filter()
render_perspective.filter()The viewer’s current filters, as [[column, op, value], ...] (e.g. [["x", ">", 1]]); [] while nothing is filtered.
Edits made in the viewer’s settings panel sync back to Python. Not rate-limited (filter edits are discrete gestures, and the renderer’s rate_policy/rate_delay parameters don’t apply to it – they only govern accessors that declare a default, plus reactive_read() on the widget).
view_config()
render_perspective.view_config()The viewer’s current configuration as a dict with the keys plugin, columns, group_by, split_by, aggregates, sort, filter, and expressions – the subset of PerspectiveWidget.save() that determines the rows shown, so cosmetic changes (opening the settings panel, switching the theme) don’t re-fire it. plugin is viewer-only (it says how the rows are displayed, not which rows), so exclude it before reproducing the rows: widget.table.view(**{k: v for k, v in cfg.items() if k != "plugin"}). Row clicks and selections never reach Python. Not rate-limited.