I am trying to make a somewhat complex dashboard in bokeh with several different figure objects and widgets arranged in rows and columns.
When a user interacts with the widgets, some of the plots are updated, and some remain the same. I am creating new plot objects and replacing them with layout.children[i] = x and then sending a push_notebook to update the dashboard, but this causes some of the plots to resize to the minimum canvas size.
I have created a simple example to show what I mean:
from numpy import random
from bokeh.plotting import figure, curdoc
from bokeh.layouts import row, widgetbox, column
from bokeh.models.widgets import Button, Slider
from bokeh.io import push_notebook, show, output_notebook
from bokeh.models.callbacks import CustomJS
output_notebook()
num_samples = 500
def bUpdate(var, val):
global num_samples
if var == “Number of Samples”:
num_samples = int(val)
replotBottom()
def bReCalc():
x = [random.random() for i in range(num_samples)]
y = [random.random() for i in range(num_samples)]
p = figure(plot_width=COL1_W, plot_height=250)
p.circle(x, y, color=‘red’)
p.toolbar.logo = None
layout.children[0].children[0].children[0].children[1] = p
x = [random.random() for i in range(num_samples)]
y = [random.random() for i in range(num_samples)]
p2 = figure(plot_width=COL2_W, plot_height=int(COL1_W*1.8))
p2.circle(x,y)
layout.children[0].children[0].children[1].children[0] = p2
push_notebook(handle=bokeh_handle)
def replotBottom():
x = [random.random() for i in range(num_samples)]
y = [random.random() for i in range(num_samples)]
p2 = figure()
p2.circle(x,y)
p2.plot_width=COL1_W
p2.plot_height=COL1_W
x = [random.random() for i in range(num_samples)]
y = [random.random() for i in range(num_samples)]
p3 = figure(plot_width = COL2_W, plot_height = int(COL1_W*1.5))
p3.circle(x,y,color=‘green’)
x = [random.random() for i in range(num_samples)]
y = [random.random() for i in range(num_samples)]
z = [random.random() for i in range(num_samples)]
p4 = figure(plot_width = int(TOTAL_WIDTH/2-5), plot_height=COL1_W)
p5 = figure(plot_width = int(TOTAL_WIDTH/2-5), plot_height=COL1_W)
p4.circle(x,y)
p5.circle(x,z)
layout.children[0].children[0].children[0].children[3]=p2
layout.children[0].children[0].children[1].children[1]=p3
layout.children[0].children[1].children[0] = p4
layout.children[0].children[1].children[1] = p5
push_notebook(handle=bokeh_handle)
cb = CustomJS(code="""
if (IPython.notebook.kernel !== undefined) {
var kernel = IPython.notebook.kernel;
cmd = “bUpdate(’” + cb_obj.title + “’,’” + cb_obj.value + “’)”;
kernel.execute(cmd, {}, {});
}
“”")
cbRC = CustomJS(code="""
if (IPython.notebook.kernel !== undefined) {
var kernel = IPython.notebook.kernel;
cmd = “bReCalc()”;
kernel.execute(cmd, {}, {});
}
“”")
TOTAL_WIDTH = 800
COL1_W = int(0.25*TOTAL_WIDTH)-5
COL2_W = int(0.75*TOTAL_WIDTH)-5
WID_W = int(0.5*COL1_W) - 15
btnCalc = Button(label=“Recalculate”, callback=cbRC, width=WID_W)
sldSamples = Slider(start=20, end=1000, step=10, title=“Number of Samples”, callback_policy=“mouseup”, callback=cb, width=WID_W)
layout = row(column( row( column( btnCalc, figure(plot_width=COL1_W, plot_height=250), \
sldSamples, figure(plot_width=COL1_W, plot_height=COL1_W)), \
column( figure(plot_width=COL2_W, plot_height=int(COL1_W*1.8)), \
figure(plot_width=COL2_W, plot_height = int(COL1_W*1.5), sizing_mode=“scale_height”)) \
), \
row(figure(plot_width = int(TOTAL_WIDTH/2-5), plot_height=COL1_W), figure(plot_width = int(TOTAL_WIDTH/2-5), plot_height=COL1_W))))
bokeh_handle = show(layout, notebook_handle=True)
When a user clicks the button or changes the value of the slider, some of the plots get updated and redrawn, while others get randomly resized. But, if you just call “show(layout)” in another cell, the layout displays correctly. Anyone have any ideas for best practices for this type of thing?
==EDIT==
The same type of error occurs, even when embedding a bokeh server similar to this example: bokeh/notebook_embed.ipynb at branch-3.0 · bokeh/bokeh · GitHub
output_notebook()
def modify_doc(doc):
num_samples = 500
def sampChange(attr, old, new):
global num_samples
num_samples = int(new)
x = [random.random() for i in range(num_samples)]
y = [random.random() for i in range(num_samples)]
p2 = figure()
p2.circle(x,y)
p2.plot_width=COL1_W
p2.plot_height=COL1_W
x = [random.random() for i in range(num_samples)]
y = [random.random() for i in range(num_samples)]
p3 = figure(plot_width = COL2_W, plot_height = int(COL1_W*1.5))
p3.circle(x,y,color=‘green’)
x = [random.random() for i in range(num_samples)]
y = [random.random() for i in range(num_samples)]
z = [random.random() for i in range(num_samples)]
p4 = figure(plot_width = int(TOTAL_WIDTH/2-5), plot_height=COL1_W)
p5 = figure(plot_width = int(TOTAL_WIDTH/2-5), plot_height=COL1_W)
p4.circle(x,y)
p5.circle(x,z)
layout.children[0].children[1] = row(p4,p5)
layout.children[0].children[0].children[0].children[3]=p2
layout.children[0].children[0].children[1].children[1]=p3
#layout.children[0].children[1].children[1] = p5
#doc.add_root(layout)
def bReCalc():
x = [random.random() for i in range(num_samples)]
y = [random.random() for i in range(num_samples)]
p = figure(plot_width=COL1_W, plot_height=250)
p.circle(x, y, color=‘red’)
p.toolbar.logo = None
layout.children[0].children[0].children[0].children[1] = p
x = [random.random() for i in range(num_samples)]
y = [random.random() for i in range(num_samples)]
p2 = figure(plot_width=COL2_W, plot_height=int(COL1_W*1.8))
p2.circle(x,y)
layout.children[0].children[0].children[1].children[0] = p2
#doc.add_root(layout)
#push_notebook(handle=bokeh_handle)
TOTAL_WIDTH = 800
COL1_W = int(0.25*TOTAL_WIDTH)-5
COL2_W = int(0.75*TOTAL_WIDTH)-5
WID_W = int(0.5*COL1_W) - 15
btnCalc = Button(label=“Recalculate”, width=WID_W)
sldSamples = Slider(start=20, end=500, step=10, title=“Number of Samples”, callback_policy=“mouseup”, width=WID_W)
btnCalc.on_click(bReCalc)
sldSamples.on_change(‘value’, sampChange)
layout = row(column( row( column( btnCalc, figure(plot_width=COL1_W, plot_height=250), \
sldSamples, figure(plot_width=COL1_W, plot_height=COL1_W)), \
column( figure(plot_width=COL2_W, plot_height=int(COL1_W*1.8)), \
figure(plot_width=COL2_W, plot_height = int(COL1_W*1.5))) \
), \
row(figure(plot_width = int(TOTAL_WIDTH/2-5), plot_height=COL1_W), figure(plot_width = int(TOTAL_WIDTH/2-5), plot_height=COL1_W))))
doc.add_root(layout)
bReCalc()
return doc
from bokeh.application.handlers import FunctionHandler
from bokeh.application import Application
handler = FunctionHandler(modify_doc)
app = Application(handler)
show(app)