Pop notebook-embedded dashboard to separate html


I have a dashboard with several plots & Python callbacks which I embedded into a Notebook as shown in the notebook embed example.

As the dashboard got quite big, I would like to visualize it on a separate own tab instead of a notebook cell: is there any way to do that?

I have tried with output_file(), but it doesn’t really work when visualizing an application,
Please note that I am not expecting my dashboard to work after I kill the notebook’s IPython kernel that generated it: I pretty much just would like it to visualize it in its own page instead of a notebook cell.

I understand that I could rewrite my dashboard as a script and use bokeh serve dashboard.py, but I do a number of steps to fetch and prepare the data which work quite nice in the notebook and I would like to keep everything together in the same file, if possible.

@dabbatelli It’s possible, but you’ll have to put a few pieces together yourself, since show encapsulates all the details, including the URL that the app ends up running on. You basically will need to do this part of the process yourself:

from tornado.ioloop import IOLoop
from ..server.server import Server

def _origin_url(url):
    if url.startswith("http"):
        url = url.split("//")[1]
    return url

loop = IOLoop.current()

origin = _origin_url(notebook_url)

server = Server({"/": app}, io_loop=loop, port=0, allow_websocket_origin=[origin])


You’ll need to supply notebook_url value there (just what you would normally pass to show). You can also hardcode a port if you prefer.

Afterwards you can obtain the URL to connect to in another tab by executing

def _server_url(url, port):
    if url.startswith("http"):
        return '%s:%d%s' % (url.rsplit(':', 1)[0], port, "/")
        return 'http://%s:%d%s' % (url.split(':')[0], port, "/")

url = _server_url(notebook_url, server.port)

Hello @Bryan,

Thanks: not as smooth as calling show(), but it works.

However, I had to hardcode a port and explicitly allow it like this:

server = Server({"/": bkapp}, io_loop=loop, port=5006, allow_websocket_origin=['localhost:5006'])

… or alternatively let it choose the port and allow everything:

server = Server({"/": bkapp}, io_loop=loop, port=0, allow_websocket_origin=['*'])

My understanding was that the websocket connection should originate from the noterbook, so origin would typically be localhost:8888, but then I get:

ERROR:bokeh.server.views.ws:Refusing websocket connection from Origin 'http://localhost:58776'; use --allow-websocket-origin=localhost:58776 or set BOKEH_ALLOW_WS_ORIGIN=localhost:58776 to permit this; currently we allow origins {'localhost:8888'}

So, it looks like the websocket connection actually originates from server.port itself instead?

@dabbatelli The “origin” is basically “whatever is in browser URL bar” of the page that displays the Bokeh app. When actually embedding the app in the notebook with show, the origin is the notebook URL e.g. localhost:8888 or similar. But you are specifically not viewing the app from the notebook, so that definitely won’t be the correct origin anymore. In my haste I overlooked this difference in my first response. If this is just local viewing then the "*" wildcard is the simplest value to configure.