i’d like to have a bokeh button play a wav file that the user selects with a file dialog. i can get this to work statically, but not dynamically.
in “templates/index.html” i put <audio id="audio_context" controls></audio>.
then i create a button and add a javascript callback to it which sets the source from a python variable:
play = Button(label='play')
play.js_on_event(ButtonClick, \
CustomJS(args=dict(context_audio=context_audio), code="""
var aud = document.getElementById("audio_context");
aud.src="data:audio/wav;base64,"+context_audio;
"""))
this works when play.js_on_event is called beforedoc.add_root(), but not afterwords. so if i have a way for context_audio to change, say via a file dialog, and then call play.js_on_event again, the new callback seems to not be registered.
i searched for a solution and found a merged PR which nominally claims to have made it possible to change CustomJS callbacks dynamically. but this does not work for me with bokeh 1.4.
@bjarthur70 Sometimes it’s just really difficult to say much without seeing/running real code. If you can provide a minimal, complete, reproducer, I would be happy to take a closer look.
i then open chrome, navigate to “localhost:5006”, open the javascript console, click on Elements, and examine the DOM of the audio HTML tag. initially it has no src field. when i click on the bokeh “play audio” button, the audio src field appears and is set to data:audio/wav;base64,hide-and-seek, as expected. i then click “change audio” and confirm that "entering change_audio_callback" is logged. then i click on “play audio” again, and though the audio tag line in the Elements window blinks to indicate is has been updated, the src field remains “hide-and-seek” instead of changing to “peek-a-boo”.
There’s at least one definite issue, and a suggestion for a different approach that I would take.
The first issue is that the args dict cannot pass arbitrary values [1]. The only thing that it can synchronize is other Bokeh models. So you can’t pass the encoded audio data that way (but also don’t need to, see next point).
The other comment I would make is that it is always the best practice with Bokeh to make the smallest change possible. In concrete terms that usually mean: change a property on an existing model, don’t replace models wholesale. And very specifically here, that means updating the code property of the existing callback, not making a new callback. (I’m not actually sure if swapping out the CustomJS callback works, I changed to how I would actually write things in practice before even trying). Below is a complete updated example:
Though note there is an open issue, hopefully implemented soon, to add a “Namespace” model that would make it simple to send fairly arbitrary data via args. Once that is available, the code can remain fixed, more like your original code, and you could just update a property you define on the namespace model for the audio data. ↩︎