1

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.

enter image description here

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:

  1. 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;
  2. 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.
  3. 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.

Azrael_DD
  • 171
  • 9

0 Answers0