Bokeh - how to make HoverTool tooltips stick to po

2019-03-21 07:35发布

问题:

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.

回答1:

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)