I have a more elegant solution to this problem and it may be of help to someone.
Sebastien’s solution was answering perfectly my question (how to trigger a server python call from JS), but I realized that was only half of my problem, because the ColumnDataSource on the server was not aware of the source changes made at the JS side. But it pointed me to the right direction, because it showed that the
source.on_change(‘data’, do_stuff)
``
is being triggered when source.data is assigned new values, but it is not being triggered when source.data is partially changed. For example this solution, which reloads the whole datasource, works (albeit inefficiently):
from bokeh.io import curdoc
from bokeh.plotting import Figure
from bokeh.models import ColumnDataSource
from bokeh.models.callbacks import CustomJS
p = Figure(plot_width=500, plot_height=300)
source_datapoints = ColumnDataSource(data=dict(x=[0,10], y=[0,10]))
p.circle(x=‘x’, y=‘y’, size=7, source=source_datapoints, color=“blue”, line_width=3)
def do_stuff(attr, old, new):
print new
def display_event():
return CustomJS(args=dict(source_datapoints=source_datapoints), code="""
if (cb_obj.event_name == ‘tap’) {
source_datapoints.data[‘x’].push(cb_obj.x);
source_datapoints.data[‘y’].push(cb_obj.y);
source_datapoints.data = {
‘x’ : source_datapoints.data[‘x’],
‘y’ : source_datapoints.data[‘y’]
};
source_datapoints.change.emit();
}
“”" )
p.js_on_event(“tap”, display_event())
source_datapoints.on_change(‘data’, do_stuff)
curdoc().add_root(p)
``
However, there is a cleaner way to add to the datasource like this:
from bokeh.io import curdoc
from bokeh.plotting import Figure
from bokeh.models import ColumnDataSource
from bokeh.models.callbacks import CustomJS
p = Figure(plot_width=500, plot_height=300)
source_datapoints = ColumnDataSource(data=dict(x=[0,10], y=[0,10]))
source_update = ColumnDataSource(data=dict(x=, y=))
p.circle(x=‘x’, y=‘y’, size=7, source=source_datapoints, color=“blue”, line_width=3)
def do_stuff(attr, old, new):
source_datapoints.stream(new)
print len(source_datapoints.data[‘x’])
def display_event():
return CustomJS(args=dict(source_update=source_update), code="""
if (cb_obj.event_name == ‘tap’) {
source_update.data = {
‘x’ : [cb_obj.x],
‘y’ : [cb_obj.y]
};
}
“”" )
p.js_on_event(“tap”, display_event())
source_update.on_change(‘data’, do_stuff)
curdoc().add_root(p)
``
This solution utilizes the source_update as the carrier of the coordinates of the new datapoint. Updating it in JS, triggers the
source_update.on_change(‘data’, do_stuff)
``
and using the stream method in do_stuff the original source_datapoints is updated.
One can use the patch method in a similar fashion to update the datasource in a more complicated way than just adding points at the end (http://bokeh.pydata.org/en/latest/docs/reference/models/sources.html)
Hope it helps,
Alex