Cannot trigger a python call at the server when source changes

source_datapoints.on_change(‘data’, do_stuff)

``

I hurriedly posted this question two days ago, but the horribly written JS part confounded the issue and deterred any attempt at replying. Here is a cleaner take.

This code just adds new points when the plot is clicked. How can I trigger a python call at the bokeh server every time the source changes? I thought the
``line at the end should do that, but maybe I’m misunderstanding something.

Thanks,
Alex
Bokeh version 0.12.6 ; Python version 2.7.13

from bokeh.io import curdoc
from bokeh.plotting import Figure
from bokeh.models import ColumnDataSource
from bokeh.models.callbacks import CustomJS
from bokeh import events

p = Figure(plot_width=500, plot_height=300)
source_datapoints = ColumnDataSource(data=dict(x=[0,10], y=[0,10]))
renderer = p.circle(x=‘x’, y=‘y’, size=7, source=source_datapoints, color=“blue”, line_width=3)

def do_stuff(attr, old, new):
print “magic happens here”

def display_event():
return CustomJS(args=dict(source_datapoints_JS=source_datapoints), code="""
if (cb_obj.event_name == ‘tap’) {
source_datapoints_JS.data[‘x’].push(cb_obj.x);
source_datapoints_JS.data[‘y’].push(cb_obj.y);
source_datapoints_JS.change.emit();
}
“”" )
p.js_on_event(events.Tap, display_event())

source_datapoints.on_change(‘data’, do_stuff) # DOES NOT WORK
curdoc().add_root(p)

``

I can reproduce that but have not found the issue yet.

Hi Alex,

I’m afraid I don’t know the answer to your question, but wanted to chime in and say: I have seen the same problem. Will be watching with interest to see if anyone has a resolution.

Cheers,

Sarah

···

On 7 Aug 2017 06:19, “Alex Spiliotopoulos” [email protected] wrote:

source_datapoints.on_change(‘data’, do_stuff)

``

I hurriedly posted this question two days ago, but the horribly written JS part confounded the issue and deterred any attempt at replying. Here is a cleaner take.

This code just adds new points when the plot is clicked. How can I trigger a python call at the bokeh server every time the source changes? I thought the
``line at the end should do that, but maybe I’m misunderstanding something.

Thanks,
Alex
Bokeh version 0.12.6 ; Python version 2.7.13

from bokeh.io import curdoc
from bokeh.plotting import Figure
from bokeh.models import ColumnDataSource
from bokeh.models.callbacks import CustomJS
from bokeh import events

p = Figure(plot_width=500, plot_height=300)
source_datapoints = ColumnDataSource(data=dict(x=[0,10], y=[0,10]))
renderer = p.circle(x=‘x’, y=‘y’, size=7, source=source_datapoints, color=“blue”, line_width=3)

def do_stuff(attr, old, new):
print “magic happens here”

def display_event():
return CustomJS(args=dict(source_datapoints_JS=source_datapoints), code=“”"
if (cb_obj.event_name == ‘tap’) {
source_datapoints_JS.data[‘x’].push(cb_obj.x);
source_datapoints_JS.data[‘y’].push(cb_obj.y);
source_datapoints_JS.change.emit();
}
“”" )
p.js_on_event(events.Tap, display_event())

source_datapoints.on_change(‘data’, do_stuff) # DOES NOT WORK
curdoc().add_root(p)

``

You received this message because you are subscribed to the Google Groups “Bokeh Discussion - Public” group.

To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].

To post to this group, send email to [email protected].

To view this discussion on the web visit https://groups.google.com/a/continuum.io/d/msgid/bokeh/2e75047c-f51e-4dd2-9b1c-fc3c1d4230bb%40continuum.io.

For more options, visit https://groups.google.com/a/continuum.io/d/optout.

I am not sure why it is not working with:

source_datapoints.on_change(‘data’, do_stuff)

``

But it works with a dummy source if you change your line like this (ill put a context line and the added line):

source_datapoints = ColumnDataSource(data=dict(x=x_points, y=np.sin(x_points)))

  • dum_source=ColumnDataSource(data={‘dum’:[0]})

``

  • return CustomJS(args=dict(p=p, source_datapoints_JS=source_datapoints), code="""
  • return CustomJS(args=dict(p=p, source_datapoints_JS=source_datapoints,dum_source=dum_source), code="""

``

source_datapoints_JS.change.emit();

  • var now = new Date();
  • dum_source.data = {‘dum’:[now]};

``

  • source_datapoints.on_change(‘data’, do_stuff) # DOES NOT WORK
  • dum_source.on_change(‘data’,do_stuff) # WORKS

``

YES!!! Thank you, it works great! I had spent a lot of time trying to figure out what was wrong, thanks so much!

from bokeh.io import curdoc

``
from bokeh.plotting import Figure

from bokeh.models import ColumnDataSource

from bokeh.models.callbacks import CustomJS

from bokeh import events

p = Figure(plot_width=500, plot_height=300)

source_datapoints = ColumnDataSource(data=dict(x=[0,10], y=[0,10]))

dummy_source = ColumnDataSource(data={‘dummy’:})

renderer = p.circle(x=‘x’, y=‘y’, size=7, source=source_datapoints, color=“blue”, line_width=3)

def do_stuff(attr, old, new):

print "magic happens here"

def display_event():

return CustomJS(args=dict(source_datapoints_JS=source_datapoints, dummy_source=dummy_source),  code="""

if (cb_obj.event_name == 'tap') {

    source_datapoints_JS.data['x'].push(cb_obj.x);

    source_datapoints_JS.data['y'].push(cb_obj.y);

    source_datapoints_JS.change.emit();

    dummy_source.data =  {'dummy':[new Date()]};

}

""" )

p.js_on_event(events.Tap, display_event())

source_datapoints.on_change(‘data’, do_stuff) # DOES NOT WORK

dummy_source.on_change(‘data’,do_stuff) # IT WORKS!!!

curdoc().add_root(p)

···

On Monday, August 7, 2017 at 3:50:16 PM UTC-5, Sébastien Roche wrote:

I am not sure why it is not working with:

source_datapoints.on_change(‘data’, do_stuff)

``

But it works with a dummy source if you change your line like this (ill put a context line and the added line):

source_datapoints = ColumnDataSource(data=dict(x=x_points, y=np.sin(x_points)))

  • dum_source=ColumnDataSource(data={‘dum’:[0]})

``

  • return CustomJS(args=dict(p=p, source_datapoints_JS=source_datapoints), code=“”"
  • return CustomJS(args=dict(p=p, source_datapoints_JS=source_datapoints,dum_source=dum_source), code=“”"

``

source_datapoints_JS.change.emit();

  • var now = new Date();
  • dum_source.data = {‘dum’:[now]};

``

  • source_datapoints.on_change(‘data’, do_stuff) # DOES NOT WORK
  • dum_source.on_change(‘data’,do_stuff) # WORKS

``

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