pythonhtmlpanelholoviews

How to make HoloMap to explore PNG images; <img> components of DIV not displaying


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".

enter image description here

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.

enter image description here


Solution

  • 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)
    

    Select widget showing option A, above image of holoviews icon