I could not find a nice solution, but copying the css files of panel to an static folder served by fast api, I could embed the panel app in fast api. For sure someone could give some hint how to do it correctly
and the code
import panel as pn, os
from fastapi import FastAPI, Request
from fastapi.templating import Jinja2Templates
from fastapi.staticfiles import StaticFiles
from starlette.middleware.cors import CORSMiddleware
from starlette.middleware import Middleware
from threading import Thread
from tornado.ioloop import IOLoop
from tornado import web
from bokeh.embed import server_document
from bokeh.layouts import column
from bokeh.models import ColumnDataSource, Slider
from bokeh.plotting import figure
from bokeh.sampledata.sea_surface_temperature import sea_surface_temperature
from bokeh.server.server import Server
from bokeh.themes import Theme
# with pn.util.edit_readonly(pn.state):
# pn.state.base_url = "http://127.0.0.1:5006"
# pn.state.rel_path = "http://127.0.0.1:8000"
app = FastAPI(title="NC Plot",
description="Prototype API for plotting",
version="0.0.1",
)
app.add_middleware(
CORSMiddleware,
allow_origins=['*'],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
app.mount("/static", StaticFiles(directory="static"), name="static")
templates = Jinja2Templates(directory="templates")
def pnapp(curdoc):
df = sea_surface_temperature.copy()
source = ColumnDataSource(data=df)
plot = figure(x_axis_type='datetime', y_range=(0, 25), y_axis_label='Temperature (Celsius)',
title="Sea Surface Temperature at 43.18, -70.43")
plot.line('time', 'temperature', source=source)
def callback(attr, old, new):
if new == 0:
data = df
else:
data = df.rolling('{0}D'.format(new)).mean()
source.data = ColumnDataSource.from_df(data)
slider = Slider(start=0, end=30, value=0, step=1, title="Smoothing by N Days")
slider.on_change('value', callback)
cond = False
print (pn.state.rel_path)
# with pn.util.edit_readonly:
# pn.state.base_url = "http://127.0.0.1:5006"
print ('urls', pn.state.base_url, 'efff', pn.state.rel_path)
if cond:
curdoc.add_root(column(slider, plot))
else:
col = pn.Column(slider, plot)
col.server_doc(curdoc)
to
@app.get("/")
async def bkapp_page(request: Request):
print ('reeee')
script = server_document('http://127.0.0.1:5006/bkapp')
# return {"item_id": "Foo"}
# print ('reeee',request)
return templates.TemplateResponse("embed.html", {"request": request, "script": script})
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
# with pn.util.edit_readonly(pn.state):
# pn.state.base_url = "http://127.0.0.1:5006"
# pn.state.rel_path = "http://127.0.0.1:5006"
server = Server({'/bkapp': pnapp}, port=5006, io_loop=IOLoop(),
allow_websocket_origin=["*"])
# handlers = [ (
# "http://127.0.0.1:5006/extensions/panel/css/(.*)",
# web.StaticFileHandler,
# {"path": os.path.join(os.path.dirname(__file__), "static")},
# ) ]
# server._tornado.add_handlers(r".*", handlers)
server.start()
server.io_loop.start()
th = Thread(target=bk_worker)
th.daemon = True
th.start()
to run the example ‘uvicorn filename:app --reload’. It is needed a templates folder with the html files and an static folder with the css files of panel.