solara icon indicating copy to clipboard operation
solara copied to clipboard

feat: Bokeh support as FigureBokeh

Open rileythai opened this issue 10 months ago • 0 comments

All Submissions:

  • [X] docs/examples
  • [X] pre-commit
  • [ ] tests

TODO

  • [ ] fix printing issue
  • [ ] change a to heatmap example instead of a scatter; use better dataset
  • [ ] document common issues and their relevant workarounds

Description of changes

This PR resolves #202, and adds a solara.lab component to support Bokeh figure and Plot objects. It additionally adds dependencies for bokeh and jupyter_bokeh.

Part of the code is based on the implementation written in #202 by @maartenbreddels.

It behaves similarly to FigurePlotly, except it shows a temporary SpinnerSolara until the plot is ready while the BokehJS backend loads.

Additionally it takes arguments of light_theme and dark_theme to update the theme via a solara-side effect (the document instance must exist for us to update theming via the high-level interface)

This PR adds:

├── solara
│  └── lab
│     └── components
│        ├── bokehloaded.vue
│        └── figurebokeh.py
└───── website
      ├── pages
      │  ├── apps
      │  └────  scatter-bokeh.py
      └──── documentation
            └── examples
               ├── fullscreen
               │  └── scatter_bokeh.py
               └── visualization
                  └── bokeh.py

and modifies relevant files for importing components in lab

Known issues/pitfalls

  • Bokeh does not support an axis type change, so changing axes via high-level interface x_axis_type, etc doesnt work.
    • log workaround: update x_scale and/or y_scale of figure via a use_effect
    • categorical workaround: scale/map data server-side (partially implemented in scatter-bokeh.py example for the colorbar) + update tickers directly.
  • JS-side selection events on a Tool will fail to link to attributes of any bokeh.model if it is regenerated with each render.
    • occurs bokeh.model.tools instance with a callback like CustomJS(args=dict(object=object)), such as object.selected.indices or similar from a ColumnDataSource upon a selection.
    • suggested approach: use_memo models with callbacks to exclude from regeneration, then use use_effect or similar for attribute updates.
  • ~~When embedded in a GridDraggable instance, theming is not applied on first render after it is added.~~
  • The Bokeh.document instance attempts to cleanup widgets that no longer exist.
    • this happens ocassionally, but leads to tracebacks that clog entire logs because it spits the entire figure object as text. See below:
Traceback (most recent call last):
  File "/ipywidgets/widgets/widget.py", line 516, in __del__
    self.close()
  File "/jupyter_bokeh/widgets.py", line 92, in close
    self._document.remove_on_change(self)
  File "/bokeh/document/document.py", line 592, in remove_on_change
    self.callbacks.remove_on_change(*callbacks)
  File "/bokeh/document/callbacks.py", line 331, in remove_on_change
    del self._change_callbacks[callback]
KeyError: BokehModel(render_bundle={'docs_json': ... # entire figure object comes out here

rileythai avatar Feb 07 '25 07:02 rileythai