Bootstrap.js controls with Bokeh charts

I’m building a web app and I’m experimenting with boostrap.js controls. I want to use the bootstrap controls to update a Bokeh chart, specifically to update the data. I’m a bit stuck on how to do it. I’ve tried manually coding it, and I can’;t get it to work. I’ve tried code gen, and that doesn’t work either. Has anyone got bootstrap.js and Bokeh to play together nicely? Can anyone point to a project on GitHub where this has been done? Thanks!

@Mike_Woodward Not sure what you have tried, what is not working and if there is something special with bootstrap. The example below is for a static document, connecting a vanilla HTML button with Bokeh CDS. I use the template argument in save to setup the layout and define JS function callback for the button.

In order to have the CDS object available for the HTML button callback function I initialize a variable in the <script> section.

const data = {'src': null};

This variable is updated with the CDS object by using Bokeh’s document_ready event

cb_doc_ready = CustomJS(
	args = {'src': src},
	code = """
  	  data['src'] = src;
	"""
)
curdoc().js_on_event("document_ready", cb_doc_ready)

The CDS object is now available for the JS function and can be updated.

function btnClick() {
    data.src.data = {
      'x': [Math.random()*2, Math.random()*2, Math.random()*2],
      'y': [Math.random()*3, Math.random()*3, Math.random()*3],
    }

The code (Bokeh 3.8.0)

from bokeh.io import save, curdoc
from bokeh.plotting import figure
from bokeh.models import ColumnDataSource, CustomJS

template = """
{% block postamble %}
<script>
  const data = {'src': null};
  function btnClick() {
    console.log(data.src.data);
    data.src.data = {
      'x': [Math.random()*2, Math.random()*2, Math.random()*2],
      'y': [Math.random()*3, Math.random()*3, Math.random()*3],
    }
  }
</script>
<style>
  html {
    font-family: sans-serif;
    font-size: 62.5%; 
  }
  body {
    margin: 0;
    padding: 0; 
    font-size: 1.6em; 
    line-height: 1.5;
    font-weight: 300;
  }
  .container {
    max-width: 800px;
    margin: auto;
  }
  .button {
    background-color: #4CAF50; 
    color: white; 
    padding: 15px 32px; 
    text-align: center;
    text-decoration: none; 
    display: inline-block; 
    font-size: 16px; 
    margin: 4px 2px; 
    cursor: pointer;
</style>
{% endblock %}
{% block contents %}
<div class="container">
  <div>
  {{ embed(roots.plot) }}
  </div>
  <div>
  This button is a HTML element, not a Bokeh widget button.
  <button class="button" type="button" onclick="btnClick()">Click me</button>
  </div>
</div>
{% endblock %}
"""

src = ColumnDataSource(data = {'x': [1, 2, 3], 'y': [3, 1, 2]})
p = figure(name = 'plot')
r_line = p.line(x = 'x', y = 'y', line_width = 4, source = src)

cb_doc_ready = CustomJS(
	args = {'src': src},
	code = """
  	  data['src'] = src;
	"""
)
curdoc().js_on_event("document_ready", cb_doc_ready)
save(p, template = template)

I got it working after several hours of work. Here’s a bit more info: my application as a whole is a Django app and I had to fix some Django things, unrelated to this problem, before I could fix this issue. I have multiple Bokeh docs in my Django app, which complicates things.

The ‘secret’ is getting the correct Bokeh document and then getting the correct data source or plot. Here are the steps to the solution.

  1. Give the plot and the ColumnDataSource a name.
  2. Create the control with a JavaScript callback.
  3. In the JavaScript control, find the Bokeh document that contains the plot or data name.
  4. In the JavaScript control, update the plot title and plot data.

The key JavaScript code is:

model = Bokeh.documents[violinDataIndex].get_model_by_name(modelName)
model.data.attendance = tierData.attendance;
model.data.probability_density = tierData.probability_density;
// Trigger a change event to update the plot
model.change.emit();

where violinDataIndex is the document index that contains the data.

1 Like