HI All,
I am trying to implement an interactive plot where the user can add points to a
multi-segments line.
So far I managed to do this:
from bokeh.plotting import figure, output_notebook, show
from bokeh.models import ColumnDataSource, PointDrawTool
from bokeh.events import Tap
import numpy as np
output_notebook()
def bkapp(doc):
p = figure(x_range=(0, 10), y_range=(0, 10), width=400, height=400)
source = ColumnDataSource({'x': [1, 5, 9], 'y': [1, 5, 9]})
def add_point(event):
x = np.array(source.data["x"])
y = np.array(source.data["y"])
indices = np.argsort(x)
x = np.sort(x)
y = y[indices]
data = {"x": list(x), "y": list(y)}
source.data = data
renderer = p.scatter(x="x", y="y", source=source, size=10)
draw_tool = PointDrawTool(renderers=[renderer], empty_value='black')
p.line(x="x", y="y", source=source)
p.on_event(Tap, add_point)
p.add_tools(draw_tool)
p.toolbar.active_tap = draw_tool
doc.add_root(p)
show(bkapp)
That seems to work just fine… My issue is that I would like the the python callback to be called when a point is deleted or moved.
Any ideas?
R
Bryan
August 13, 2020, 4:59am
2
Hi @rbeucher lease edit your post to use code formatting so that the code is intelligible (either with the </>
icon on the editing toolbar, or triple backtick ``` fences around the code blocks)
1 Like
Bryan
August 13, 2020, 6:48pm
3
@rbeucher the main function of edit tools like PointDrawTool
is to update a data source, which is how points are added or deleted. So, what you want is a callback on the source, e.g
source.on-change('data', ...)
Hi @Bryan
Thanks. I tried that. The issue is that this results in some recursion that I don’t want…
from bokeh.plotting import figure, output_notebook, show
from bokeh.models import ColumnDataSource, PointDrawTool
from bokeh.events import Tap
import numpy as np
output_notebook()
def bkapp(doc):
p = figure(x_range=(0, 10), y_range=(0, 10), width=400, height=400)
source = ColumnDataSource({
'x': [1, 5, 9], 'y': [1, 5, 9]})
def add_point(attr, old, new):
x = np.array(source.data["x"])
y = np.array(source.data["y"])
indices = np.argsort(x)
x = np.sort(x)
y = y[indices]
data = {"x": list(x), "y": list(y)}
source.data = data
print("OK")
renderer = p.scatter(x="x", y="y", source=source, size=10)
draw_tool = PointDrawTool(renderers=[renderer], empty_value='black')
p.line(x="x", y="y", source=source)
source.on_change("data", add_point)
p.add_tools(draw_tool)
p.toolbar.active_tap = draw_tool
doc.add_root(p)
show(bkapp)
Bryan
August 14, 2020, 7:07pm
5
@rbeucher Right, you are updating the data, in a callback on data updates. Is that actually something you need to do? If you just need be able to add/delete points, then the tool does that for you, including updating the data source as necessary.
Well I want to sort the points and have them available via Python