With Bokeh in Python, it is easy to create informative tooltips for the data in plots. How can I create and customize hover tooltips for widget elements in the resulting webpage?
All the searching about hover tooltips for Bokeh lead to plot tooltips. But I want to convey additional information to the user about the widgets in my app, specifically information about slider and select widgets.
Using this official example as a starting point, I got the basics working: https://github.com/bokeh/bokeh/blob/main/examples/server/app/sliders.py
In the screenshot, my mouse is hovering over the frequency slider and my toolip is displayed.
These are my files:
- main.py
- templates
- index.html
- styles.css
main.py:
''' Present an interactive function explorer with slider widgets.
Scrub the sliders to change the properties of the ``sin`` curve, or
type into the title text box to update the title of the plot.
Use the ``bokeh serve`` command to run the example by executing:
bokeh serve sliders.py
at your command prompt. Then navigate to the URL
http://localhost:5006/sliders
in your browser.
'''
import numpy as np
from bokeh.io import curdoc
from bokeh.layouts import column, row
from bokeh.models import ColumnDataSource, Slider, HoverTool
from bokeh.plotting import figure
# Set up data
N = 200
x = np.linspace(0, 4*np.pi, N)
y = np.sin(x)
source = ColumnDataSource(data=dict(x=x, y=y))
# Set up plot
plot = figure(height=400, width=400, title="Widget Hover Tooltip Test",
tools="crosshair,pan,reset,save,wheel_zoom",
x_range=[0, 4*np.pi], y_range=[-2.5, 2.5])
plot.line('x', 'y', source=source, line_width=3, line_alpha=0.6)
hover = HoverTool(point_policy='snap_to_data', # 'follow_mouse'
mode='vline',
)
plot.add_tools(hover)
# Set up widgets
offset = Slider(title="offset", value=0.0, start=-5.0, end=5.0, step=0.1,
css_classes=['tooltip', 'offset'])
amplitude = Slider(title="amplitude", value=1.0, start=-5.0, end=5.0, step=0.1,
css_classes=['tooltip', 'amplitude'])
phase = Slider(title="phase", value=0.0, start=0.0, end=2*np.pi,
css_classes=['tooltip', 'phase'])
freq = Slider(title="frequency", value=1.0, start=0.1, end=5.1, step=0.1,
css_classes=['tooltip', 'freq'])
# Set up callbacks
def update_data(attrname, old, new):
# Get the current slider values
a = amplitude.value
b = offset.value
w = phase.value
k = freq.value
# Generate the new curve
x = np.linspace(0, 4*np.pi, N)
y = a*np.sin(k*x + w) + b
source.data = dict(x=x, y=y)
for w in [offset, amplitude, phase, freq]:
w.on_change('value', update_data)
# Set up layouts and add to document
inputs = column(offset, amplitude, phase, freq)
curdoc().add_root(row(inputs, plot, width=800))
curdoc().title = "Sliders"
The only relevant difference is me adding two css classes to each slider widget, e.g.
css_classes=['tooltip', 'amplitude']
. One is for generally denoting a tooltip, another specific to each slider.
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
{{ bokeh_css }}
{{ bokeh_js }}
<style type="text/css">{% include 'styles.css' %}</style>
</head>
<body>
{{ plot_div|indent(8) }}
{{ plot_script|indent(8) }}
</body>
</html>
styles.css:
.tooltip:hover:before, .tooltip:hover:after {
display:block;
z-index:99;
}
.offset:before {
content: "Change the offset of the sine wave";
}
.amplitude:before {
content: "Change the amplitude of the sine wave";
}
.phase:before {
content: "Change the phase of the sine wave";
}
.freq:before {
content: "Change the freqency of the sine wave";
}
.tooltip:before {
position:absolute;
/* vertically center */
top:50%;
transform:translateY(-50%);
/* move to right */
left:101%;
margin-left:15pt; /* and add a small left margin */
/* basic styles */
width:300px;
padding:10px;
border: solid;
border-width: thin; /* <- This does not work */
border-radius:1px;
border-color:grey;
background:white;
/*color: #31a9d6;*/
color: black;
text-align:center;
display:none; /* hide by default */
}
/* This shows left of the slider, not left of the tooltip,
because I do not know how to reference the tooltip */
.tooltip:after {
content: " ";
position: absolute;
top: 50%;
right: 100%; /* To the left of the tooltip */
margin-top: -5px;
border-width: 5px;
border-style: solid;
border-color: transparent black transparent transparent;
}
To the actual questions, i.e. my remaining issues:
- I would like to copy the style of the tooltips available for plots, but I cannot draw a border. It seems like another style setting forces
border:0;
- I cannot display the tooltip arrow. All the general examples on "tooltips via css" I can find use another css class for the tooltip itself, which is defined in html.
- Currently I have defined the tooltip text for each tip in the styles.css. If possible, I would like to define them in the python code.
Any help is appreciated.