This Bokeh app below served by a Bokeh server that has been embedded
in another web app framework. For more information see the section
Embedding Bokeh Server as a Library
in the User's Guide.
{{ script|safe }}
```
If you wish to replicate navigate to the `examples/gallery/apps/flask` directory and follow the these steps:
* Step One: call `python holoviews_app.py` in the terminal (this will start the Panel/Bokeh server)
* Step Two: open a new terminal and call `python flask_app.py` (this will start the Flask application)
* Step Three: go to web browser and type `localhost:5000` and the app will appear
## Combining HoloViews and Panel or Bokeh Plots/Widgets
While HoloViews provides very convenient ways of creating an app it is not as fully featured as Bokeh itself is. Therefore we often want to extend a HoloViews based app with Panel or Bokeh plots and widgets. Here we will discover to achieve this with both Panel and then the equivalent using pure Bokeh.
```python
import holoviews as hv
import numpy as np
import panel as pn
# Create the holoviews app again
def sine(phase):
xs = np.linspace(0, np.pi*4)
return hv.Curve((xs, np.sin(xs+phase))).opts(width=800)
stream = hv.streams.Stream.define('Phase', phase=0.)()
dmap = hv.DynamicMap(sine, streams=[stream])
start, end = 0, np.pi*2
slider = pn.widgets.FloatSlider(start=start, end=end, value=start, step=0.2, name="Phase")
# Create a slider and play buttons
def animate_update():
year = slider.value + 0.2
if year > end:
year = start
slider.value = year
def slider_update(event):
# Notify the HoloViews stream of the slider update
stream.event(phase=event.new)
slider.param.watch(slider_update, 'value')
def animate(event):
if button.name == '► Play':
button.name = '❚❚ Pause'
callback.start()
else:
button.name = '► Play'
callback.stop()
button = pn.widgets.Button(name='► Play', width=60, align='end')
button.on_click(animate)
callback = pn.state.add_periodic_callback(animate_update, 50, start=False)
app = pn.Column(
dmap,
pn.Row(slider, button)
)
app
```
If instead we want to deploy this we could add `.servable` as discussed before or use `pn.serve`. Note however that when using `pn.serve` all sessions will share the same state therefore it is best to
wrap the creation of the app in a function which we can then provide to `pn.serve`. For more detail on deploying Panel applications also see the [Panel server deployment guide](https://panel.holoviz.org/user_guide/Server_Deployment.html).
Now we can reimplement the same example using Bokeh allowing us to compare and contrast the approaches:
```python
import numpy as np
import holoviews as hv
from bokeh.io import show, curdoc
from bokeh.layouts import layout
from bokeh.models import Slider, Button
renderer = hv.renderer('bokeh').instance(mode='server')
# Create the holoviews app again
def sine(phase):
xs = np.linspace(0, np.pi*4)
return hv.Curve((xs, np.sin(xs+phase))).opts(width=800)
stream = hv.streams.Stream.define('Phase', phase=0.)()
dmap = hv.DynamicMap(sine, streams=[stream])
# Define valid function for FunctionHandler
# when deploying as script, simply attach to curdoc
def modify_doc(doc):
# Create HoloViews plot and attach the document
hvplot = renderer.get_plot(dmap, doc)
# Create a slider and play buttons
def animate_update():
year = slider.value + 0.2
if year > end:
year = start
slider.value = year
def slider_update(attrname, old, new):
# Notify the HoloViews stream of the slider update
stream.event(phase=new)
start, end = 0, np.pi*2
slider = Slider(start=start, end=end, value=start, step=0.2, title="Phase")
slider.on_change('value', slider_update)
callback_id = None
def animate():
global callback_id
if button.label == '► Play':
button.label = '❚❚ Pause'
callback_id = doc.add_periodic_callback(animate_update, 50)
else:
button.label = '► Play'
doc.remove_periodic_callback(callback_id)
button = Button(label='► Play', width=60)
button.on_click(animate)
# Combine the holoviews plot and widgets in a layout
plot = layout([
[hvplot.state],
[slider, button]], sizing_mode='fixed')
doc.add_root(plot)
return doc
# To display in the notebook
show(modify_doc, notebook_url='localhost:8888')
# To display in a script
# doc = modify_doc(curdoc())
```