Adding a HTML widget with images and updating it similar to using the hover tool

Dear all,
I am generating some scatter plots with numerical features extracted from images.
I created a custom tooltip with html/css, such that the actual image from which the features were extracted is shown in the tooltip when I hover over a data point.
The image url is taken from a data frame column named “img”.
This is how I create the custom tooltip:
def generateCustomTooltip(columnnames=[‘treatment’, ‘date’]):
tooltip ="""




        """
for col in columnnames:
    tooltip += """<div>
                     <span style="font-size: 13px;">""" + col + ": @"+col+"""</span>
                   </div>"""
      
tooltip +=  """
      <div>
            <span style="font-size: 15px;">Location</span>
            <span style="font-size: 10px; color: #696;">($x, $y)</span>
        </div>
    </div>
    """
return(tooltip)

This works fine, however, displaying the image in a tooltip can be a bit crammed.
I would prefer to add a html widget displayed next to the plot that is updated in the same way the hovertool is.
I have looked at the example “CustomJS for User Interaction Events” on page JavaScript callbacks — Bokeh 2.4.2 Documentation and tried to modify
the example code by including an but the image doesn’t get rendered in the “Div” widget.

I also don’t know whether replacement of placeholders such as @img with values from the data source column “img” works in CustomJS widgets.

I’m struggling with this as I’m reasonably well versed in python programming but am not familiar with Javascript.

Any help would be appreciated.

Volker

···

Hey Volker, did you have any luck with that? I am trying to do the same …

···

Am Mittwoch, 5. Juli 2017 16:49:38 UTC+2 schrieb [email protected]:

Dear all,
I am generating some scatter plots with numerical features extracted from images.
I created a custom tooltip with html/css, such that the actual image from which the features were extracted is shown in the tooltip when I hover over a data point.
The image url is taken from a data frame column named “img”.
This is how I create the custom tooltip:
def generateCustomTooltip(columnnames=[‘treatment’, ‘date’]):
tooltip =“”"




        """
for col in columnnames:
    tooltip += """<div>
                     <span style="font-size: 13px;">""" + col + ": @"+col+"""</span>
                   </div>"""
      
tooltip +=  """
      <div>
            <span style="font-size: 15px;">Location</span>
            <span style="font-size: 10px; color: #696;">($x, $y)</span>
        </div>
    </div>
    """
return(tooltip)

This works fine, however, displaying the image in a tooltip can be a bit crammed.
I would prefer to add a html widget displayed next to the plot that is updated in the same way the hovertool is.
I have looked at the example “CustomJS for User Interaction Events” on page http://bokeh.pydata.org/en/latest/docs/user_guide/interaction/callbacks.html and tried to modify
the example code by including an but the image doesn’t get rendered in the “Div” widget.

I also don’t know whether replacement of placeholders such as @img with values from the data source column “img” works in CustomJS widgets.

I’m struggling with this as I’m reasonably well versed in python programming but am not familiar with Javascript.

Any help would be appreciated.

Volker

Hi Moritz,

Here’s a sample code (meant to be used with “bokeh serve”, but could be easily adapted for static HTML files or something else):

from bokeh.io import curdoc
from bokeh.layouts import row
from bokeh.models import Div, HoverTool, CustomJS, ColumnDataSource
from bokeh.plotting import figure

ht = HoverTool()

f = figure(tools=[ht])
ds = ColumnDataSource(data=dict(x=[1, 2, 3],
                                y=[2, 3, 1],
                                src=['https://images.pexels.com/photos/326875/pexels-photo-326875.jpeg?w=800&h=600&auto=compress&cs=tinysrgb',
                                     'https://images.pexels.com/photos/37337/cat-silhouette-cats-silhouette-cat-s-eyes.jpg?w=800&h=600&auto=compress&cs=tinysrgb',
                                     'https://images.pexels.com/photos/87413/animal-cat-domestic-eye-87413.jpeg?w=800&h=600&auto=compress&cs=tinysrgb']))
f.circle(x='x', y='y', source=ds, radius=0.5)

div = Div(text="")

ht.callback = CustomJS(args=dict(div=div, ds=ds), code="""
    const hit_test_result = cb_data.index;
    const indices = hit_test_result._1d.indices;
    if (indices.length > 0) {
        div.text = `<img src="${ds.data['src'][indices[0]]}"/>`;
    }
""")

curdoc().add_root(row(f, div))

Note that _1d in there is suitable for most of the glyphs, but not for all of them. Also, in some cases (e.g. for the line glyph), the index values there will not mean what you want.

More details are available here https://github.com/bokeh/bokeh/blob/master/bokeh/models/sources.py#L23-L54 and in particular implementations of _hit_* methods of glyphs, like https://github.com/bokeh/bokeh/blob/master/bokehjs/src/coffee/models/glyphs/line.ts#L47

Regards,

Eugene

···

On Monday, February 5, 2018 at 6:18:52 AM UTC+7, Moritz Lürig wrote:

Hey Volker, did you have any luck with that? I am trying to do the same …

Am Mittwoch, 5. Juli 2017 16:49:38 UTC+2 schrieb [email protected]:

Dear all,
I am generating some scatter plots with numerical features extracted from images.
I created a custom tooltip with html/css, such that the actual image from which the features were extracted is shown in the tooltip when I hover over a data point.
The image url is taken from a data frame column named “img”.
This is how I create the custom tooltip:
def generateCustomTooltip(columnnames=[‘treatment’, ‘date’]):
tooltip =“”"




        """
