When a callback is assigned to a FileInput widget on a change of the “value” parameter, the callback function runs before the FileInput widget’s “filename” parameter is set. This can cause some unexpected results.
For example, taking bokeh/file_input.py at ed54705d627ae500509e57fe071fc7a60fc8a951 · bokeh/bokeh · GitHub and making a slight change such that the callback gets attached to the ‘value’ parameter:
from bokeh.layouts import column
from bokeh.models import CustomJS, Div, FileInput
from bokeh.plotting import output_file, show
# Set up widgets
file_input = FileInput(accept=".csv,.json")
para = Div(text="<h1>FileInput Values:</h1><p>filename:<p>b64 value:")
# Create CustomJS callback to display file_input attributes on change
callback = CustomJS(args=dict(para=para, file_input=file_input), code="""
para.text = "<h1>FileInput Values:</h1><p>filename: " + file_input.filename + "<p>b64 value: " + file_input.value
""")
# Attach callback to FileInput widget
file_input.js_on_change('value', callback) # THIS IS THE CHANGE
output_file("file_input.html")
show(column(file_input, para))
Running this shows the filename not showing up.
This is not an issue for this particular case, since we can use the js_on_change
function with ‘change’ instead of ‘value’. However, if I want to attach a typical Python function as the callback, e.g., using file_input.change('value', callback)
, where ‘callback’ is a Python function of the form def callback(attr, old, new)
, then using ‘change’ isn’t an option.
I was using ‘value’ for a while (since this is what’s shown in the documentation) and trying to figure out why my filename wasn’t showing up in the callback. It took me a while to figure out that I should be using ‘filename’ instead (since ‘filename’ gets changed later on in bokeh.models.widgets.inputs — Bokeh 2.4.2 Documentation than ‘value’).
Is there some way to attach a callback to a widget that waits for all properties of the widget to be updated prior to running the callback function? Similar to what’s done with file_input.js_on_change('change', callback)
, but when using a typical Python function instead of a CustomJS.