如何在多个地块上的bokeh中链接CrossHairTool? [英] How do I link the CrossHairTool in bokeh over several plots?

查看:54
本文介绍了如何在多个地块上的bokeh中链接CrossHairTool?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在一个图中移动十字线(尺寸=宽度)时,我想在其他图中看到相同的位置.我的图共享相同的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.xcb_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_locationcb_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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