如何在 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 端没有任何影响。
在 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 端没有任何影响。