Panel to HoloViews (Javascript)

Interacting between Panel widgets and HoloViews objects using Javascript.

Pros:

  • No server necessary

  • Interactivity retained after saving to HTML

Cons:

  • File size may become large

References:

Float slider for glyph fill alpha

This same concept can be applied for any styling including, but not limited to, size, fill_color, fill_alpha, line_width, etc.

[1]:
import holoviews as hv
import panel as pn

hv.extension("bokeh")
pn.extension()

x = [0, 1, 2]
y = [3, 4, 5]

widget = pn.widgets.FloatSlider(value=1, step=0.01)
plot = hv.Points((x, y)).opts(size=10)

link = widget.jslink(plot, value='glyph.fill_alpha')

pn.Column(widget, plot)
[1]:

Text input for title text

[2]:
import holoviews as hv
import panel as pn

hv.extension("bokeh")
pn.extension()

x = [0, 1, 2]
y = [3, 4, 5]
label = "Label"

widget = pn.widgets.TextInput(value=label)
plot = hv.Curve((x, y)).opts(title=label)

link = widget.jslink(plot, value="plot.title.text")

pn.Column(widget, plot)
[2]:

Text input for axis label text

This same concept can be applied for the y-axis.

[3]:
import holoviews as hv
import panel as pn

hv.extension("bokeh")
pn.extension()

x = [0, 1, 2]
y = [3, 4, 5]
label = "Label"

widget = pn.widgets.TextInput(value=label)
plot = hv.Curve((x, y)).opts(xlabel=label)

link = widget.jslink(plot, value="xaxis.axis_label")

pn.Column(widget, plot)
[3]:

Float slider for label size

This same concept can be applied for both x and y axes.

[4]:
import holoviews as hv
import panel as pn

hv.extension("bokeh")
pn.extension()

x = [0, 1, 2]
y = [3, 4, 5]
title = "Title"

widget = pn.widgets.FloatSlider(value=5, start=5, end=10)
plot = hv.Curve((x, y)).opts(title=title)

jscode = """
    plot.title.text_font_size = String(source.value) + 'pt'
"""
link = widget.jslink(plot, code={'value': jscode})

pn.Column(widget, plot)
[4]:

Radio button group for label alignment

[5]:
import holoviews as hv
import panel as pn

hv.extension("bokeh")
pn.extension()

x = [0, 1, 2]
y = [3, 4, 5]
title = "Title"

widget = pn.widgets.RadioButtonGroup(options=["left", "center", "right"])
plot = hv.Curve((x, y)).opts(title=title)

link = widget.jslink(plot, value="plot.title.align")

pn.Column(widget, plot)
[5]:

Range slider for axis limits

This same concept can be applied for the y-axis.

[6]:
import holoviews as hv
import panel as pn

hv.extension("bokeh")
pn.extension()

x = [0, 1, 2]
y = [3, 4, 5]

widget = pn.widgets.RangeSlider(start=min(x), end=max(x))
plot = hv.Curve((x, y))

jscode = """
    x_range.start = cb_obj.value[0];
    x_range.end = cb_obj.value[1];
"""
link = widget.jslink(plot, code={'value': jscode})

pn.Column(widget, plot)
[6]:

Range slider for colorbar limits

[7]:
import panel as pn
import holoviews as hv

hv.extension('bokeh')
pn.extension()

x = [0, 1, 2]
y = [3, 4, 5]
z = [[6, 7, 8], [9, 10, 11], [12, 13, 14]]

widget = pn.widgets.RangeSlider(start=min(min(z)), end=max(max(z)))
plot = hv.Image((x, y, z)).opts(colorbar=True)

jscode = """
    color_mapper.low = cb_obj.value[0];
    color_mapper.high = cb_obj.value[1];
"""
link = widget.jslink(plot, code={'value': jscode})

pn.Column(widget, plot)
[7]:

Float slider for data modification

[8]:
import holoviews as hv
import panel as pn

hv.extension("bokeh")
pn.extension()

x = [0, 1, 2]
y = [0, 0, 0]

widget = pn.widgets.FloatSlider(value=0, start=0, end=2, step=0.5)
plot = hv.Curve((x, y)).opts(ylim=(-1, 3))

jscode = """
    var data = cds.data
    var y = data['y']
    for (var i = 0; i < y.length; i++) {
        y[i] = cb_obj.value
    }
    cds.change.emit();
"""
link = widget.jslink(plot, code={'value': jscode})

pn.Column(widget, plot)
[8]:

Select for colorbar colors

Note, a greater number of cmaps is proportional to a larger file size!

[9]:
import panel as pn
import holoviews as hv

hv.extension("bokeh")
pn.extension()

x = [0, 1, 2]
y = [3, 4, 5]
z = [[6, 7, 8], [9, 10, 11], [12, 13, 14]]

cmaps = ["RdBu_r", "Reds"]
nums = [5, 11, 255]
# convert each named cmap to list of hexcodes for javascript to understand
cmaps_colors = {
    f"{cmap}_n{num}": hv.plotting.util.process_cmap(cmap, num)
    for cmap in cmaps for num in nums
}

widget = pn.widgets.Select(options=list(cmaps_colors))
plot = hv.Image((x, y, z)).opts(colorbar=True, cmap=cmaps_colors["RdBu_r_n5"])

jscode = """
    color_mapper.palette = cmap_dict[source.value];
"""
link = widget.jslink(plot, code={"value": jscode}, args={"cmap_dict": cmaps_colors})

pn.Column(widget, plot)
[9]: