This code plots a holoviews curve, then tries to pick up the underlying bokeh figure and add a glyph to that. The curve plots ok, but the glyph is nowhere to be found. Any idea on how to make that visible?
import numpy as np
import holoviews as hv
import panel as pn
from bokeh.models import ColumnDataSource, TeXGlyph
hv.extension('bokeh')
xs = np.linspace(-5,5,100)
cv = hv.Curve((xs,-(xs-2)**2))
hv_pane = pn.pane.HoloViews(cv)
p = hv.render(cv, backend='bokeh')
source = ColumnDataSource(dict(x=[0], y=[-30], text=["$$\frac{1}{x}$$"]))
glyph = TeXGlyph(x="x",y="y",text="text")
p.add_glyph(source, glyph)
pn.template.MaterialTemplate(
site="Panel",
title="Getting Started App",
sidebar=[],
main=[hv_pane],
).servable();
Once you render it, it’s no longer linked to the HoloViews object. What you need to do instead is either using hooks Customizing Plots — HoloViews v1.20.2 or use pn.pane.Bokeh()
Indeed, many thanks for your response. I have a module that creates a matplotlib plot, but I think Bokeh would be a more appropriate backend so I’m adding that now. It needs to create plot with many lines having different nr of datapoints and some annotations in the plot with math symbols. I made the test code below that tests the functionality I need, though I think I can not easily change the axes to flip between Linear and Log, but that can be handled separately.
import panel as pn
import numpy as np
import holoviews as hv
from bokeh.plotting import figure
from bokeh.models import ColumnDataSource, TeXGlyph, Label
hv.extension('bokeh')
pn.extension('katex','mathjax')
class MyPlot:
def __init__(self):
# Create Bokeh figure
self.fig=figure(height=400, width=600)
self.fig.toolbar.logo = None
# Create text & data source
self.data = {'x-values':[], 'y-values':[]}
self.source = ColumnDataSource(data=self.data)
self.text_source = ColumnDataSource(dict(x=[4],y=[0],text=["$${1}\over{x}$$"]))
# Add multi line
self.fig.multi_line(xs='x-values',ys='y-values',source=self.source)
# Add math label glyph to plot
self.glyph = TeXGlyph(x="x",y="y",text="text", text_font_size="16px")
self.fig.add_glyph(self.text_source, self.glyph)
label = Label(
text="$$y = \sin(x)\$$",
x=150, y=130,
x_units="screen", y_units="screen",
)
self.fig.add_layout(label)
# Add axis labels
self.fig.yaxis.axis_label = r"\[\sin(x)\]"
self.fig.xaxis.axis_label = r"\[x\pi\]"
return
def update_plot(self, event):
xcoords_lines = []
ycoords_lines = []
nr_lines = np.random.randint(1,8)
for i in range(nr_lines):
nn = np.random.randint(20,50)
off = np.random.rand()*2*np.pi
xx = np.linspace(0,2*np.pi,nn)+off
yy = np.cos(xx+off)
xcoords_lines.append(xx)
ycoords_lines.append(yy)
self.source.data['x-values'] = xcoords_lines
self.source.data['y-values'] = ycoords_lines
self.text_source.data['x'] = [3.0 + np.random.rand()]
self.text_source.data['y'] = [np.random.rand()-0.5]
return
# Holoviews plot
points = [(0.1*i, np.sin(0.1*i)) for i in range(100)]
plot_widget = hv.Curve(points).opts(show_grid=True)
hv_pane = pn.pane.HoloViews(plot_widget, height=400, width=600)
# Bokeh plot & update button
cc = MyPlot()
button_widget = pn.widgets.Button(name='Static Text', button_type='primary')
button_widget.on_click(cc.update_plot)
# Panel layout
main_pn = pn.Row(hv_pane, cc.fig)
pn.template.MaterialTemplate(
site="Panel",
title="Getting Started App",
sidebar=[button_widget],
main=[main_pn],
).servable();