Raksha
Raksha

Reputation: 1699

How to toggle between figures using a dropdown menu in Bokeh?

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

Answers (1)

Tony
Tony

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:

enter image description here

Upvotes: 2

Related Questions