I have a vision for a dashboard where you can click on one of several Buttons to perform additive operations, and undo the operation by clicking on the button again. Actually undo is the wrong word, as I do not want the second click to restore it to an original state as that could interfere with the results of another button, but I would like the second click to take out what it put in. As an example I have the following problem where pushing a button labeled Apple
, adds a value of 3.0
to every data point, which updates the plot of circle glyphs. My question is this, “is there a way I can set up the code where pressing the button again will subtract a value of 3.0
?”
from bokeh.models import ColumnDataSource, CustomJS, Button from bokeh.plotting import Figure, output_file, show from bokeh.layouts import column output_file("js_on_change.html") x = [1.2, 2.4, 0.8, 5.6, 12.1, 8.8] y = [3.4, 1.1, 4.2, 6.6, 1.8, 12.1] z = ['Apple', 'Apple', 'Orange', 'Orange', 'Orange', 'Orange'] data = dict(x=x, y=y, z=z) source = ColumnDataSource(data) plot = Figure(plot_width=400, plot_height=400) plot.circle('x', 'y', source=source, size=8, line_alpha=0.6) callback1 = CustomJS(args=dict(source=source), code=""" var data = source.data; var f = 3.0 var x = data['x'] var y = data['y'] for (var i = 0; i < x.length; i++) { y[i] = y[i] + f } source.change.emit(); """) button = Button(label="Apple", button_type="success") button.js_on_click(callback1) layout = column(button, plot) show(layout)
Advertisement
Answer
One possible solution is to add a second CDS to your custom JavaScript to count the clicks.
CustomJS(args=dict(source=source, counter=counter), code="...")
In you JavaScript you have to add a few lines. If the number of clicks is even, you add f
, else you substract it.
var count = counter.data; var c = count['counter'] if (c[0]%2==1) f = -1*f; c[0]++ counter.change.emit();
The created figure will work like this:
Complete code
from bokeh.models import ColumnDataSource, CustomJS, Button, Range1d from bokeh.plotting import Figure, output_notebook, show from bokeh.layouts import column output_notebook() x = [1] y = [3] z = ['Apple'] data = dict(x=x, y=y, z=z) source = ColumnDataSource(data) counter = [0] counter = dict(counter=counter) counter = ColumnDataSource(counter) plot = Figure(plot_width=400, plot_height=400) plot.circle('x', 'y', source=source, size=8, line_alpha=0.6) plot.y_range = Range1d(0,7) callback1 = CustomJS(args=dict(source=source, counter=counter), code=""" var f = 3.0 var count = counter.data; var c = count['counter'] if (c[0]%2==1) f = -1*f; c[0]++ counter.change.emit(); var data = source.data; var x = data['x'] var y = data['y'] for (var i = 0; i < x.length; i++) { y[i] = y[i] + f } source.change.emit(); """) button = Button(label="Apple", button_type="success") button.js_on_click(callback1) layout = column(button, plot) show(layout)