5

I am trying to embed a Panel app within a Flask app. I am struggling to get the app to update when the widget changes. To reproduce the issue I have used the sea temperature example from Bokeh which is included in the Panel docs.

The Panel app works fine on its own when using panel.show() - when the slider widget value is changed the plot updates.

The Bokeh only version of the app (not using Panel, as in the example) also works fine when working with Flask.

With my attempt to embed the Panel in Flask, the slider no longer updates the chart.

I suspect I am missing something within the modify_doc function so that the request is handled correctly.

Here is the code

from flask import Flask, render_template

from bokeh.embed import server_document
from bokeh.models import ColumnDataSource
from bokeh.plotting import figure
import param
import panel as pn
from bokeh.server.server import Server
from tornado.ioloop import IOLoop

from bokeh.sampledata.sea_surface_temperature import sea_surface_temperature


class SeaSurface(param.Parameterized):

    smoothing = param.Integer(0,bounds=(0, 30))

    def __init__(self, **params):
        super(SeaSurface, self).__init__(**params)
        self.df = sea_surface_temperature.copy()
        self.source = ColumnDataSource(data=self.df)

        self.plot = figure(x_axis_type='datetime', y_range=(0, 25), y_axis_label='Temperature (Celsius)',
                title="Sea Surface Temperature at 43.18, -70.43")

        self.plot.line('time', 'temperature', source=self.source)

    @param.depends('smoothing')
    def view(self):
        if self.smoothing == 0:
            data = self.df
        else:
            data = self.df.rolling('{0}D'.format(self.smoothing)).mean()

        self.source.data = ColumnDataSource(data=data).data

        return self.plot

    def panel(self):
        return pn.Row(self.param, self.view)


# uncomment to run app directly out of panel and comment Flask setup below
# sea = SeaSurface(name='Sea Surface')
# sea.panel().show()

# Flask setup
app = Flask(__name__)

def modify_doc(doc):
    sea = SeaSurface(name='Sea Surface')
    doc.add_root(sea.panel().get_root())


@app.route('/', methods=['GET'])
def bkapp_page():
    script = server_document('http://localhost:5006/bkapp')
    return render_template("bokeh_example.html", script=script, template="Flask")


def bk_worker():
    # Can't pass num_procs > 1 in this configuration. If you need to run multiple
    # processes, see e.g. flask_gunicorn_embed.py
    server = Server({'/bkapp': modify_doc}, io_loop=IOLoop(), allow_websocket_origin=["127.0.0.1:8000"])
    server.start()
    server.io_loop.start()


from threading import Thread
Thread(target=bk_worker).start()

if __name__ == '__main__':
    print('Opening single process Flask app with embedded Bokeh application on http://localhost:8000/')
    print()
    print('Multiple connections may block the Bokeh app in this configuration!')
    print('See "flask_gunicorn_embed.py" for one way to run multi-process')
    app.run(port=8000)

Panel seems great btw - think this is the answer for Python dashboarding

rob_sp
  • 71
  • 4

1 Answers1

2

Solved my own problem almost as soon as I had posted.

The modify_doc function should have included the original doc variable in the call to get_root() so get_root(doc) instead of get_root() as below:

def modify_doc(doc):
    sea = SeaSurface(name='Sea Surface')
    doc.add_root(sea.panel().get_root(doc))
rob_sp
  • 71
  • 4