I want to highlight the sample over which I´m hovering. I did managed to solve this with a custom JSCallback. See the minimal working example below.
My question is, why do I need to call source.change.emit()
in order to update the location of my Span? If I remove the source.change.emit()
in my callback, the location of the horizontal line does not change at all (visually). So far I thought, I only need to call this command, if I actually change the data inside the source object (like source.data['Value1'] = <new_value>
.
import pandas as pd
from bokeh.plotting import figure, curdoc
from bokeh.models import ColumnDataSource, HoverTool, CustomJS, Span
from bokeh.layouts import layout
plot1 = figure(plot_width=1000, plot_height=250)
df = pd.DataFrame({"ID":[0, 1, 2, 3, 4, 5, 6, 7],
"Value1":[0, 100, 200, 300, 400, 500, 600, 700],
"Value2":[0, 1, 2, 4,8 , 16, 32, 64]})
source = ColumnDataSource(df)
line = plot1.line(x='ID', y='Value1', source=source)
circle = plot1.circle(x='ID', y='Value1', source=source)
v_line = Span(location=2, dimension='height', line_color='green',
line_dash='dashed', line_width=3)
plot1.add_layout(v_line)
callback = CustomJS(args=dict(source=source, v_line=v_line),
code="""
v_line.location = cb_data['index'].line_indices[0];
source.change.emit();
""")
hover_tool_plot = HoverTool(mode='vline', line_policy='nearest', callback=callback, renderers=[line])
plot1.add_tools(hover_tool_plot)
layout_ = layout([[plot1]])
curdoc().add_root(layout_)
In my application, I deal with a lot of data in multiple plots having the same source object. So when I include the source.change.emit()
part in the callback the Span loacation changes, but the visual hover annoation gets noticeably slower and also skips some samples because of this non-smoothness. Is there a way to update the location of the Span without calling source.change.emit()
?
Thanks in advance.
Edit:
I'm running on Bokeh 2.3.0.
If you move outside the begin/end of the line, the location
then line_indices
is empty, and setting from line_indices[0]
is undefined. In this case things stop updating because undefined
is not a valid thing to set location
to. Perhaps this is what you are seeing?
This version of the code updates and keeps the tooltip and span in sync without an emit
call with Bokeh 2.3 (note: please always, always specify relevant software versions in every question)
callback = CustomJS(args=dict(source=source, v_line=v_line),
code="""
const loc = cb_data['index'].line_indices[0]
if (loc === undefined) {
v_line.visible = false
} else {
v_line.visible = true
v_line.location = loc;
}
""")
hover_tool_plot = HoverTool(mode='vline', line_policy='prev',
callback=callback, renderers=[line])
Note I have also changed line_policy
to prev
because I believe that is what line_indices
will always report, regardless. It's possible this could be made more flexible, but no one has ever asked about it before. Feel free to propose a feature request on github.