Hi,
I would like to use a Button widget with a confirm dialog (i.e. “do you really want to do this ? YES/NO”).
Based on the user’s choice in the confirm dialog, I would like to get a callback in the Python code similar to a typical “on_click” event.
So far I have tried and failed with the following approaches:
(1) The Dialog widget [1] appears to do exactly what I want but I can’t get it to work.
I get the JS error “String property ‘content’ given invalid value: Row(7437aac2-…)” with the following code:
#!/usr/bin/env python
import numpy as np
#from bokeh.io import vplot
from bokeh.layouts import column
from bokeh.layouts import row, widgetbox
from bokeh.models import CustomJS, Slider, Paragraph, Dialog, Button
from bokeh.plotting import figure, output_file, show, ColumnDataSource
x = np.linspace(0, 10, 500)
y = np.sin(x)
source = ColumnDataSource(data=dict(x=x, y=y))
plot = figure(y_range=(-10, 10), plot_width=400, plot_height=400)
plot.line(‘x’, ‘y’, source=source, line_width=3, line_alpha=0.6)
descr_box = Paragraph(text=‘content loading…’)
btn_close_loading = Button(label=‘Close Loading’)
dialog_loading = Dialog(
title=‘loading’, content=row(descr_box), name=‘loading_dialog’,
buttons=[btn_close_loading], visible=True)
layout = column(
dialog_loading,
plot,
widgetbox(btn_close_loading),
)
output_file(“slider.html”, title=“slider.py example”)
show(layout)
``
(2) I tried and failed to use a Javascript callback function that changes a ColumnDataSource object like it is described in the bokeh documentation [2].
Within the JavaScript code I emit a ‘change’ trigger to be able to react on it from within the Python code.
The idea is that while running the bokeh server, I can add an ‘on_change’ callback function to the data source allowing me to get the result of the JavaScript dialog.
While the Javascript Dialog appears as soon as the button is pressed, I never get a callback for the changed ColumnDataSource. In addition, as soon as I add a CustomJS callback
parameter to the Toggle button, the “on_click” callback is no longer working. Neither are any other Python callbacks:
#!/usr/bin/env python
``
from future import print_function
from bokeh.util.browser import view
from bokeh.document import Document
from bokeh.plotting import curdoc
from bokeh.models.widgets import Toggle
from bokeh.models.widgets import RadioButtonGroup
from bokeh.models.widgets.dialogs import Dialog
from bokeh.models.widgets.buttons import Button
from bokeh.models import CustomJS, ColumnDataSource
from bokeh.layouts import widgetbox
from bokeh.client import push_session
x=[0]
y=x
confirmResponse = ColumnDataSource(data=dict(answer=x, y=y))
callback = CustomJS(args=dict(confirmResponse=confirmResponse), code="""
// get data source from Callback args
var data = confirmResponse.get('data');
data['answer']
var confirmResponse = confirm("Really do it?")
if (confirmResponse) {
data['answer']=1;
} else {
data['answer']=0;
}
// trigger update of data source
confirmResponse.trigger('change');
""")
def toggle_handler(active):
print("toggle_handler: %s, confirmResponse: %s" % (active,x))
toggle = Toggle(label=“Toggle button”, button_type=“success”, callback=callback)
toggle.on_click(toggle_handler)
def default_handler(attrname, old, new):
print ("default handler with attribute %s" %attrname, old, new)
toggle.properties()
toggle.on_change(‘active’, default_handler)
toggle.on_change(‘clicks’, default_handler)
toggle.on_change(‘value’, default_handler)
toggle.on_change(‘disabled’, default_handler)
confirmResponse.properties()
confirmResponse.on_change(‘data’, default_handler)
confirmResponse.on_change(‘name’, default_handler)
confirmResponse.on_change(‘selected’, default_handler)
confirmResponse.on_change(‘tags’, default_handler)
confirmResponse.on_change(‘change’, default_handler)
box = widgetbox(children=[toggle])
document = Document()
document.add_root(box)
session = push_session(document)
session.show()
if name == “main”:
session.loop_until_closed()
(3) I tried to create my own dialog by just adding a new button from within an “on_click” event. While this works, I can no longer remove that button from the WidgetBox. If I try, I
get the error “UnboundLocalError: local variable ‘toggle1’ referenced before assignment”.
#!/usr/bin/env python
from future import print_function
from bokeh.util.browser import view
from bokeh.document import Document
from bokeh.plotting import curdoc
from bokeh.models.widgets import Toggle
from bokeh.models.widgets import RadioButtonGroup
from bokeh.models.widgets.dialogs import Dialog
from bokeh.models.widgets.buttons import Button
from bokeh.models import CustomJS, ColumnDataSource
from bokeh.layouts import widgetbox
from bokeh.client import push_session
toggle1=None
def toggle_handler(active):
print("toggle_handler: %s" % (active))
if active:
toggle.label = 'start'
toggle.disabled=True
toggle1 = Toggle(label="Another Toggle button", button_type="success")
box.children.append(toggle1)
else:
toggle.label = 'stop'
toggle.disabled=False
if (toggle1 is not None):
box.children.remove(toggle1)
toggle1=None
toggle = Toggle(label=“Toggle button”, button_type=“success”)
toggle.on_click(toggle_handler)
def default_handler(attrname, old, new):
print ("default handler with attribute %s" %attrname, old, new)
toggle.properties()
toggle.on_change(‘active’, default_handler)
toggle.on_change(‘clicks’, default_handler)
toggle.on_change(‘value’, default_handler)
toggle.on_change(‘disabled’, default_handler)
box = widgetbox(children=[toggle])
document = Document()
document.add_root(box)
session = push_session(document)
session.show()
if name == “main”:
session.loop_until_closed()
``
So, after all of these approaches have failed, is there any way to create something like a confirm dialog with Bokeh at all ?
Thanks,
Stefan
[1] http://bokeh.pydata.org/en/latest/docs/reference/models/widgets.dialogs.html