如何将Python回调添加到pointdrawtool

时间:2019-04-05 11:53:52

标签: python callback bokeh

对于python / bokeh来说还是很新的,因此表示歉意。我正在尝试使用pointdrawtool在bokeh服务器生成的绘图上向我的图形添加箭头。我可以通过添加共享相同columndatasource的不可见圆圈来添加它,因此会绘制箭头,但是我想通过回调调整箭头起点,使它们成为箭头,而不仅仅是三角形。

我已经尝试过在这里和其他地方所见过的各种尝试,但到目前为止我都失败了。我对什么可以和不能产生回调没有很好的了解。如果有更好的简单方法,那也很好。

from bokeh.server.server import Server
from bokeh.application import Application
from bokeh.application.handlers.function import FunctionHandler
from bokeh.layouts import layout, row, column
from bokeh.plotting import figure, output_file, show, save, reset_output
from bokeh.models import Label, BoxAnnotation, CustomJS, Slider, Button, ColumnDataSource, BoxEditTool, FreehandDrawTool,PointDrawTool, Arrow, NormalHead
from bokeh.models.widgets import Select
import pandas as pd
import numpy as np
import webbrowser as wb
def make_document(doc):
    try:
        #set the dimensions for the plot
        x_start=1600
        x_end=2530
        y_start=1800
        y_end=5300
        #### set up figure
        p = figure(plot_width=1000, plot_height=600, x_range=(x_start,x_end),
        y_range=(y_end,y_start), tools="pan, wheel_zoom,box_zoom,reset, undo,
        redo")
        #### set up annotation color and thickness:
        thick_ann=10.0
        col_ann="yellow"
        alpha_ann=0.7
        ### source dataset and associated code for for any added arrows 
        #source_ar=ColumnDataSource( {"xs":[0,0],"ys":[0,3],"xe":[1,1], "ye":[1,4]})
        source_ar=ColumnDataSource( {"xs":[],"ys":[],"xe":[], "ye":[]})
        a1=Arrow(end=NormalHead(size=thick_ann*3, fill_color=col_ann, line_color=col_ann, line_alpha=alpha_ann, fill_alpha=alpha_ann),x_start='xs', y_start='ys', x_end='xs', y_end='ys', source=source_ar, line_color=col_ann, line_width=thick_ann, line_alpha=alpha_ann)
        p.add_layout(a1)
        ### add invisible circle - use this to add and remove arrows 
        c1=p.circle('xs','ys', size=thick_ann*3,alpha=0.0, source=source_ar)
        artool=PointDrawTool(renderers=[c1])
        p.add_tools(artool)
        #### example callback I think I want to run when adding an arrow via the tool - adjust start
        #### values so is actual arrow
        def arr_callback(attr, old, new):
            source_ar.data["xe"][-1]=source_ar.data["xs"][-1] +5
            source_ar.data["ye"][-1]=source_ar.data["ys"][-1] +5
        #c1.glyph.data_source.on_change('selected',arr_callback)
        doc.add_root(p)
    except:
        server.stop()
apps = {'/': Application(FunctionHandler(make_document))}
server = Server(apps, port=5003)
server.start()
wb.open('http://localhost:5003', new=2)

预期结果-添加一个点,该点添加一个不可见的圆,还绘制了一个箭头,然后调整了起点,因此它是箭头而不是三角形。

2 个答案:

答案 0 :(得分:1)

我对Bokeh很陌生,但今天早上我想这样做,并提出了解决方案:

from bokeh.plotting import figure, output_notebook, show
from bokeh.models import ColumnDataSource, PointDrawTool
from bokeh.events import Tap

output_notebook()

def bkapp(doc):

    p = figure(x_range=(0, 10), y_range=(0, 10), width=400, 
               height=400, tools=[])

    source = ColumnDataSource({
             'x': [1, 5, 9], 'y': [1, 5, 9], 'color': ['red', 'green', '                yellow']
             })

    def callback(event):
        source.data["x"].append(event.x)
        source.data["y"].append(event.y)
        print(source.data)

    renderer = p.scatter(x="x", y="y", source=source, color="color", 
                         size=10)
    draw_tool = PointDrawTool(renderers=[renderer], 
                              empty_value='black')
    p.on_event(Tap, callback)

    p.add_tools(draw_tool)
    p.toolbar.active_tap = draw_tool

    doc.add_root(p)

show(bkapp)

这正在使用Jupyter笔记本中的嵌入式服务器。 我只是在画点...

有帮助吗?

答案 1 :(得分:0)

据我所知,一般情况下,仅JS回调可以添加到Bokeh中的工具中(CrossHairTool。TapTool等。)。不幸的是,为什么某些工具根本不支持回调(如ResetTool或PointDrawTool等),目前尚无据可查。尝试将回调附加到PointDrawTool会出错。

但是,如果您只想在每个鼠标上单击添加新箭头,则另一种选择是使用例如附在绘图画布上的JS回调(请参见下面的Bokeh v1.0.4代码)。以python app.py

的身份运行代码
from tornado.ioloop import IOLoop
from bokeh.server.server import Server
from bokeh.application import Application
from bokeh.application.handlers.function import FunctionHandler
from bokeh.plotting import figure, show
from bokeh.models import CustomJS, ColumnDataSource, Arrow, NormalHead, Segment

def make_document(doc):
        p = figure(plot_width = 1000, plot_height = 600, x_range = (0, 10),
        y_range = (0, 6), tools = "pan, wheel_zoom,box_zoom,reset,undo,redo")
        #### set up annotation color and thickness:
        thick_ann = 10.0
        col_ann = "red"
        alpha_ann = 0.7
        #### source dataset and associated code for for any added arrows
        source = ColumnDataSource(data = {"xs":[1, 2, 3], "ys":[1, 2, 3], "xe":[4, 5, 6], "ye":[1, 2, 3], 'width': [30] * 3, 'color': [col_ann] * 3 })
        a1 = Arrow(end = NormalHead(size = thick_ann * 3, fill_color = col_ann, line_color = col_ann, line_alpha = alpha_ann, fill_alpha = alpha_ann), x_start = 'xs', y_start = 'ys', x_end = 'xe', y_end = 'ye', source = source, line_color = col_ann, line_alpha = alpha_ann)
        s1 = p.segment(x0 = 'xs', y0 = 'ys', x1 = 'xe', y1 = 'ye', color = 'color', source = source)
        p.add_layout(a1)

        code = """  new_x = Number(cb_obj.x);
                    new_y = Number(cb_obj.y);
                    data = {xe: [new_x], ys: [new_y], ye: [new_y]};
                    data['xs'] = [Number(data['xe']) - 3];
                    data['color'] = ['red'];
                    data['width'] = [90];
                    source.stream(data); """
        p.js_on_event('tap', CustomJS(args = dict(source = source), code = code))
        doc.add_root(p)

io_loop = IOLoop.current()
server = Server(applications = {'/': Application(FunctionHandler(make_document))}, io_loop = io_loop, port = 5001)
server.start()
server.show('/')
io_loop.start()

结果:

enter image description here