Reputation: 1477
I have modified this example. What I want- eventually- is a way to get the datapoints selected in a graph and modifiy them in the python code. Thus I added a function here that should return the values from the second graph (thats what the button is for). However, if I select the points, they are plotted correctly, but the data source is not changed (the button click provides {'X':[],'Y':[]}. How do I write back from JS to the python bokeh data source? Isn't the s2.change.emit() or a s2.trigger('change') supposed to do this? I also tried the latter but its not working.
from random import random
from bokeh.layouts import row
from bokeh.models import CustomJS, ColumnDataSource
from bokeh.plotting import figure
from bokeh.models.widgets import Button
from bokeh.io import curdoc
x = [random() for x in range(500)]
y = [random() for y in range(500)]
s1 = ColumnDataSource(data=dict(x=x, y=y))
p1 = figure(plot_width=400, plot_height=400, tools="lasso_select", title="Select Here")
p1.circle('x', 'y', source=s1, alpha=0.6)
s2 = ColumnDataSource(data=dict(x=[], y=[]))
p2 = figure(plot_width=400, plot_height=400, x_range=(0, 1), y_range=(0, 1),
tools="", title="Watch Here")
p2.circle('x', 'y', source=s2, alpha=0.6)
s1.selected.js_on_change('indices', CustomJS(args=dict(s1=s1, s2=s2), code="""
var inds = cb_obj.indices;
var d1 = s1.data;
var d2 = s2.data;
d2['x'] = []
d2['y'] = []
for (var i = 0; i < inds.length; i++) {
d2['x'].push(d1['x'][inds[i]])
d2['y'].push(d1['y'][inds[i]])
}
s2.change.emit();
""")
)
def get_values():
global s2
print(s2.data)
button = Button(label="Get selected set")
button.on_click(get_values)
layout = row(p1, p2,button)
curdoc().add_root(layout)
curdoc().title = "my dashboard"
Upvotes: 1
Views: 2788
Reputation: 8287
This code will display the updated source data from the second plot when the button is clicked. Run it with: bokeh serve --show app.py
from random import random
from bokeh.models import CustomJS, ColumnDataSource, Row
from bokeh.plotting import figure, show, curdoc
from bokeh.models.widgets import Button
x = [random() for x in range(500)]
y = [random() for y in range(500)]
s1 = ColumnDataSource(data = dict(x = x, y = y))
p1 = figure(plot_width = 400, plot_height = 400, tools = "lasso_select", title = "Select Here")
p1.circle('x', 'y', source = s1, alpha = 0.6)
s2 = ColumnDataSource(data = dict(x = [], y = []))
p2 = figure(plot_width = 400, plot_height = 400, x_range = (0, 1), y_range = (0, 1), tools = "", title = "Watch Here")
p2.circle('x', 'y', source = s2, alpha = 0.6)
s1.selected.js_on_change('indices', CustomJS(args = dict(s1 = s1, s2 = s2), code = """
var inds = cb_obj.indices;
var d1 = s1.data;
d2 = {'x': [], 'y': []}
for (var i = 0; i < inds.length; i++) {
d2['x'].push(d1['x'][inds[i]])
d2['y'].push(d1['y'][inds[i]])
}
s2.data = d2 """))
def get_values():
print(s2.data)
button = Button(label = "Get selected set")
button.on_click(get_values)
curdoc().add_root(Row(p1, p2, button))
The source data is not updated in your example because there is no mechanism in JS to detect the push changes. Only assigning a new object to source.data
can be detected. Once the change is detected the data between BokehJS and Python models can be synchronised.
Upvotes: 2