Bokeh View

Last Updated: August 2023


This gizmo requires the bokeh library to be installed. Starting with Tethys 5.0 or if you are using micro-tethys-platform, you will need to install bokeh using conda or pip as follows:

# conda: conda-forge channel strongly recommended
conda install -c conda-forge "bokeh<3"

# pip
pip install "bokeh<3"

Don't Forget: If you end up using this gizmo in your app, add bokeh as a requirement to your install.yml.


class tethys_sdk.gizmos.BokehView(plot_input, height=None, width='100%', attributes='', classes='', divid='', hidden=False)

Simple options object for Bokeh plotting.


For more information about Bokeh and for Python examples, see


A bokeh figure to be plotted.


bokeh figure


Height of the plot element. Any valid css unit of length.




Width of the plot element. Any valid css unit of length.




Dictionary of attributed to add to the outer div.




Space separated string of classes to add to the outer div.




If True, the plot will be hidden. Default is False.



Controller Code Example:

from tethys_sdk.gizmos import BokehView
from bokeh.plotting import figure

plot = figure(plot_height=300)[1,2], [3,4])
my_bokeh_view = BokehView(plot, height="300px")

context = {'bokeh_view_input': my_bokeh_view}

Template Code Example:

{% load tethys %}

{% gizmo bokeh_view_input %}

Additional Examples:

from math import pi
import numpy as np
import pandas as pd
from bokeh.layouts import column
from bokeh.models import ColumnDataSource, RangeTool, HoverTool
from bokeh.palettes import Bokeh, Category20c
from bokeh.plotting import figure
from bokeh.transform import cumsum, dodge
from bokeh.sampledata.penguins import data as penguin_data
from bokeh.sampledata.stocks import AAPL
from bokeh.transform import factor_cmap, factor_mark
from tethys_sdk.gizmos import BokehView

# Scatter plot
SPECIES = sorted(penguin_data.species.unique())
MARKERS = ['hex', 'circle_x', 'triangle']

scatter_fig = figure(title = "Penguin size", background_fill_color="#fafafa")
scatter_fig.xaxis.axis_label = 'Flipper Length (mm)'
scatter_fig.yaxis.axis_label = 'Body Mass (g)'

scatter_fig.scatter("flipper_length_mm", "body_mass_g", source=penguin_data,
        legend_group="species", fill_alpha=0.4, size=12,
        marker=factor_mark('species', MARKERS, SPECIES),
        color=factor_cmap('species', 'Category10_3', SPECIES))

scatter_fig.legend.location = "top_left"
scatter_fig.legend.title = "Species"
scatter_plot = BokehView(scatter_fig)

# Line plot
line_x = np.linspace(0, 4*np.pi, 100)
line_y = np.sin(line_x)

TOOLS = "pan,wheel_zoom,box_zoom,reset,save,box_select"

line_fig = figure(title="Legend Example", tools=TOOLS)

line_fig.line(line_x,   line_y, legend_label="sin(x)")
line_fig.line(line_x, 2*line_y, legend_label="2*sin(x)", color="orange")
line_fig.line(line_x, 3*line_y, legend_label="3*sin(x)", color="green")

line_fig.legend.title = 'Example Title'
line_plot = BokehView(line_fig)

