Hi everyone, I’m trying to deploy a Panel dashboard application with multiuser capabilities. At the moment, as far as I have been able to investigate, I need to use asynchronous operations or threads to avoid users blocking each other, in my case these blockings occur in two moments:
- When the user enters the application, gets the data and serves it in graphs in a material design template.
- When a user needs to choose a date in a widget and the whole dashboard is updated with the data obtained from the new date.
So now I find myself trying to solve the first point. Having seen the example code in the thread linked here, I find that this code works correctly using pn.Columns:
import pandas as pd
import panel as pn
import holoviews as hv
import threading
import time
def holoviz_config(plot_engine, pandas_plot_backend, loading_type, loading_color, sizing_mode):
hv.extension(plot_engine)
pd.options.plotting.backend = pandas_plot_backend
pn.extension(loading_spinner=loading_type, loading_color=loading_color, sizing_mode=sizing_mode)
def empty_plot_with_text(str):
# Creates an empty plot with a text inside
empty_plot = hv.Curve([])
empty_plot = empty_plot * \
hv.Text(0.6, 0.6, str)
return empty_plot
def create_empty_plot_panel(str):
# Creates an empty plot in a panel
empty_plot = empty_plot_with_text(str)
panel = pn.panel(empty_plot, sizing_mode='stretch_width', linked_axes=False)
return panel
def create_dashboard(col: pn.Column):
print('Creating dashboard')
panel_A = create_empty_plot_panel('A')
# We are going to simulate a 5 seconds task here before plots are finally appended to template
time.sleep(5)
# Creates a grid and append the plot to it
grid = pn.GridSpec(sizing_mode='stretch_both', ncols=3, nrows=2, mode='override')
grid[0, :1] = panel_A
# Append grid to Panel.Column object
col[0] = grid
def get_user_page():
loading = pn.indicators.LoadingSpinner(value=True, width=100, height=100, align='center')
col = pn.Column(loading)
t = threading.Thread(target=create_dashboard, args=(col, ))
t.daemon = True
t.start()
return col
def main():
holoviz_config(plot_engine='bokeh', pandas_plot_backend='holoviews', loading_type='dots', loading_color='#00204e', sizing_mode="stretch_width")
pn.serve(get_user_page, port=5006, allow_websocket_origin='localhost:5006', websocket_origin='localhost:5006', show=False, threaded=True, n_threads=8)
if __name__ == '__main__':
main()
However, I find it difficult to adapt it to the template object I’m using in my app. Since the material design template object it is not a listable object such as pn.Col, I think the template object is returning a copy and I can’t modify the template structure from another thread in order to see the content in the website and it is always showing the loading indicator.
I have recreated my problem in the following code fragment.
import pandas as pd
import hvplot.pandas
import panel as pn
import holoviews as hv
import threading
import time
def holoviz_config(plot_engine, pandas_plot_backend, loading_type, loading_color, sizing_mode):
hv.extension(plot_engine)
pd.options.plotting.backend = pandas_plot_backend
pn.extension(loading_spinner=loading_type, loading_color=loading_color, sizing_mode=sizing_mode)
def empty_plot_with_text(str):
# Creates an empty plot with a text inside
empty_plot = hv.Curve([])
empty_plot = empty_plot * \
hv.Text(0.6, 0.6, str)
return empty_plot
def create_empty_plot_panel(str):
# Creates an empty plot and appends it in a panel
empty_plot = empty_plot_with_text(str)
panel = pn.panel(empty_plot, sizing_mode='stretch_width', linked_axes=False)
return panel
def create_dashboard(template: pn.template.MaterialTemplate):
print('Creating dashboard')
panel_A = create_empty_plot_panel('A')
# We are going to simulate a 5 seconds task here before plots are finally appended to template
time.sleep(5)
# Creates a grid and append plots to it
grid = pn.GridSpec(sizing_mode='stretch_both', ncols=1, nrows=1, mode='override')
grid[0, :1] = panel_A
# Append grid to template
template.main.append(grid)
def get_user_page():
loading = pn.indicators.LoadingSpinner(value=True, width=100, height=100, align='center')
material_dashboard = pn.template.MaterialTemplate(title='Threading Test APP (Template)', header_background='#00204e', favicon='/images/favicon.ico')
material_dashboard.main.append(loading)
t = threading.Thread(target=create_dashboard, args=(material_dashboard, ))
t.daemon = True
t.start()
return material_dashboard
def main():
holoviz_config(plot_engine='bokeh', pandas_plot_backend='holoviews', loading_type='dots', loading_color='#00204e', sizing_mode="stretch_width")
pn.serve(get_user_page, port=5006, allow_websocket_origin='localhost:5006', websocket_origin='localhost:5006', show=False, threaded=True, n_threads=8)
if __name__ == '__main__':
main()
You can run it from the command line just using:
python app.py
Any suggestion or strategy on how to modify material design template structure from another thread and see the contents updated in the object returned in pn.serve?
Thank you for your time, any idea or suggestion will be welcome.