如何在多个地块上的bokeh中链接CrossHairTool? [英] How do I link the CrossHairTool in bokeh over several plots?
问题描述
在一个图中移动十字线(尺寸=宽度)时,我想在其他图中看到相同的位置.我的图共享相同的x轴.
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.
这是情节设置和示例:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from bokeh.plotting import figure, ColumnDataSource, output_file, save
from bokeh.models import Span, CrosshairTool, HoverTool, ResetTool, PanTool, WheelZoomTool
from datetime import datetime
def timeline_figure(title=None, x_range=None, y_range=None):
# TODO: align x-axis
# TOOLS = "resize,crosshair,pan,wheel_zoom,box_zoom,reset,box_select,lasso_select,save"
# TOOLS = "resize,crosshair,xpan,xwheel_zoom,box_zoom,reset,save"
TOOLS = [CrosshairTool(dimensions=['height']),
PanTool(dimensions=['width']),
HoverTool(tooltips=[("Dato", "@Date")]),
WheelZoomTool(dimensions=['width']),
ResetTool()]
# Setting up the bokeh figure
fig = figure(width=800, height=250, title=title, x_axis_type="datetime",
x_range=x_range, y_range=y_range, tools=TOOLS)
# make the outline "invisible"
fig.outline_line_color = 'white'
# change just some things about the x-grid
fig.xgrid.grid_line_color = None
fig.ygrid.grid_line_color = None
# change just some things about the y-grid
fig.yaxis.minor_tick_line_color = None
year = 2016
dec = Span(location=datetime(year-1, 12, 1, 0, 0, 0).timestamp() * 1000,
dimension='height', line_color='grey', line_dash='dashed', line_width=1)
jan = Span(location=datetime(year, 1, 1, 0, 0, 0).timestamp() * 1000,
dimension='height', line_color='grey', line_dash='dashed', line_width=1)
feb = Span(location=datetime(year, 2, 1, 0, 0, 0).timestamp() * 1000,
dimension='height', line_color='grey', line_dash='dashed', line_width=1)
mar = Span(location=datetime(year, 3, 1, 0, 0, 0).timestamp() * 1000,
dimension='height', line_color='grey', line_dash='dashed', line_width=1)
apr = Span(location=datetime(year, 4, 1, 0, 0, 0).timestamp() * 1000,
dimension='height', line_color='grey', line_dash='dashed', line_width=1)
may = Span(location=datetime(year, 5, 1, 0, 0, 0).timestamp() * 1000,
dimension='height', line_color='grey', line_dash='dashed', line_width=1)
fig.renderers.extend([dec, jan, feb, mar, apr, may])
return fig
def usage():
import numpy as np
from datetime import timedelta
from bokeh.io import gridplot
output_file("test.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()
p2.square(x='t', y='s2', source=source, size=10, color="red")
p = gridplot([[p1], [p2]])
save(p)
if __name__ == "__main__":
usage()
感谢您的任何建议.
Karsten
推荐答案
Bokeh当前没有对此的内置支持.我想出了如何使用JavaScript回调来做到这一点.以下功能适用于散景0.13上的两个垂直对齐的图:
Bokeh currently has no built in support for this. I figured out how to do this using javascript callbacks. The following function works for two vertically aligned plots on Bokeh 0.13:
from bokeh.models import CustomJS, CrosshairTool
def add_vlinked_crosshairs(fig1, fig2):
cross1 = CrosshairTool()
cross2 = CrosshairTool()
fig1.add_tools(cross1)
fig2.add_tools(cross2)
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'
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))
这个想法是在每个图上添加一个鼠标移动回调,从而触发要绘制的另一个图上十字线的垂直部分.这是通过使用鼠标回调(cb_obj.sx
)提供的屏幕位置更新十字准线的spans.height.computed_location
成员来完成的.
The idea is to add a mouse move callback to each plot which triggers the vertical part of the crosshair on the other plot to be drawn. This is done by updating the spans.height.computed_location
member of the crosshair with the screen position provided by the mouse callback (cb_obj.sx
).
鼠标移动事件在图的整个区域(包括轴,边界等)上触发.添加检查以确保鼠标位于数据空间内(cb_obj.x
和cb_obj.y
是轴坐标)和直线如果不是,则将其删除.还添加了mouseleave
事件,因为在情节之外快速移动可能不会在边界区域触发事件.
The mouse move event fires on the entire area of the plot, including axes, borders etc. Checks are added to make sure the mouse is inside the data space (cb_obj.x
and cb_obj.y
are axis coordinates) and the line is removed if it is not. A mouseleave
event was also added since a fast move outside of the plot may not fire an event on the border area.
如果绘图垂直对齐,则此方法有效.对于水平对齐(根据OP),只需更改cross.spans.height.computed_location
-> cross.spans.width.computed_location
和cb_obj.sx
-> cb_obj.sy
.
This works if the plots are vertically aligned. For horizontal alignment (as per the OP) just change cross.spans.height.computed_location
-> cross.spans.width.computed_location
and cb_obj.sx
-> cb_obj.sy
.
这仅在地块大小相同时起作用,否则,将需要进一步检查.
This only works if the plots are the same size, further checks will be necessary if they are not.
这篇关于如何在多个地块上的bokeh中链接CrossHairTool?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!