Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bokeh - how to make HoverTool tooltips stick to points on click?

The code below comes from a jupyter notebook:

from bokeh.io import show, output_notebook
from bokeh.plotting import ColumnDataSource, figure
from bokeh.models import HoverTool, Range1d

output_notebook()
fig = figure(tools=[HoverTool(tooltips=[("html", '@html{safe}')])])

fig.quad(left="left", top="top", bottom="bottom", right="right",
         source=ColumnDataSource({"left": [1,3], "bottom": [1,3],
                                  "right": [2,4], "top": [2,4],
                                  "html":["<b>I'm bold</b>", "<span 

style='color:red;font-size:32px;'>BIG RED TEXT</span>"]}))
    show(fig)

I need to make the HoverTool tooltips stick to exactly where they are on a clicking the point, so if a user wanted to highlight the and copy the text in the tooltip they could. This codepen has an example of the type of behavior I would like to see. I know that this must be possible by either injecting some type of CustomJS or altering BokehJS coffescript and building BokehJS from scratch but I haven't been able to figure it out. Does anybody out there have any idea how to do this?

UPDATE:
It might be possible to a create a custom tool using the tap_tool.coffee, hover_tool.coffee or tooltip.coffee. I'll update this if I figure it out.

like image 355
James Draper Avatar asked Nov 09 '22 03:11

James Draper


1 Answers

This is a workaround creating your own Hover text using models.Label inside the 'callback' function of models.HoverTool . Also models.TapTool is used for toggle updating the Label text. You can't edit the text in the glyph models.Label, but an TextInput widget has been added where the text is updated as one hover the graph glyphs.

import bokeh
import bokeh.plotting
fig = bokeh.plotting.figure()

d_source = bokeh.models.ColumnDataSource({"left": [1,3,1],
                "bottom": [1,3,3],"right": [2,4,2],"top": [2,4,4]})
h_source = bokeh.models.ColumnDataSource({"do_hover":[True,True]  })
fig.quad(left="left", top="top", bottom="bottom", right="right",
         source=d_source)

myToolTip = bokeh.models.Label(x=2.5,y=2.5, text="",
                               background_fill_color = "#ffffff")

HoverCallback = bokeh.models.CustomJS(args=dict(d_source=d_source,
                    myToolTip=myToolTip,h_source=h_source),code="""
        function findWhereIam(x,y,s){
            // To find where the cursor is. 
            selection = -1;
            for (i = 0; i < s.data.left.length; i++) {
                x0 = s.data.left[i];
                x1 = s.data.right[i];
                y0 = s.data.bottom[i];
                y1 = s.data.top[i];
                if (x>x0 && x<x1 && y>y0 && y<y1){
                    // It's inside rectangle!!!
                    selection = i;
                }
            }
            return selection
        }
        if (h_source.data.do_hover[0]){
            x_data = cb_data['geometry'].x;
            y_data = cb_data['geometry'].y;
            var selection = findWhereIam(x_data,y_data,d_source)
            if (selection>=0){
                x0 = d_source.data.left[selection];
                x1 = d_source.data.right[selection];
                y0 = d_source.data.bottom[selection];
                y1 = d_source.data.top[selection];
                myToolTip.x = 0.5 * (x0+x1);
                myToolTip.y = 0.5 * (y0+y1);
                myToolTip.text = "on:"+selection;
                myToolTip.text_font_size = "24pt";
                myToolTip.background_fill_color = "#ffffff";
                myToolTip.border_line_color = "#000000";
                myToolTip.text_align = "center";
                myToolTip.trigger('change');

                current_selection.value = myToolTip.text;
                current_selection.trigger('change');
            }else{
                myToolTip.text = ""; //erase
                myToolTip.trigger('change');
                current_selection.value = myToolTip.text;
                current_selection.trigger('change');
            }
        }
    """)

TapCallback = bokeh.models.CustomJS(args=dict(h_source=h_source), code="""
        h_source.data.do_hover[0] = !h_source.data.do_hover[0];
    """)

current_selection = bokeh.models.widgets.TextInput(value="",
                                                   title="Selection")

HoverCallback.args.update(dict(current_selection=current_selection))

fig.add_tools(bokeh.models.HoverTool(tooltips=None,callback=HoverCallback))
fig.add_tools(bokeh.models.TapTool(callback=TapCallback))
fig.add_layout(myToolTip)
page = bokeh.plotting.gridplot([[fig],[current_selection]])
bokeh.io.output_notebook()
bokeh.io.show(page)

enter image description here

like image 55
Pablo Reyes Avatar answered Nov 14 '22 21:11

Pablo Reyes