Hi everyone,
I would like to link a selection of a HoloViews plot to a Panel Markdown, where more information about the selection should be displayed. I also want to use a JS Callback for it, so I can create a stand-alone HTML file. I also looked through the documentation of HoloViews and Panel and it is well documented how to connect two HoloViews objects (https://holoviews.org/user_guide/Linking_Plots.html) and how to connect two Panel objects or connect a Panel widget to a HoloViews object (both descript here https://panel.holoviz.org/user_guide/Links.html). However I could not find out how to connect the other way around, from an HoloViews object to a Panel object.
Here is a mini example to make clear what I want to achieve. In this app, you can select one of the points in the left plot and according to this selection, the text to the right is changing. Instead of a panel markdown object, I used here two HoloViews text objects, but a panel markdown object would be nicer for formatting the text (I also included a panel markdown object in the example called text_markdown
, if someone wants to give it a shot how to link it to the HoloViews selection).
import pandas as pd
import panel as pn
import holoviews as hv
from holoviews.plotting.links import Link
from holoviews.plotting.bokeh import LinkCallback
from bokeh.models import HoverTool
hv.extension('bokeh')
pn.extension()
# creating some artifical Data
data = {'Lon': [-134.349, -146.888, -90.87697, -37.8032],
'Lat': [58.38, 61.299, 79.5146, 65.6957],
'name': ['Lemon Creek Glacier', 'Columbia Glacier',
'White Glacier', 'Mittivakkat Glacier'],
'description': ['Pure ice glacier', 'Calving glacier',
'Pure ice glacier', 'Ice cap']}
df = pd.DataFrame.from_dict(data)
# defining the links and callbacks between the HoloViews objects
class HeaderLink(Link):
_requires_target = True
class HeaderCallback(LinkCallback):
source_model = 'selected'
source_handles = ['cds']
on_source_changes = ['indices']
target_model = 'glyph'
source_code = """
var inds = source_selected.indices
if (inds.length == 0)
target_glyph.text = 'Nothing selected!'
else
target_glyph.text = source_cds.data['name'][inds[0]]
"""
HeaderLink.register_callback('bokeh', HeaderCallback)
class DescriptionLink(Link):
_requires_target = True
class DescriptionCallback(LinkCallback):
source_model = 'selected'
source_handles = ['cds']
on_source_changes = ['indices']
target_model = 'glyph'
source_code = """
var inds = source_selected.indices
if (inds.length == 0)
target_glyph.text = ' '
else
target_glyph.text = source_cds.data['description'][inds[0]]
"""
DescriptionLink.register_callback('bokeh', DescriptionCallback)
# define a hover
tooltips = [
('Lon', '@Lon'),
('Lat', '@Lat'),
('Name', '@name')
]
hover = HoverTool(tooltips=tooltips)
# create the actual plotting elements
points = hv.Scatter(df,
kdims=['Lon'],
vdims=['Lat', 'name', 'description']
).opts(default_tools=['tap', 'reset', hover],
size=10,
xaxis=None,
yaxis=None)
header_text = hv.Text(0, 0.5, 'Nothing selected!'
).opts(text_font_size='20pt',
default_tools=['reset'])
description_text = hv.Text(0, 0.25, ' '
).opts(default_tools=['reset'])
text_markdown = pn.pane.Markdown("## Heading Panel markdown \n" +
"Description Panel markdown\n\n" +
"<b>Not linked right Now!</b>")
# connect the HoloViews objects with the links
HeaderLink(points, header_text)
DescriptionLink(points, description_text)
# insert a Link from the points to text_markdown here
# probably should contain JS code something like:
# var inds = source_selected.indices
# if (inds.length == 0)
# target.text = '## Nothing selected!'
# else
# target.text = ('## ' + source_cds.data['name'][inds[0]] +
# '\n' + source_cds.data['description][inds[0]])
# put the plots together in the final app
app = pn.Row(points,
(header_text * description_text
).opts(xlim=(-2, 2),
ylim=(0, 0.6),
xaxis=None,
yaxis=None,
toolbar=None),
text_markdown)
# save as a stand-alone HTML
app.save('app.html', embed=True)
I appreciate any help with how this JS Link from the HoloViews selection to the Panel Markdown could be done. Thank you!