This is a follow up to this discussion.
Is there, or perhaps should there be, a proper way to apply a bokeh theme to a plot in a pane? For the sake of clarity, I am working in a jupyterlab notebook.
I can get bokeh to show a themed object as follows:
from bokeh.io import curdoc, output_notebook, show
from bokeh.plotting import figure
from bokeh.themes import built_in_themes
output_notebook()
theme = built_in_themes["dark_minimal"]
doc = curdoc()
doc.theme = theme
fruits = ['Apples', 'Pears', 'Nectarines', 'Plums', 'Grapes', 'Strawberries']
counts = [5, 3, 4, 2, 4, 6]
p = figure(x_range=fruits, plot_height=250, title="Fruit counts",
toolbar_location=None, tools="", align="center")
p.vbar(x=fruits, top=counts, width=0.9)
p.xgrid.grid_line_color = None
p.y_range.start = 0
show(p)
Similarly for holoviews:
import holoviews as hv
from holoviews import opts
hv.extension("bokeh")
hv.renderer('bokeh').theme = theme
plot = hv.Bars((fruits, counts))
plot
But panel does not respect the bokeh theme:
import panel as pn
pn.extension()
pn.pane.Bokeh(p)
I also tried this unsuccessfully:
theme.apply_to_model(p)
pn.pane.Bokeh(p)
Although, the previously set holoviews theme is respected by panel:
pn.pane.HoloViews(plot)
A work around was provided in the same discussion mentioned above:
pn.viewable._Document.theme = theme
pn.pane.Bokeh(p)
However, to quote the concerns mentioned in the discussion:
It works, but is ugly and involves accessing “private” attribute and making module-wide change
Digging around, I also noticed that pn.io.save.file_html
accepts a theme. And as a quick side note, while this renders fine:
from IPython.display import display, HTML
from bokeh.resources import CDN
html = pn.io.save.file_html(p, resources=CDN, theme=theme)
HTML(html)
It does not render well with panel unless the height is manually specified:
pn.pane.HTML(html)
In my imagination allowing pn.pane.Bokeh
to accept a theme parameter would make sense.
In short, is there a better way to apply a bokeh theme to a bokeh object in a panel bokeh pane?
Update while still typing this post… I tried this, and it worked:
import param
from bokeh.themes import Theme
from panel.pane.plot import Bokeh
class ThemedBokeh(Bokeh):
theme = param.ClassSelector(default=None, class_=(Theme, str),
allow_None=True, doc="""
Bokeh theme to apply to the plot.""")
def _get_model(self, doc, root=None, parent=None, comm=None):
model = super()._get_model(doc, root=root, parent=parent, comm=comm)
if self.theme:
model._document.theme = self.theme
return model
Then simply:
ThemedBokeh(p, theme=theme)
I think that really is a testament to how well structured this stack of technology is.
I don’t know if there are negative repercussions or potential side effects to this approach. If this is an appropriate approach, I’d be happy to submit a PR that either adds the theme parameter to the Bokeh pane class, or implements a ThemedBokeh class.
Any suggestions or feedback would be appreciated.