I have a specific design in mind for my bokeh app. I am using bokeh 0.12.3
and a bokeh server to keep everything in sync. Please, have a look at my mockup:
On the left-hand side, there is a static navigation bar, the right part of the view will consist of plots, that are manually added. The amount of plot columns on the right-hand side shall change w.r.t. window size. I am well aware of the bokeh layout documentation laying out plots and widgets, but it's a bit more complicated. This is the layout I currently have:
doc_layout = layout(children=[[column(radio_buttons,
cbx_buttons,
div,
data_table,
plot,
button)]],
sizing_mode='scale_width')
curdoc().add_root(doc_layout)
In order to add new plots I use:
doc_layout.children[-1].children.append(plot)
# appends plot to layout children [[column(..), plot]]
But the behavior is very strange, and not at all what I actually want to achieve. New plots are added on top of the column (menu panel) instead.
Here, a short example where you can try out to see what I mean:
from bokeh.io import curdoc
from bokeh.plotting import figure
from bokeh.models.sources import ColumnDataSource
from bokeh.models.widgets import Button, DataTable, TableColumn
from bokeh.layouts import layout, widgetbox, column, row
WIDTH = 200
HEIGHT = 200
def add_plot():
p = figure(width=WIDTH, height=HEIGHT, tools=[], toolbar_location=None)
p.line([0, 1, 2, 3, 4, 5], [0, 1, 4, 9, 16, 25])
doc_layout.children[-1].children.append(p)
src = ColumnDataSource(dict(x=[0, 1, 2, 3, 4, 5], y=[0, 1, 4, 9, 16, 25]))
t1 = DataTable(source=src, width=WIDTH, height=HEIGHT,
columns=[TableColumn(field='x', title='x'),
TableColumn(field='y', title='y')])
b = Button(label='add plot')
b.on_click(add_plot)
doc_layout = layout([[widgetbox(b, t1)]], sizing_mode='scale_width')
curdoc().add_root(doc_layout)
I am not sure what is the best solution to overcome this problem. I've tried several things, from layout()
with different sizing modes, gridplot()
, column()
/row()
in different combinations. In my previous version, where the navigation menu was placed on the top of the page instead on the left-hand side, everything seemed to work:
layout(children=[[widgetbox(radio_button, cbx_button),
widgetbox(data_table),
widgetbox(div),
widgetbox(button)],
[Spacer()]],
sizing_mode='scale_width')
You can change the last line in your callback to:
doc_layout.children[0].children[-1].children.append(p)
And change your layout to:
doc_layout = layout(sizing_mode='scale_width')
doc_layout.children.append(row(column(widgetbox(b, t1)), column()))
But then then don't propagate exactly as you'd like. I think for that you would need to do some custom css styling.
Assuming yours is a directory format app, one option would be to make a template/index.html
file where you can add a style
block in the header where you could try to override css to make your plots inline-block
s or something.
<style>
.bk-whatever-class {
...
}
</style>
Use a developer tool on your browser to find the appropriate classes and toy with them. But perhaps not the best solution....
For widgets there is a css_classes
attribute, where you could specify a class for that widget to use, but that unfortunately doesn't help with the plot canvases.
mycol = column(css_classes=['myclass'])