Bokeh hovertools which run arbitrary python code

时间:2019-03-19 15:04:13

标签: python bokeh

I am using Bokeh to try and create a figure whose data points when 'hovered' over by the user will display another graph within the hover tool, showing additional information about that data point (i.e., in the main figure data points are the mean of a time-series over a set interval, I want the hover tool to show all the data in that interval).

The user guide (full code copied in below) provides one solution: use a custom HTML tooltip to reference figures on file. This would, however, require me creating all the figures on file (which could be up 10,000) to be referenced. This is too large a time overhead so I was hoping for a better solution. Namely: Is it possible for hover tools to run python code on the fly, such that they can display plots of the data interactively?

(Example image, take from user guide, below code)

The below code was copied from the bokeh user guide on 19th March 2019.

from bokeh.plotting import figure, output_file, show, ColumnDataSource

output_file("toolbar.html")

source = ColumnDataSource(data=dict(
    x=[1, 2, 3, 4, 5],
    y=[2, 5, 8, 2, 7],
    desc=['A', 'b', 'C', 'd', 'E'],
imgs=[
    'https://bokeh.pydata.org/static/snake.jpg',
    'https://bokeh.pydata.org/static/snake2.png',
    'https://bokeh.pydata.org/static/snake3D.png',
    'https://bokeh.pydata.org/static/snake4_TheRevenge.png',
    'https://bokeh.pydata.org/static/snakebite.jpg'
],
fonts=[
    '<i>italics</i>',
    '<pre>pre</pre>',
    '<b>bold</b>',
    '<small>small</small>',
    '<del>del</del>'
]
))

TOOLTIPS = """
<div>
    <div>
        <img
            src="@imgs" height="42" alt="@imgs" width="42"
            style="float: left; margin: 0px 15px 15px 0px;"
            border="2"
        ></img>
    </div>
    <div>
        <span style="font-size: 17px; font-weight: bold;">@desc</span>
        <span style="font-size: 15px; color: #966;">[$index]</span>
    </div>
    <div>
        <span>@fonts{safe}</span>
    </div>
    <div>
        <span style="font-size: 15px;">Location</span>
        <span style="font-size: 10px; color: #696;">($x, $y)</span>
    </div>
</div>
"""

p = figure(plot_width=400, plot_height=400, tooltips=TOOLTIPS,
       title="Mouse over the dots")

p.circle('x', 'y', size=20, source=source)

show(p)

example_hover_tool

1 个答案:

答案 0 :(得分:2)

您只能在Bokeh服务器应用程序中使用Python回调。似乎无法对HoverTool使用Python回调(它必须始终是JS回调,否则会出现此错误:ValueError: expected an instance of type Callback, got <function callback at 0x114fdbb90> of type function)。

以下解决方案使用JS回调,当将鼠标悬停在主图上的圆圈上时,它会显示一个小的“工具提示图”(适用于Bokeh v1.0.4,并且仅在Bokeh文档中有2个图):

from bokeh.plotting import figure, show
from bokeh.layouts import gridplot, Row
from bokeh.models import ColumnDataSource, CDSView, BooleanFilter, CustomJS, BoxSelectTool, HoverTool
import pandas as pd

data = {'x': [1, 2, 3],
        'y':[1, 2, 3],
        'xs':[[9, 8, 7], [6, 5, 4], [3, 2, 1]],
        'ys':[[29, 28, 29], [27, 28, 27], [25, 25, 20]]}
source = ColumnDataSource(data)
plot = figure(title = 'PLOT IN HOVER TOOLTIP', tools = '')
circles = plot.circle('x', 'y', size = 20, source = source)

plot_tooltip = figure(name = 'plot_tooltip', plot_width = 200, plot_height = 200, x_axis_location = None, y_axis_location = None, title = None, tools = 'hover', tooltips = [("x", "@x"), ("y", "@y")], toolbar_location = None)
lines = plot_tooltip.line('x', 'y', source = ColumnDataSource({'x': [], 'y': []}))
circles2 = plot_tooltip.circle('x', 'y', source = ColumnDataSource({'x': [], 'y': []}))

code = """  
var indices = cb_data.index['1d'].indices;
if (indices.length > 0){
    if(plot_tooltip.x_range.bounds == null)
    {
        Bokeh.documents[0].add_root(plot_tooltip)
    }
    const idx = indices[0]
    lines.data_source.data['x'] = source.data['xs'][idx]
    lines.data_source.data['y'] = source.data['ys'][idx]
    lines.data_source.change.emit();

    circles.data_source.data['x'] = source.data['xs'][idx]
    circles.data_source.data['y'] = source.data['ys'][idx]
    circles.data_source.change.emit();  

    div = document.getElementsByClassName('bk-root')[1];
    div.style = "position:absolute; left:" + cb_data.geometry['sx'] + "px; top:" + cb_data.geometry['sy'] + "px;";              
} """

callback = CustomJS(args = dict(source = source, lines = lines, circles = circles2, plot_tooltip = plot_tooltip), code = code)

hover = HoverTool()
hover.callback = callback
hover.tooltips = None
hover.renderers = [circles]
plot.add_tools(hover)

show(plot)

结果:

enter image description here