如何在 Bokeh 中通过 Python 检测 JS 更新的 ColumnDataSource?

How to detect JS updated ColumnDataSource by Python in Bokeh?

在 Bokeh 中,我有一个 CustomJS 触发回调更新 ColumnDataSource。我想对此触发一些 Python 代码进行任何更新。但是,这无法通过指定 on_change 数据源回调来实现,我也无法访问更新后的数据。我究竟做错了什么?我的代码...

from bokeh.models import ColumnDataSource, CustomJS, Button
from bokeh.layouts import layout
from bokeh.tile_providers import get_provider
from bokeh.plotting import figure
from bokeh.io import curdoc
from bokeh.events import Tap


def callback():
    print('button checks source:')
    print(source.data)


def callback_source(attr, old, new):
    print('source changed!')
    print(source.data)


# define the source, initially empty
source = ColumnDataSource(data=dict(lon=[], lat=[]))

# set a map
tile_provider = get_provider('CARTODBPOSITRON')
initial_bounds = ((-1000000, 4000000), (3000000, 8000000))
plot = figure(
    x_range=initial_bounds[0], y_range=initial_bounds[1],
    x_axis_type="mercator", y_axis_type="mercator"
)
plot.add_tile(tile_provider)

# show the current lon/lat pair in source
plot.circle(x="lon", y="lat", size=15, fill_alpha=0.8, source=source)
plot.js_on_event(Tap, CustomJS(args=dict(source=source), code="""
    // get the source.data contents
    const x = source.data['lon'];
    const y = source.data['lat'];
    // if source.data is empty, append the cursor coords
    if (x.length == 0) {
        x.push(cb_obj.x);
        y.push(cb_obj.y);
    // if source.data is populated, replace the cursor coords
    } else {
        x[0] = cb_obj.x;
        y[0] = cb_obj.y;
    }
    source.change.emit();
"""))
# how to detect changes in source.data which trigger Python code?
source.on_change('data', callback_source)
button = Button(label="show source.data status", button_type="success")
# print the source status
button.on_click(callback)
curdoc().add_root(layout([button, plot]))

引出一个情节,我的点击事件正确地更新了圆圈,但显然没有更新 source 变量。

2022-01-07 08:12:46,924 Starting Bokeh server version 2.4.2 (running on Tornado 6.1)
2022-01-07 08:12:46,927 User authentication hooks NOT provided (default user enabled)
2022-01-07 08:12:46,931 Bokeh app running at: http://localhost:5006/temp_test
2022-01-07 08:12:46,931 Starting Bokeh server with process id: 4797
2022-01-07 08:12:48,212 WebSocket connection opened
2022-01-07 08:12:48,214 ServerConnection created
button checks source:
{'lon': [], 'lat': []}

根据@bigreddot 提供的答案,以下新的 JS 代码(source.change.emit() 不是必需的)有效:

    const x=[cb_obj.x];
    const y=[cb_obj.y];
    const new_data = {lon:x, lat:y};
    source.data = new_data;

要将 属性 更新事件发送回 Python,您实际上必须将一个全新的(“dict”)值分配给 source.data,而不仅仅是更新阵列“到位”。对 source.change.emit() 的调用纯粹是一个 JS 端事件,对 Python 端没有任何影响。