When moving the crosshair (dimensions=width) in one plot I want to see the same position in the other plot(s). My plots share the same x-axis.
Here is the plot setup
Marged solution (updated for Bokeh v1.0.4)
from bokeh.layouts import gridplot
from bokeh.models import CustomJS, CrosshairTool
from bokeh.plotting import figure, ColumnDataSource, output_file, save, show
from bokeh.models import Span, CrosshairTool, HoverTool, ResetTool, PanTool, WheelZoomTool
from datetime import datetime
from datetime import timedelta
import numpy as np
import time
def add_vlinked_crosshairs(fig1, fig2):
js_move = '''if(cb_obj.x >= fig.x_range.start && cb_obj.x <= fig.x_range.end && cb_obj.y >= fig.y_range.start && cb_obj.y <= fig.y_range.end)
{ cross.spans.height.computed_location = cb_obj.sx }
else
{ cross.spans.height.computed_location = null }'''
js_leave = 'cross.spans.height.computed_location = null'
cross1 = CrosshairTool()
cross2 = CrosshairTool()
fig1.add_tools(cross1)
fig2.add_tools(cross2)
args = {'cross': cross2, 'fig': fig1}
fig1.js_on_event('mousemove', CustomJS(args = args, code = js_move))
fig1.js_on_event('mouseleave', CustomJS(args = args, code = js_leave))
args = {'cross': cross1, 'fig': fig2}
fig2.js_on_event('mousemove', CustomJS(args = args, code = js_move))
fig2.js_on_event('mouseleave', CustomJS(args = args, code = js_leave))
def to_seconds(date):
return time.mktime(date.timetuple())
def timeline_figure(title = None, x_range = None, y_range = None):
TOOLS = [CrosshairTool(dimensions = 'height'), PanTool(dimensions = 'width'), HoverTool(tooltips = [("Date", "@t")]), WheelZoomTool(dimensions = 'width'), ResetTool()]
fig = figure(width = 800, height = 250, title = title, x_axis_type = "datetime", x_range = x_range, y_range = y_range, tools = TOOLS)
fig.outline_line_color = 'white'
fig.xgrid.grid_line_color = None
fig.ygrid.grid_line_color = None
fig.yaxis.minor_tick_line_color = None
year = 2016
dec = Span(location = to_seconds(datetime(year - 1, 12, 1, 0, 0, 0)))
jan = Span(location = to_seconds(datetime(year, 1, 1, 0, 0, 0)))
feb = Span(location = to_seconds(datetime(year, 2, 1, 0, 0, 0)))
mar = Span(location = to_seconds(datetime(year, 3, 1, 0, 0, 0)))
apr = Span(location = to_seconds(datetime(year, 4, 1, 0, 0, 0)))
may = Span(location = to_seconds(datetime(year, 5, 1, 0, 0, 0)))
fig.renderers.extend([dec, jan, feb, mar, apr, may])
return fig
def usage():
output_file("test_linked_crosshair.html", mode = "cdn")
d_start = datetime(2016, 6, 1)
d_step = timedelta(days = 1)
t = [d_start + (i * d_step) for i in range(0, 12)]
s1 = np.random. randint(2, 10, 12)
s2 = np.random.randint(2, 10, 12)
source = ColumnDataSource({'t': t, 's1': s1, 's2': s2})
p1 = timeline_figure()
p1.triangle(x = 't', y = 's1', source = source, size = 10, color = "blue")
p2 = timeline_figure(x_range = p1.x_range)
p2.square(x = 't', y = 's2', source = source, size = 10, color = "red")
add_vlinked_crosshairs(p1, p2)
p = gridplot([[p1], [p2]])
show(p)
if __name__ == "__main__":
usage()
Result: