Bokeh app with multiple modules

Hi,

I’d like to split my bokeh app across modules. I found a discussion on github, but I’m not sure how to do it.

If you want to split up the app code into multiple modules, that’s fine, but you will need to encapsulate all Bokeh object creation inside functions that always return new instance, rather than creating any as module globals.

Are there any references that might help? Do I need to handle this in each module or in main.py?

Below is my main.py if that helps. Basically I create a call for each component of the application, then reference the layout with <class_object>.layout, and I pass the ColumnDataSources and DataTables into the class init. These objects often reference each other, so it is a little complicated.

Thanks!

Dan

#!/usr/bin/env python2
# -*- coding: utf-8 -*-
"""
main program for Bokeh server (re-write for new query UI)
Created on Sun Apr 21 2017
@author: Dan Cutright, PhD
"""

from __future__ import print_function
from tools.utilities import load_options
from bokeh.io import curdoc
from bokeh.models.widgets import Panel, Tabs
import options
from dvh_bokeh_models.main.sources import Sources
from dvh_bokeh_models.main.custom_titles import custom_title
from dvh_bokeh_models.main.mlc_analyzer import MLC_Analyzer
from dvh_bokeh_models.main.time_series import TimeSeries
from dvh_bokeh_models.main.correlation import Correlation
from dvh_bokeh_models.main.roi_viewer import ROI_Viewer
from dvh_bokeh_models.main.rad_bio import RadBio
from dvh_bokeh_models.main.regression import Regression
from dvh_bokeh_models.main.query import Query
from dvh_bokeh_models.main.dvhs import DVHs
from dvh_bokeh_models.main.data_tables import DataTables
from dvh_bokeh_models.main.categories import Categories
from dvh_bokeh_models.main.planning_data import PlanningData
from tools.source_listener import SourceListener

options = load_options(options)

# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
# Bokeh component classes
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

# ColumnDataSource objects
sources = Sources()

# Categories map of dropdown values, SQL column, and SQL table (and data source for range_categories)
categories = Categories(sources)

# Bokeh table objects
data_tables = DataTables(sources)

# Bokeh objects for each tab layout
planning_data = PlanningData(custom_title, data_tables)
roi_viewer = ROI_Viewer(sources, custom_title)
mlc_analyzer = MLC_Analyzer(sources, custom_title, data_tables)
time_series = TimeSeries(sources, categories.range, custom_title)
correlation = Correlation(sources, categories, custom_title)
regression = Regression(sources, time_series, correlation, categories.multi_var_reg_var_names, custom_title, data_tables)
correlation.add_regression_link(regression)
rad_bio = RadBio(sources, time_series, correlation, regression, custom_title, data_tables)
dvhs = DVHs(sources, time_series, correlation, regression, custom_title, data_tables)
query = Query(sources, categories, dvhs, rad_bio, roi_viewer, time_series, correlation, regression, mlc_analyzer,
              custom_title, data_tables)
dvhs.add_query_link(query)

# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
# Listen for changes to sources
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
source_listener = SourceListener(sources, query, dvhs, rad_bio, regression)

# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
# Layout
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

query_tab = Panel(child=query.layout, title='Query')
dvh_tab = Panel(child=dvhs.layout, title='DVHs')
rad_bio_tab = Panel(child=rad_bio.layout, title='Rad Bio')
optional_tabs = [('ROI Viewer', Panel(child=roi_viewer.layout, title='ROI Viewer')),
                 ('Planning Data', Panel(child=planning_data.layout, title='Planning Data')),
                 ('Time-Series', Panel(child=time_series.layout, title='Time-Series')),
                 ('Correlation', Panel(child=correlation.layout, title='Correlation')),
                 ('Regression', Panel(child=regression.layout, title='Regression')),
                 ('MLC Analyzer', Panel(child=mlc_analyzer.layout, title='MLC Analyzer'))]

rendered_tabs = [query_tab, dvh_tab, rad_bio_tab] + \
                [tab for (title, tab) in optional_tabs if options.OPTIONAL_TABS[title]]

tabs = Tabs(tabs=rendered_tabs)

# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
# Create the document Bokeh server will use to generate the web page
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
curdoc().add_root(tabs)
curdoc().title = "DVH Analytics"

I think I’ve figured out the issue, one of my modules depended on top-level bokeh objects. Will follow-up soon.

···

On Wednesday, December 26, 2018 at 2:48:05 PM UTC-5, [email protected] wrote:

Hi,

I’d like to split my bokeh app across modules. I found a discussion on github, but I’m not sure how to do it.

https://github.com/bokeh/bokeh/issues/8265

If you want to split up the app code into multiple modules, that’s fine, but you will need to encapsulate all Bokeh object creation inside functions that always return new instance, rather than creating any as module globals.

Are there any references that might help? Do I need to handle this in each module or in main.py?

Below is my main.py if that helps. Basically I create a call for each component of the application, then reference the layout with <class_object>.layout, and I pass the ColumnDataSources and DataTables into the class init. These objects often reference each other, so it is a little complicated.

Thanks!

Dan

https://github.com/cutright/DVH-Analytics

#!/usr/bin/env python2
# -*- coding: utf-8 -*-
"""
main program for Bokeh server (re-write for new query UI)
Created on Sun Apr 21 2017
@author: Dan Cutright, PhD
"""

