Search code examples
pythonbokeh

How to add a fixed-position tooltip in Bokeh?


My goal is to add an HoverTool to my figure which displays the weekday by name. The date is defined by the x-axis values. I want to display this information at a fixed position even if the visible section is changed by a tool like BoxZoom.

Since the HoverTool needs at least one renderer I first tried to define a line but I did not find a way to define the position relative to the figure. In fact if I zoom it can happen, that this line is not in the visible part and the HoverTool isn't working anymore (or in an area which is not visible).

My second attempt was to define an extra_y_range to draw the line relative. But I did not find a way to unselect the BoxZoom for this axis.

import pandas as pd

from bokeh.plotting import figure, output_notebook, show
from bokeh.models import (
    HoverTool, 
    LinearAxis,
    Range1d,
)
output_notebook()

dr = pd.date_range('2020-01-01', '2020-01-05', freq='D')

p = figure(title="line", plot_width=300, plot_height=300, x_axis_type='datetime')
p.line(x=dr, y=[6, 7, 2, 4, 5])
p.extra_y_ranges.update({"extra": Range1d(0, 1)})
p.add_layout(LinearAxis(y_range_name="extra", axis_label=''), "right")
l = p.line(x=dr, y=0.8, color='gray', **{"y_range_name":"extra"})


p.add_tools(
            HoverTool(
                tooltips=[("", "@x{%A}")],
                renderers=[l],
                mode="vline",
                formatters={"@x": "datetime"},
            ))
show(p)

Display weekday by name

Is there a way to add an HoverTool which stays at the same position in the visible area even if this area is effected by tools?


Solution

  • You can try this solution which works for Bokeh v2.1.1. In the code below the tooltip is fixed at absolute position on the screen. You can add yourself more generic solution independent of the plot position on the screen.

    from bokeh.models import CustomJS
    from bokeh.models import HoverTool
    from bokeh.plotting import show, figure
    import numpy as np
    
    p = figure(plot_width = 300, plot_height = 300, tooltips = [('value X', '@x'), ('value Y', '@y')])
    circles = p.circle(x=np.random.rand(10)*10, y=np.random.rand(10)*10, size=10)
    
    callback = CustomJS(args={'p': p}, code="""
        var tooltips = document.getElementsByClassName("bk-tooltip");
        const tw = 100;
        for (var i = 0; i < tooltips.length; i++) {
            tooltips[i].style.top = '5px'; 
            tooltips[i].style.left = p.width/2 - tw/2 + 'px'; 
            tooltips[i].style.width = tw + 'px'; 
        } """)
    
    hover = p.select_one(HoverTool)
    hover.renderers = [circles]
    hover.callback = callback
    hover.show_arrow = False
    
    show(p)
    

    Result: enter image description here

    For Bokeh v3, change the string bk-tooltip to bk-Tooltip with a capital T.