在散景中的Tap事件上更新DataTable

时间:2019-06-25 18:56:45

标签: python bokeh

我正在尝试计算两点的欧式距离。初始距离在数据中计算。

然后,当用户移动线条时,我希望distance列根据新坐标进行更新。我可以看到xy列正在更新,但是distance列没有更新。以下是我的尝试:

output_file("tools_point_draw.html")

_tools_to_show = 'box_zoom,pan,save,hover,reset,tap'        

p = figure(x_range=(0, 10), y_range=(0, 10), tools=_tools_to_show,
           plot_width=862, plot_height=604,
           title='Plot name')

p.background_fill_color = 'white'

d_true = {'x': [2, 3], 'y': [4, 1], 
          'color': ['red', 'red'],
          'desc': ['true','true']}

df_true = pd.DataFrame(d_true)
df_true['distance'] = np.sqrt(np.sum((df_true['x'] - df_true['y'])**2))
source = ColumnDataSource(df_true)

renderer2 = p.scatter(x='x', y='y', source=source, color='color', size=15,
                      line_color='red', line_width=5)
renderer = p.line(x='x', y='y', source=source, color='red',
                  line_dash='dashed', line_width=10)

columns = [TableColumn(field="x", title="I am X"),
           TableColumn(field="y", title="I am Y"),
           TableColumn(field='color', title='color'),
           TableColumn(field='desc', title='desc'),
           TableColumn(field='distance', title='distance')]

update = CustomJS(args=dict(source_data=source), code="""
    var data = source_data.data;
    var f = cb_obj.value; //is this necessary?

    //Sum of squares for euclidean
    for(var i = 0, i < data['x'].length; i < size ; i++) {
        var res += Math.pow(data['x'][i] - data['y'][i], 2)
    }

    //Take square root
    var res2 = Math.sqrt(res)

    //Update table
    data['distance'] = res2

    source_data.change.emit();

    """)

update.js_on_change('tap', update)

table = DataTable(source=source, columns=columns, editable=True, height=200, width=862)

draw_tool = PointDrawTool(renderers=[renderer, renderer2], empty_value='black')
p.add_tools(draw_tool)
p.toolbar.active_tap = draw_tool

show(Column(p, table))

2 个答案:

答案 0 :(得分:2)

您的回调实际上从未在这里触发。这只是pointdraw事件在做事情。 更改source.data时,应具有回调触发器。

source.js_on_change('data', update)

我是从第一个点开始做的,但是你也可以从原点做。

如果是从第一个点开始,则每次都需要更新所有距离(因为绘图工具可让您拖动现有点)

from bokeh.plotting import figure
from bokeh.io import output_file, show
from bokeh.models import DataTable, TableColumn, Column, PointDrawTool, ColumnDataSource, CustomJS
import pandas as pd
import numpy as np

output_file("tools_point_draw.html")

_tools_to_show = 'box_zoom,pan,save,hover,reset,tap'        

p = figure(x_range=(0, 10), y_range=(0, 10), tools=_tools_to_show,
           plot_width=862, plot_height=604,
           title='Plot name')

p.background_fill_color = 'white'

d_true = {'x': [2, 3], 'y': [4, 1], 
          'color': ['red', 'red'],
          'desc': ['true','true']}

df_true = pd.DataFrame(d_true)
df_true['distance'] = [0]+[np.sqrt((df_true['x'][i]-df_true['x'][i-1])**2+(df_true['y'][i]-df_true['y'][i-1])**2) for i in range(1,len(df_true['x']))]
source = ColumnDataSource(df_true)

renderer2 = p.scatter(x='x', y='y', source=source, color='color', size=15,
                      line_color='red', line_width=5)
renderer = p.line(x='x', y='y', source=source, color='red',
                  line_dash='dashed', line_width=10)

columns = [TableColumn(field="x", title="I am X"),
           TableColumn(field="y", title="I am Y"),
           TableColumn(field='color', title='color'),
           TableColumn(field='desc', title='desc'),
           TableColumn(field='distance', title='distance')]

update = CustomJS(args=dict(source_data=source), code="""
    var data = source_data.data;
    var res = 0;

    //Sum of squares for euclidean
    for(var i = 1; i < data['x'].length ; i++) {
        res += Math.sqrt(Math.pow(data['x'][i] - data['x'][i-1], 2)+Math.pow(data['y'][i] - data['y'][i-1], 2));

        //Update table
        data['color'][i] = 'red';
        data['desc'][i] = 'true';
        data['distance'][i] = res;
    }

    source_data.change.emit();
    """)

source.js_on_change('data', update)

table = DataTable(source=source, columns=columns, editable=True, height=200, width=862)

draw_tool = PointDrawTool(renderers=[renderer, renderer2])
p.add_tools(draw_tool)
p.toolbar.active_tap = draw_tool

show(Column(p, table))

答案 1 :(得分:0)

虽然找不到方法更新表,但我只是添加了一个Div并更新了其中的文本。不优雅,但是可以完成工作。

div_euclid = Div(text="""
               <b>Diameter of predicted form is:</b> 334.80 <br>
               <b>Diameter of true form is:</b> 368.64 <br>
               <b>RMSE is:</b> 34.13
               """, 
               align='center',
               style={'font-size': '100%'})

p.js_on_event(events.MouseMove,      
            CustomJS(args=dict(div=div_euclid, 
                              source_data_pred=src_pred,
                              source_data_true=src_true),
     code="""
     var data_p = source_data_pred.data;
     var data_t = source_data_true.data;

     var x_p = data_p['x']
     var y_p = data_p['y']

     var x_t = data_t['x']
     var y_t = data_t['y']

     var diam_p = 0
     var diam_t = 0
     var rmse = 0

     //Diameter of pred form
     diam_p = Math.sqrt(Math.pow((x_p[0]-x_p[1]),2) + Math.pow((y_p[0]-y_p[1]),2))

     //Diameter of true form
     diam_t = Math.sqrt(Math.pow((x_t[0]-x_t[1]),2) + Math.pow((y_t[0]-y_t[1]),2))

     //RMSE
     rmse = Math.sqrt(Math.pow(diam_p - diam_t,2)/1)

     //Result
     div.text = "<b>Diameter of predicted form is: </b>" + diam_p.toFixed(2) + "<br> <b>Diameter of true form is: </b>" + diam_t.toFixed(2) + " <br> <b>RMSE is: </b>" + rmse.toFixed(2);

     """))