I would like to make a simple app to explore a set of PNG files. In general, there will be several selection widgets (e.g. for sex and handedness), and a PNG file to display for each combination of selections.
I am trying to do this using HoloMap
with a dictionary of holoviews.Div
objects, so that the interactivity does not depend on having a live Python server.
The individual cells of the HoloMap display correctly, but the interactive HoloMap
does not display the image components of the Div
objects.
To demonstrate, I make a HoloMap
to explore two PNG files, A or B.
import holoviews as hv
hv.extension("bokeh") # To render in Notebook environment.
# Define format template for html div to display a figure.
# See https://holoviews.org/reference/elements/bokeh/Div.html.
div_format = """
<figure>
<img src=" {pic} " height='200' width='200'>
<figcaption> {caption} </figcaption>
"""
# Map to URLs of two images.
pic_dict = {"A": "https://assets.holoviews.org/logo/holoviews_color_icon_500x500.png",
"B": "https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png"}
# Map to holoviews div objects for the images.
div_map = {key: hv.Div(div_format.format(pic=pic,
caption="Figure "+key)) \
for key, pic in pic_dict.items()}
holomap = hv.HoloMap(div_map, kdims="Figure")
holomap["A"]
# Shows PNG A and caption "Figure A".
Alternatively, we can also display holomap["B"]
.
The holomap
as a whole lets us interactively explore the figure captions (with a selection widget it generates). However, the images themselves are not displayed in the interactive HoloMap
--is this a bug? If so, is there a work-around? Or a better way to explore a set of images?
holomap
# Shows interactive display with figure caption and select widget, but no PNG.
Instead of using HoloMap
, an interactive image explorer could potentially be built directly from bokeh widgets, with a little javascript to change the image to reflect user selection.
This solution uses bokeh
and javascript
directly instead of relying on holoviews to do that under the hood.
For example, to explore two PNG files by selecting 'A' or 'B':
from bokeh.models import CustomJS, Div, Select
from bokeh.layouts import column
from bokeh.io import output_notebook, show
# Map to URLs of two images.
pic_dict = {"A": "https://assets.holoviews.org/logo/holoviews_color_icon_500x500.png",
"B": "https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png"}
option_keys = list(pic_dict.keys())
option_values = list(pic_dict.values())
# Make widget to display html, set to show first URL.
bk_html = Div(text='<img src="' + option_values[0] + '"/>')
# Make widget to select key from pic_dict.
bk_select = Select(options=option_keys, value=option_keys[0], title="Image selection")
# Link select-widget value to html-widget text, to show selected image.
bk_select.js_on_change(
'value',
CustomJS(
args={"html": bk_html},
code=f"""
console.log('Entering bk_select jscallback.');
// Array of URLs corresponding to this.options.
const option_lookup = {option_values};
// Look up URL corresponding to chosen option.
var option_index = this.options.indexOf(this.value);
var url = option_lookup[option_index];
// Set HTML text to display image at URL.
html.text = '<img src="' + url + '"/>';
console.log('Set image to ', html.text);
"""
)
)
# Lay out widgets in a column.
layout = column(bk_select, bk_html)
# To display Bokeh output inline in a Jupyter notebook:
output_notebook(hide_banner=True)
# Display the layout.
show(layout)