Trying to build a dashboard with multiple pages using Flask/Bokeh combination.
The code that I use for main file (test.py
) collects(imports) all the files corresponding to pages of the dashboard.
# test.py
from page1 import Page1
app = Flask(__name__)
app.config.from_envvar('FLASKR_SETTINGS', silent=True)
class Dashboard(object):
def __init__(self, *pages):
self._pages = [page(self) for page in pages]
def __call__(self, doc):
tabs = []
for page in self._pages:
tabs.append(Panel(child=page.layout, title=page.title))
doc.add_root(Tabs(tabs=tabs))
bkapp = Application(FunctionHandler(Dashboard(Page1)))
The intent is to define each page of the dashboard in its own file which, eventually, is imported in the main file(test.py
) and served by an webserver (gunicorn, for example).
# page1.py from bokeh.models.widgets import Select
from bokeh.layouts import column, gridplot, widgetbox, layout, row
class Page(object):
title = "Override in subclass"
def __init__(self, dashboard):
self._dash = dashboard
self._layout = None
@property
def layout(self):
if self._layout is None:
self._layout = self._make_layout()
return self._layout
def _make_layout(self):
raise NotImplementedError("subclasses must define")
class Page1(Page):
title = "Page1"
def _make_layout(self):
self.sim_prod = Select(title="Selection:",
value="Yes",
options=["Yes", "No"]
)
self.x = row(self.sim_prod)
self.layout1 = layout([ [self.x] ], sizing_mode='scale_width')
return self.layout1
def some_callback(self, attr, old, new):
# to be defined later
pass
The widget(Select
, in this case) is built and served properly (see picture) but an error is generated when trying to reload the page:
Error:
raise RuntimeError("Models must be owned by only a single document, %r is already in a doc" % (self))
RuntimeError: Models must be owned by only a single document, WidgetBox(id='04f8b890-e3ca-48d9-954d-c33b96e80c78', ...) is already in a doc
ERROR:tornado.access:500 GET /bkapp/autoload.js?bokeh-autoload-element=8ca7bf9a-0c4f-462c-97f9-1f6b1a246628&bokeh-app-path=/bkapp&bokeh-absolute-url=http://127.0.0.1:60624/bkapp (127.0.0.1) 233.03ms
[2018-05-01 10:17:20 -0700] [94201] [DEBUG] GET /
Any suggestion how to fix it, greatly appreciated! I'm using Python 3.6 and Bokeh 0.12.15.
Cheers,
In case someone else bumps into the same issue, changing the layout
method of Page
class, as shown below, re-creates the layout every time the page is reloaded.
class Page(object):
title = "Override in subclass"
def __init__(self, dashboard):
self._dash = dashboard
self._layout = None
@property
def layout(self):
return self._make_layout()
def _make_layout(self):
raise NotImplementedError("subclasses must define")