from __future__ import print_function
from tools.utilities import load_options
from [bokeh.io](http://bokeh.io) import curdoc
from bokeh.models.widgets import Panel, Tabs
import options
from dvh_bokeh_models.main.sources import Sources
from dvh_bokeh_models.main.custom_titles import custom_title
from dvh_bokeh_models.main.mlc_analyzer import MLC_Analyzer
from dvh_bokeh_models.main.time_series import TimeSeries
from dvh_bokeh_models.main.correlation import Correlation
from dvh_bokeh_models.main.roi_viewer import ROI_Viewer
from dvh_bokeh_models.main.rad_bio import RadBio
from dvh_bokeh_models.main.regression import Regression
from dvh_bokeh_models.main.query import Query
from dvh_bokeh_models.main.dvhs import DVHs
from dvh_bokeh_models.main.data_tables import DataTables
from dvh_bokeh_models.main.categories import Categories
from dvh_bokeh_models.main.planning_data import PlanningData
from tools.source_listener import SourceListener


options = load_options(options)


# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
# Bokeh component classes
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

# ColumnDataSource objects
sources = Sources()

# Categories map of dropdown values, SQL column, and SQL table (and data source for range_categories)
categories = Categories(sources)

# Bokeh table objects
data_tables = DataTables(sources)

# Bokeh objects for each tab layout
planning_data = PlanningData(custom_title, data_tables)
roi_viewer = ROI_Viewer(sources, custom_title)
mlc_analyzer = MLC_Analyzer(sources, custom_title, data_tables)
time_series = TimeSeries(sources, categories.range, custom_title)
correlation = Correlation(sources, categories, custom_title)
regression = Regression(sources, time_series, correlation, categories.multi_var_reg_var_names, custom_title, data_tables)
correlation.add_regression_link(regression)
rad_bio = RadBio(sources, time_series, correlation, regression, custom_title, data_tables)
dvhs = DVHs(sources, time_series, correlation, regression, custom_title, data_tables)
query = Query(sources, categories, dvhs, rad_bio, roi_viewer, time_series, correlation, regression, mlc_analyzer,
              custom_title, data_tables)
dvhs.add_query_link(query)


# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
# Listen for changes to sources
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
source_listener = SourceListener(sources, query, dvhs, rad_bio, regression)


# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
# Layout
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

query_tab = Panel(child=query.layout, title='Query')
dvh_tab = Panel(child=dvhs.layout, title='DVHs')
rad_bio_tab = Panel(child=rad_bio.layout, title='Rad Bio')
optional_tabs = [('ROI Viewer', Panel(child=roi_viewer.layout, title='ROI Viewer')),
                 ('Planning Data', Panel(child=planning_data.layout, title='Planning Data')),
                 ('Time-Series', Panel(child=time_series.layout, title='Time-Series')),
                 ('Correlation', Panel(child=correlation.layout, title='Correlation')),
                 ('Regression', Panel(child=regression.layout, title='Regression')),
                 ('MLC Analyzer', Panel(child=mlc_analyzer.layout, title='MLC Analyzer'))]

rendered_tabs = [query_tab, dvh_tab, rad_bio_tab] + \
                [tab for (title, tab) in optional_tabs if options.OPTIONAL_TABS[title]]

tabs = Tabs(tabs=rendered_tabs)


# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
# Create the document Bokeh server will use to generate the web page
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
curdoc().add_root(tabs)
curdoc().title = "DVH Analytics"

Hi Dan,

Yes, this is exactly a situation that is problematic, due to the way that Python itself caches module imports. I suppose we could consider automatically cloning objects that would be duplicated. But sometimes "automagic" features help to write good code more quickly, and sometimes they help to make bugs more quickly. I suspect that this idea would fall into the latter, offhand.

Thanks,

Bryan

···

On Dec 27, 2018, at 09:56, [email protected] wrote:

I think I've figured out the issue, one of my modules depended on top-level bokeh objects. Will follow-up soon.

On Wednesday, December 26, 2018 at 2:48:05 PM UTC-5, dan.cu...@gmail.com wrote:
Hi,

I'd like to split my bokeh app across modules. I found a discussion on github, but I'm not sure how to do it.

https://github.com/bokeh/bokeh/issues/8265

If you want to split up the app code into multiple modules, that's fine, but you will need to encapsulate all Bokeh object creation inside functions that always return new instance, rather than creating any as module globals.

Are there any references that might help? Do I need to handle this in each module or in main.py?

Below is my main.py if that helps. Basically I create a call for each component of the application, then reference the layout with <class_object>.layout, and I pass the ColumnDataSources and DataTables into the class init. These objects often reference each other, so it is a little complicated.

Thanks!

Dan
GitHub - cutright/DVH-Analytics: A DICOM Database Application for Radiation Oncology

#!/usr/bin/env python2
# -*- coding: utf-8 -*-
"""
main program for Bokeh server (re-write for new query UI)
Created on Sun Apr 21 2017
@author: Dan Cutright, PhD
"""

from __future__ import print_function
from tools.utilities import load_options
from bokeh.io import curdoc
from bokeh.models.widgets import Panel, Tabs
import options
from dvh_bokeh_models.main.sources import Sources
from dvh_bokeh_models.main.custom_titles import custom_title
from dvh_bokeh_models.main.mlc_analyzer import MLC_Analyzer
from dvh_bokeh_models.main.time_series import TimeSeries
from dvh_bokeh_models.main.correlation import Correlation
from dvh_bokeh_models.main.roi_viewer import ROI_Viewer
from dvh_bokeh_models.main.rad_bio import RadBio
from dvh_bokeh_models.main.regression import Regression
from dvh_bokeh_models.main.query import Query
from dvh_bokeh_models.main.dvhs import DVHs
from dvh_bokeh_models.main.data_tables import DataTables
from dvh_bokeh_models.main.categories import Categories
from dvh_bokeh_models.main.planning_data import PlanningData
from tools.source_listener import SourceListener

options = load_options(options)

# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
# Bokeh component classes
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

# ColumnDataSource objects
sources = Sources()

# Categories map of dropdown values, SQL column, and SQL table (and data source for range_categories)
categories = Categories(sources)

# Bokeh table objects
data_tables = DataTables(sources)

# Bokeh objects for each tab layout
planning_data = PlanningData(custom_title, data_tables)
roi_viewer = ROI_Viewer(sources, custom_title)
mlc_analyzer = MLC_Analyzer(sources, custom_title, data_tables)
time_series = TimeSeries(sources, categories.range, custom_title)
correlation = Correlation(sources, categories, custom_title)
regression = Regression(sources, time_series, correlation, categories.multi_var_reg_var_names, custom_title, data_tables)
correlation.add_regression_link(regression)
rad_bio = RadBio(sources, time_series, correlation, regression, custom_title, data_tables)
dvhs = DVHs(sources, time_series, correlation, regression, custom_title, data_tables)
query = Query(sources, categories, dvhs, rad_bio, roi_viewer, time_series, correlation, regression, mlc_analyzer,
              custom_title, data_tables)
dvhs.add_query_link(query)

# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
# Listen for changes to sources
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
source_listener = SourceListener(sources, query, dvhs, rad_bio, regression)

# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
# Layout
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

query_tab = Panel(child=query.layout, title='Query')
dvh_tab = Panel(child=dvhs.layout, title='DVHs')
rad_bio_tab = Panel(child=rad_bio.layout, title='Rad Bio')
optional_tabs = [('ROI Viewer', Panel(child=roi_viewer.layout, title='ROI Viewer')),
                 ('Planning Data', Panel(child=planning_data.layout, title='Planning Data')),
                 ('Time-Series', Panel(child=time_series.layout, title='Time-Series')),
                 ('Correlation', Panel(child=correlation.layout, title='Correlation')),
                 ('Regression', Panel(child=regression.layout, title='Regression')),
                 ('MLC Analyzer', Panel(child=mlc_analyzer.layout, title='MLC Analyzer'))]

rendered_tabs = [query_tab, dvh_tab, rad_bio_tab] + \
                [tab for (title, tab) in optional_tabs if options.OPTIONAL_TABS[title]]

tabs = Tabs(tabs=rendered_tabs)

# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
# Create the document Bokeh server will use to generate the web page
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
curdoc().add_root(tabs)
curdoc().title = "DVH Analytics"

--
You received this message because you are subscribed to the Google Groups "Bokeh Discussion - Public" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit https://groups.google.com/a/continuum.io/d/msgid/bokeh/0793b5df-96fd-4327-9381-3273f5314ee3%40continuum.io\.
For more options, visit https://groups.google.com/a/continuum.io/d/optout\.

Thanks for your time Bryan.

Yes, I had two modules containing bokeh objects that weren’t wrapped in classes. Everything works great now!

Dan

···

On Thursday, December 27, 2018 at 1:01:14 PM UTC-5, Bryan Van de ven wrote:

Hi Dan,

Yes, this is exactly a situation that is problematic, due to the way that Python itself caches module imports. I suppose we could consider automatically cloning objects that would be duplicated. But sometimes “automagic” features help to write good code more quickly, and sometimes they help to make bugs more quickly. I suspect that this idea would fall into the latter, offhand.

Thanks,

Bryan

On Dec 27, 2018, at 09:56, [email protected] wrote:

I think I’ve figured out the issue, one of my modules depended on top-level bokeh objects. Will follow-up soon.

On Wednesday, December 26, 2018 at 2:48:05 PM UTC-5, [email protected] wrote:

Hi,

I’d like to split my bokeh app across modules. I found a discussion on github, but I’m not sure how to do it.

https://github.com/bokeh/bokeh/issues/8265

If you want to split up the app code into multiple modules, that’s fine, but you will need to encapsulate all Bokeh object creation inside functions that always return new instance, rather than creating any as module globals.

Are there any references that might help? Do I need to handle this in each module or in main.py?

Below is my main.py if that helps. Basically I create a call for each component of the application, then reference the layout with <class_object>.layout, and I pass the ColumnDataSources and DataTables into the class init. These objects often reference each other, so it is a little complicated.

Thanks!

Dan

https://github.com/cutright/DVH-Analytics

#!/usr/bin/env python2

-- coding: utf-8 --

“”"

main program for Bokeh server (re-write for new query UI)

Created on Sun Apr 21 2017

@author: Dan Cutright, PhD

“”"

from future import print_function

from tools.utilities import load_options

from bokeh.io import curdoc

from bokeh.models.widgets import Panel, Tabs

import options

from dvh_bokeh_models.main.sources import Sources

from dvh_bokeh_models.main.custom_titles import custom_title

from dvh_bokeh_models.main.mlc_analyzer import MLC_Analyzer

from dvh_bokeh_models.main.time_series import TimeSeries

from dvh_bokeh_models.main.correlation import Correlation

from dvh_bokeh_models.main.roi_viewer import ROI_Viewer

from dvh_bokeh_models.main.rad_bio import RadBio

from dvh_bokeh_models.main.regression import Regression

from dvh_bokeh_models.main.query import Query

from dvh_bokeh_models.main.dvhs import DVHs

from dvh_bokeh_models.main.data_tables import DataTables

from dvh_bokeh_models.main.categories import Categories

from dvh_bokeh_models.main.planning_data import PlanningData

from tools.source_listener import SourceListener

options = load_options(options)

!!!

Bokeh component classes

!!!

ColumnDataSource objects

sources = Sources()

Categories map of dropdown values, SQL column, and SQL table (and data source for range_categories)

categories = Categories(sources)

Bokeh table objects

data_tables = DataTables(sources)

Bokeh objects for each tab layout

planning_data = PlanningData(custom_title, data_tables)

roi_viewer = ROI_Viewer(sources, custom_title)

mlc_analyzer = MLC_Analyzer(sources, custom_title, data_tables)

time_series = TimeSeries(sources, categories.range, custom_title)

correlation = Correlation(sources, categories, custom_title)

regression = Regression(sources, time_series, correlation, categories.multi_var_reg_var_names, custom_title, data_tables)

correlation.add_regression_link(regression)

rad_bio = RadBio(sources, time_series, correlation, regression, custom_title, data_tables)

dvhs = DVHs(sources, time_series, correlation, regression, custom_title, data_tables)

query = Query(sources, categories, dvhs, rad_bio, roi_viewer, time_series, correlation, regression, mlc_analyzer,

          custom_title, data_tables)

dvhs.add_query_link(query)

!!!

Listen for changes to sources

!!!

source_listener = SourceListener(sources, query, dvhs, rad_bio, regression)

!!!

Layout

!!!

query_tab = Panel(child=query.layout, title=‘Query’)

dvh_tab = Panel(child=dvhs.layout, title=‘DVHs’)

rad_bio_tab = Panel(child=rad_bio.layout, title=‘Rad Bio’)

optional_tabs = [(‘ROI Viewer’, Panel(child=roi_viewer.layout, title=‘ROI Viewer’)),

             ('Planning Data', Panel(child=planning_data.layout, title='Planning Data')),
             ('Time-Series', Panel(child=time_series.layout, title='Time-Series')),
             ('Correlation', Panel(child=correlation.layout, title='Correlation')),
             ('Regression', Panel(child=regression.layout, title='Regression')),
             ('MLC Analyzer', Panel(child=mlc_analyzer.layout, title='MLC Analyzer'))]

rendered_tabs = [query_tab, dvh_tab, rad_bio_tab] + \

            [tab for (title, tab) in optional_tabs if options.OPTIONAL_TABS[title]]

tabs = Tabs(tabs=rendered_tabs)

!!!

Create the document Bokeh server will use to generate the web page

!!!

curdoc().add_root(tabs)

curdoc().title = “DVH Analytics”


You received this message because you are subscribed to the Google Groups “Bokeh Discussion - Public” group.

To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].

To post to this group, send email to [email protected].

To view this discussion on the web visit https://groups.google.com/a/continuum.io/d/msgid/bokeh/0793b5df-96fd-4327-9381-3273f5314ee3%40continuum.io.

For more options, visit https://groups.google.com/a/continuum.io/d/optout.