Seb
Seb

Reputation: 1775

Bokeh: how to turn HoverTool on/off from callback?

Is there a way to activate/deactivate a hovertool in a callback?

I tried to do that at the same time as I toggle line visibility using checkboxes. Each line has a hovertool, when a line has "visible=false" I set the "names" attribute of the corresponding tool to a dummy string; otherwise I set it equal to the initial "name" attribute of the line.

It works for the first time any box is checked, the other two line disappear and the hovertool doesn't show on them. But it doesn't work to reactivate the hovertool.

Here is my attempt:

from bokeh.plotting import figure, output_file
from bokeh.models import CustomJS, HoverTool, CheckboxGroup, ColumnDataSource
from bokeh.layouts import gridplot
from bokeh.resources import CDN
from bokeh.embed import file_html

from collections import OrderedDict

TOOLTIPS = [ ("x", "$~x"), ("y", "$~y") ]

TOOLS = ['crosshair']

source = ColumnDataSource(data={'x':range(10),'y0':range(10),'y1':range(10)[::-1],'y2':[i**0.5 for i in range(10)]})

fig = figure(tools=TOOLS)

fig.tools[0].dimensions = 'height'

plots = []
plots.append( fig.line(x='x',y='y0', name="0", source=source) )
plots.append( fig.line(x='x',y='y1', name="1", source=source) )
plots.append( fig.line(x='x',y='y2', name="2", source=source) )


names = ['first','second','third']
ID=0
for plot in plots:
    fig.add_tools( HoverTool(mode='vline',line_policy='interp',renderers=[plot],names=[str(ID)],tooltips=OrderedDict( [('name',names[ID])]+TOOLTIPS )) )
    ID+=1

N_plots = range(len(plots))

checkbox = CheckboxGroup(labels=names,active=[],width=200)
checkbox_iterable =[('p'+str(i),plots[i]) for i in N_plots]+[('hover'+str(i),fig.tools[1:][i]) for i in N_plots]+[('checkbox',checkbox)]
checkbox_code = """var indexOf = [].indexOf;"""+''.join(['p'+str(i)+'.visible = indexOf.call(checkbox.active, '+str(i)+') >= 0;' for i in N_plots])
checkbox_code += ''.join(['if(p'+str(i)+'.visible) hover'+str(i)+'.names = ["'+str(i)+'"];' for i in N_plots])+''.join(['if(p'+str(i)+'.visible==false) hover'+str(i)+'.names = ["no"];' for i in N_plots])
checkbox_code += ''.join(['console.log(hover'+str(i)+'.names);' for i in N_plots])
checkbox.callback = CustomJS(args={key: value for key,value in checkbox_iterable}, code=checkbox_code)

grid = gridplot([[fig,checkbox]],toolbar_location='left')

outfile=open('hovtest.html','w')
outfile.write(file_html(grid,CDN,'test'))
outfile.close()

I use bokeh 0.12.4

Upvotes: 3

Views: 2077

Answers (1)

Seb
Seb

Reputation: 1775

Someone from the bokeh discussion group found a nice way to do this (bokeh discussion thread).

We can alter the display property of the hover tool based on the checkbox.active list. Here is an example code:

from bokeh.plotting import figure, output_file
from bokeh.models import CustomJS, HoverTool, CheckboxGroup, ColumnDataSource
from bokeh.layouts import gridplot
from bokeh.resources import CDN
from bokeh.embed import file_html

from collections import OrderedDict

TOOLTIPS = [ ("x", "$~x"), ("y", "$~y") ]

TOOLS = ['crosshair']

data = {'x':range(10),
        'y0':range(10),
        'y1':range(10)[::-1],
        'y2':[i**0.5 for i in range(10)]
        }

source = ColumnDataSource(data=data)

fig = figure(tools=TOOLS)

fig.tools[0].dimensions = 'height'

plots = []
plots.append( fig.line(x='x',y='y0', name="first", source=source) )
plots.append( fig.line(x='x',y='y1', name="second", source=source) )
plots.append( fig.line(x='x',y='y2', name="third", source=source) )

checkbox = CheckboxGroup(   
    labels=[plot.name for plot in plots],
    active=[0,1,2],
    width=200,
    callback = CustomJS(    args=dict(p0=plots[0],p1=plots[1],p2=plots[2]),
                            code = """
                                p0.visible = cb_obj.active.includes(0);
                                p1.visible = cb_obj.active.includes(1);
                                p2.visible = cb_obj.active.includes(2);
                                """
                        )
    )

for ID,plot in enumerate(plots):
    fig.add_tools(  
        HoverTool(  
            mode='vline',
            line_policy='interp',
            renderers=[plot],
            names=[plot.name],
            tooltips=OrderedDict( [('name',plot.name)]+TOOLTIPS ),
            callback=CustomJS(  args=dict(cb=checkbox),
                                code="""
                                    if(!cb.active.includes(%d)) {
                                    document.getElementsByClassName('bk-tooltip')[%d].style.display = 'none';
                                    }
                                    """ % (ID,ID)
                                )
            ) 
    )

grid = gridplot([[fig,checkbox]],toolbar_location='left')

outfile=open('hovtest.html','w')
outfile.write(file_html(grid,CDN,'hovtest'))
outfile.close()

Upvotes: 2

Related Questions