for col in columnnames:
    tooltip += """<div>
                     <span style="font-size: 13px;">""" + col + ": @"+col+"""</span>
                   </div>"""
      
tooltip +=  """
      <div>
            <span style="font-size: 15px;">Location</span>
            <span style="font-size: 10px; color: #696;">($x, $y)</span>
        </div>
    </div>
    """
return(tooltip)

This works fine, however, displaying the image in a tooltip can be a bit crammed.
I would prefer to add a html widget displayed next to the plot that is updated in the same way the hovertool is.
I have looked at the example “CustomJS for User Interaction Events” on page http://bokeh.pydata.org/en/latest/docs/user_guide/interaction/callbacks.html and tried to modify
the example code by including an but the image doesn’t get rendered in the “Div” widget.

I also don’t know whether replacement of placeholders such as @img with values from the data source column “img” works in CustomJS widgets.

I’m struggling with this as I’m reasonably well versed in python programming but am not familiar with Javascript.

Any help would be appreciated.

Volker

This worked like a charm, thanks very much!

···

Am Mittwoch, 5. Juli 2017 16:49:38 UTC+2 schrieb [email protected]:

Dear all,
I am generating some scatter plots with numerical features extracted from images.
I created a custom tooltip with html/css, such that the actual image from which the features were extracted is shown in the tooltip when I hover over a data point.
The image url is taken from a data frame column named “img”.
This is how I create the custom tooltip:
def generateCustomTooltip(columnnames=[‘treatment’, ‘date’]):
tooltip =“”"




        """
for col in columnnames:
    tooltip += """<div>
                     <span style="font-size: 13px;">""" + col + ": @"+col+"""</span>
                   </div>"""
      
tooltip +=  """
      <div>
            <span style="font-size: 15px;">Location</span>
            <span style="font-size: 10px; color: #696;">($x, $y)</span>
        </div>
    </div>
    """
return(tooltip)

This works fine, however, displaying the image in a tooltip can be a bit crammed.
I would prefer to add a html widget displayed next to the plot that is updated in the same way the hovertool is.
I have looked at the example “CustomJS for User Interaction Events” on page http://bokeh.pydata.org/en/latest/docs/user_guide/interaction/callbacks.html and tried to modify
the example code by including an but the image doesn’t get rendered in the “Div” widget.

I also don’t know whether replacement of placeholders such as @img with values from the data source column “img” works in CustomJS widgets.

I’m struggling with this as I’m reasonably well versed in python programming but am not familiar with Javascript.

Any help would be appreciated.

Volker

Hi there,

two years later, I tried to reproduce some of my old figures, with goal of showing images as I hover over points, but the code doesn’t seem to work anymore with 2.0.2. - doesn anyone know whats going on?

Here is a plot that I made back then (with bokeh 0.12): Bokeh Plot

I would like to create similar plots again.

from bokeh.io import show
from bokeh.layouts import row, column
from bokeh.models import Div, HoverTool, CustomJS, ColumnDataSource
from bokeh.plotting import figure

ht = HoverTool()

f = figure(tools=[ht])
ds = ColumnDataSource(data=dict(x=[1, 2, 3],
                                y=[2, 3, 1],
                                src=['https://images.pexels.com/photos/326875/pexels-photo-326875.jpeg?w=800&h=600&auto=compress&cs=tinysrgb',
                                     'https://images.pexels.com/photos/37337/cat-silhouette-cats-silhouette-cat-s-eyes.jpg?w=800&h=600&auto=compress&cs=tinysrgb',
                                     'https://images.pexels.com/photos/87413/animal-cat-domestic-eye-87413.jpeg?w=800&h=600&auto=compress&cs=tinysrgb']))
f.circle(x='x', y='y', source=ds, radius=0.5)

div = Div(text="")

ht.callback = CustomJS(args=dict(div=div, ds=ds), code="""
    const hit_test_result = cb_data.index;
    const indices = hit_test_result._1d.indices;
    if (indices.length > 0) {
        div.text = `<img src="${ds.data['src'][indices[0]]}"/>`;
    }
""")

# curdoc().add_root(row(f, div))

# make figure and layout in static html
p = figure(title="test", 
           tools=['box_select', 'reset', 'zoom_in', 'zoom_out', 'box_zoom', "tap", ht]) 
           # these are the tools we want to include to interact with the responsive surface
p.scatter(x='x', y='y', source=ds, size=8)
layout = column(row(p, div))

# render html
show(layout)

That’s a really cool plot. For a start, the old way of specifying selections was deprecated and eventually removed:

ah I see, thanks for pointing that out.

after changing hit_test_result._1d.indices to hit_test_result.indices it worked again

1 Like