I’m currently using bokeh serve and wanted to have a confirmation dialog when a button is pressed before executing some python code. There are some old posts from 2016 about dialogs, but that seems to be deprecated. I’ve come up with the code below. Basically I use two hidden PreText widgets to trigger javascript and python calls when their text is changed. It seems to work, but I feel like there is a better way. Thoughts?
import uuid
from bokeh.io import curdoc
from bokeh.layouts import column
from bokeh.models import Button, CustomJS, PreText
# Global to store everything
STATE = {}
# What function to call if confirmation is yes
STATE['confirm_func'] = None
# triggers confirmation by changing the value in confirm_text
def trigger_confirm(text):
# add in uuid so it's allways different
STATE['confirm_text'].text = str(uuid.uuid4()) + text
# handles confirmation when value in confirm_res is changed
def handle_confirm(attr, old, new):
if STATE['confirm_func'] is None:
return
if STATE['confirm_res'].text.strip() == 'yes':
STATE['confirm_func']()
STATE['confirm_func'] = None
# Call right after button is pressed
def button_callback(event):
# Set function to call on yes result
STATE['confirm_func'] = button_action
trigger_confirm("Are you sure you want to push the button?")
# This occurs right away, doesn't wait for dialog to be done
# Actual action to perform after confirmation dialog
def button_action():
print("=====Button action done=====")
STATE['confirm_text'] = PreText(text="")
STATE['confirm_res'] = PreText(text="")
confirm_js = CustomJS(
args=dict(confirm_text=STATE['confirm_text'], confirm_res=STATE['confirm_res']),
code=
'''
// Remove uuid from text
let confirm_str = confirm_text.text.substring(36);
// Create dialog
let dialog_ans = confirm(confirm_str);
let res = '';
if (dialog_ans) {
console.log("yes");
// handle where no change in result still triggers callback
if (confirm_res.text == 'yes') {
res = 'yes ';
} else {
res = 'yes';
}
} else {
console.log("no");
// handle where no change in result still triggers callback
if (confirm_res.text == 'yes') {
res = 'no ';
} else {
res = 'no';
}
}
console.log(res);
confirm_res.text = res;
'''
)
# Set up callbacks
STATE['confirm_text'].js_on_change('text', confirm_js)
STATE['confirm_res'].on_change('text', handle_confirm)
# Generic button
STATE['button'] = Button(label='Do something')
STATE['button'].on_click(button_callback)
# Hide the dummy text widgets
STATE['confirm_res'].visible = False
STATE['confirm_text'].visible = False
# Add everything to doc so it syncs
curdoc().add_root(column(STATE['confirm_text'], STATE['confirm_res'], STATE['button']))