Reputation: 1699
I have a figure where I already added buttons to display different plots. How can I add another couple figures, each having different sets of plots and switch between the figures using a dropdown menu? I'm trying to condense the code as much as possible, as to not be rewriting the same functions for each figure. What is the best way to go about that? In the sample code below I didn't include a slider and three buttons for the second figure, but I would like all figures to have them
import numpy as np
import pandas as pd
import warnings
from bokeh.layouts import widgetbox
from bokeh.plotting import figure, show, output_file, output_notebook
from bokeh.palettes import Spectral11, colorblind, Inferno, BuGn, brewer
from bokeh.models import HoverTool, value, LabelSet, Legend, ColumnDataSource, LinearColorMapper, BasicTicker, PrintfTickFormatter, ColorBar
from bokeh.models.widgets import DateRangeSlider, CheckboxButtonGroup
from bokeh.models import CustomJS, ColumnDataSource
from bokeh.layouts import column, row
from json import loads
import ast
import datetime as dt
warnings.filterwarnings('ignore')
TOOLS = 'save,pan,box_zoom,reset,wheel_zoom'
p = figure(title="data", plot_height=400, tools=TOOLS, plot_width=1300)
start_date = dt.datetime.strptime('2019 04 15', '%Y %m %d')
end_date = dt.datetime.strptime('2019 04 18', '%Y %m %d')
t = np.arange(0.0, 2.0, 0.01)
dates = np.arange(start_date, end_date, np.timedelta64(1, 'h'),
dtype='datetime64')
x = np.sin(3*np.pi*t)[:72]
y = np.cos(3*np.pi*t)[:72]
z = np.cos(6*np.pi*t)[:72]
for c in [x, y, z]:
c[40:50] = np.nan
source = ColumnDataSource(data={'Date': dates, 'x': x, 'y': y, 'z': z})
p.xaxis.axis_label = 'Date'
p.yaxis.axis_label = 'Position (m)'
def add_plot(y, color):
new_plot = p.line(x='Date', y=y, line_width=1, color=color, source=source)
return new_plot
x = add_plot('x', 'red')
y = add_plot('y', 'green')
z = add_plot('z', 'blue')
checkbox = CheckboxButtonGroup(labels=['x', 'y', 'z'], active=[0, 1, 2])
checkbox.callback = CustomJS(args=dict(x=x, y=y, z=z), code="""
//console.log(cb_obj.active);
x.visible = false;
y.visible = false;
z.visible = false;
for (i in cb_obj.active) {
//console.log(cb_obj.active[i]);
if (cb_obj.active[i] == 0) {
x.visible = true;
} else if (cb_obj.active[i] == 1) {
y.visible = true;
} else if (cb_obj.active[i] == 2) {
z.visible = true;
}
}
""")
callback = CustomJS(args=dict(p=p), code="""
var a = cb_obj.value;
p.x_range.start = a[0];
p.x_range.end = a[1];
""")
range_slider = DateRangeSlider(start=start_date, end=end_date,
value=(start_date, end_date), step=1)
range_slider.js_on_change('value', callback)
def get_hovertools():
hovers = {'x': x, 'y': y, 'z': z}
for k, v in hovers.items():
hovers[k] = HoverTool(mode='vline', renderers=[v])
hovers[k].tooltips = [('Date', '@Date{%F %H:%M:%S.%u}'),
(k, '@{'+k+'}{%0.2f}m')]
hovers[k].formatters = {'Date': 'datetime', k: 'printf'}
p.add_tools(hovers[k])
get_hovertools()
# --------------------- second figure here --------------------------
p2 = figure(title="data", plot_height=400, tools=TOOLS, plot_width=1300)
start_date = dt.datetime.strptime('2019 04 15', '%Y %m %d')
end_date = dt.datetime.strptime('2019 04 18', '%Y %m %d')
t = np.arange(0.0, 2.0, 0.01)
dates = np.arange(start_date, end_date, np.timedelta64(1, 'h'),
dtype='datetime64')
x2 = [1]*72
y2 = [2]*72
z2 = [3]*72
source = ColumnDataSource(data={'Date': dates, 'x': x2, 'y': y2, 'z': z2})
def add_plot(y, color):
new_plot = p2.line(x='Date', y=y, line_width=1, color=color, source=source)
return new_plot
x2 = add_plot('x', 'red')
y2 = add_plot('y', 'green')
z2 = add_plot('z', 'blue')
layout = column(p, widgetbox(checkbox), widgetbox(range_slider),
p2)
show(layout)
Upvotes: 1
Views: 2361
Reputation: 8297
This example shows how to add AND remove figure from a Bokeh document (Bokeh v1.1.0). It doesn't include widgets for clarity reason but you could add your widgets there as well using the same approach.
However, maybe you could consider tabs as an option. When using tabs you don't need to remove/add the root elements, those are continuously present on the separate tabs which user can switch. You can find tabs examples here
from bokeh.models import Select, Row, ColumnDataSource
from bokeh.plotting import figure, curdoc
import numpy as np
x = np.linspace(0, 4 * np.pi, 100)
source = ColumnDataSource(dict(x = x, y = np.cos(x)))
glyphs = ["line", "scatter"]
select = Select(title = "Select plot:", value = "", options = [""] + glyphs)
curdoc().add_root(Row(select, name = 'root'))
def callback(attr, old, new):
layouts = curdoc().get_model_by_name('root').children
for glyph in glyphs:
plot = curdoc().get_model_by_name(glyph)
if plot:
layouts.remove(plot)
if new:
p = figure(name = new)
exec(new + ' = p.' + new + '("x", "y", source = source)')
layouts.append(p)
select.on_change('value', callback)
Result:
Upvotes: 2