# Pie plot
pie_x = {
    'United States': 157,
    'United Kingdom': 93,
    'Japan': 89,
    'China': 63,
    'Germany': 44,
    'India': 42,
    'Italy': 40,
    'Australia': 35,
    'Brazil': 32,
    'France': 31,
    'Taiwan': 31,
    'Spain': 29

pie_data = pd.Series(pie_x).reset_index(name='value').rename(columns={'index': 'country'})
pie_data['angle'] = pie_data['value']/pie_data['value'].sum() * 2*pi
pie_data['color'] = Category20c[len(pie_x)]

pie_fig = figure(height=350, title="Pie Chart", toolbar_location=None,
        tools="hover", tooltips="@country: @value", x_range=(-0.5, 1.0))

pie_fig.wedge(x=0, y=1, radius=0.4,
        start_angle=cumsum('angle', include_zero=True), end_angle=cumsum('angle'),
        line_color="white", fill_color='color', legend_field='country', source=pie_data)

pie_fig.axis.axis_label = None
pie_fig.axis.visible = False
pie_fig.grid.grid_line_color = None

pie_plot = BokehView(pie_fig)

# Bar chart
fruits = ['Apples', 'Pears', 'Nectarines', 'Plums', 'Grapes', 'Strawberries']
years = ['2015', '2016', '2017']

bar_data = {'fruits' : fruits,
        '2015'   : [2, 1, 4, 3, 2, 4],
        '2016'   : [5, 3, 3, 2, 4, 6],
        '2017'   : [3, 2, 4, 4, 5, 3]}

source = ColumnDataSource(data=bar_data)

bar_fig = figure(x_range=fruits, y_range=(0, 10), title="Fruit Counts by Year",
        height=350, toolbar_location=None, tools="")

bar_fig.vbar(x=dodge('fruits', -0.25, range=bar_fig.x_range), top='2015', source=source,
    width=0.2, color="#c9d9d3", legend_label="2015")

bar_fig.vbar(x=dodge('fruits',  0.0,  range=bar_fig.x_range), top='2016', source=source,
    width=0.2, color="#718dbf", legend_label="2016")

bar_fig.vbar(x=dodge('fruits',  0.25, range=bar_fig.x_range), top='2017', source=source,
    width=0.2, color="#e84d60", legend_label="2017")

bar_fig.x_range.range_padding = 0.1
bar_fig.xgrid.grid_line_color = None
bar_fig.legend.location = "top_left"
bar_fig.legend.orientation = "horizontal"

bar_chart = BokehView(bar_fig)

# Time series
dates = np.array(AAPL['date'], dtype=np.datetime64)
source = ColumnDataSource(data=dict(date=dates, close=AAPL['adj_close']))

time_series_fig = figure(height=300, width=800, tools="xpan", toolbar_location=None,
                        x_axis_type="datetime", x_axis_location="above",
                        background_fill_color="#efefef", x_range=(dates[1500], dates[2500]))

time_series_fig.line('date', 'close', source=source)
time_series_fig.yaxis.axis_label = 'Price'

select = figure(title="Drag the middle and edges of the selection box to change the range above",
                height=130, width=800, y_range=time_series_fig.y_range,
                x_axis_type="datetime", y_axis_type=None,
                tools="", toolbar_location=None, background_fill_color="#efefef")

range_tool = RangeTool(x_range=time_series_fig.x_range)
range_tool.overlay.fill_color = "navy"
range_tool.overlay.fill_alpha = 0.2

select.line('date', 'close', source=source)
select.ygrid.grid_line_color = None

time_series_plot = BokehView(column(time_series_fig, select))

# Hexbin plot
n = 500
hex_x = 2 + 2*np.random.standard_normal(n)
hex_y = 2 + 2*np.random.standard_normal(n)

hex_fig = figure(title="Hexbin for 500 points", match_aspect=True,
        tools="wheel_zoom,reset", background_fill_color='#440154')
hex_fig.grid.visible = False

hex_r, _ = hex_fig.hexbin(hex_x, hex_y, size=0.5, hover_color="pink", hover_alpha=0.8), hex_y, color="white", size=1)

    tooltips=[("count", "@c"), ("(q,r)", "(@q, @r)")],
    mode="mouse", point_policy="follow_mouse", renderers=[hex_r]

hexbin_plot = BokehView(hex_fig)

context = {
    'docs_endpoint': docs_endpoint,
    'scatter_plot': scatter_plot,
    'line_plot': line_plot,
    'pie_plot': pie_plot,
    'bar_chart': bar_chart,
    'time_series_plot': time_series_plot,
    'hexbin_plot': hexbin_plot,

Template Code:

{% load tethys %}

{% gizmo scatter_plot %}
{% gizmo line_plot %}
{% gizmo pie_plot %}
{% gizmo bar_chart %}
{% gizmo time_series_plot %}
{% gizmo hexbin_plot %}


Often dynamically loading in plots can be useful. Here is a description of how to do so with Bokeh.


In order to use this, you will either need to use a BokehView gizmo on the main page or register the dependencies in the main html template page using the import_gizmo_dependency tag with the bokeh_view name in the import_gizmos block.

For example:

{% block import_gizmos %}
    {% import_gizmo_dependency bokeh_view %}
{% endblock %}

Three elements are required:

  1. A controller for the AJAX call with a BokehView gizmo.

from tethys_sdk.gizmos import BokehView
from tethys_sdk.routing import controller
from bokeh.plotting import figure
from .app import App

@controller(name="bokeh_ajax", url="app-name/bokeh")
def bokeh_ajax(request):
    Controller for the bokeh ajax request.
    plot = figure(plot_height=300)[1,2], [3,4])
    my_bokeh_view = BokehView(plot, height="300px")

    context = {'bokeh_view_input': my_bokeh_view}

    return App.render(request, 'bokeh_ajax.html', context)
  1. A template for with the tethys gizmo (e.g. bokeh_ajax.html)

{% load tethys %}

{% gizmo bokeh_view_input %}
  1. The AJAX call in the javascript

$(function() { //wait for page to load

        url: 'bokeh',
        method: 'GET',
        data: {
            'plot_height': 500, //example data to pass to the controller
        success: function(data) {
            // add plot to page