When my dash app is launched a global variable is created (dat) by a computationally expensive function. I want dat to be updated at regular intervals but not by any user action. I understand that normal python global variables are unsuitable for use in dash due to the way memory is shared so I am using a hidden div to store the data which is updated at regular intervals.
I do not want the data to be updated when the app is launched by each user, but rather want the updating to happen in the background and preferably update all users apps with the updated data. If the app had to be updated with a page refresh that’s fine too, but the updated data will be available for all users.
I have included an example below which performs the task that I want to do, the trouble is that storing the data in div is fine if it is a string or a numpy array, but if the data structure is more complex like a pandas dataframe (see commented lines of code) or a dictionary of pandas dataframes then the app will run, but the page won’t load in a browser (Error loading layout). Maybe memoizing is the solution, though all examples I have seen seem subtly different enough from this such that I can’t see how it could be used. The use of a hidden div seems like a bit of a hack to me but is recommended by the plotly help pages
So my question is how can I create and update global variables in dash using more complex data structures than strings or arrays?
import dash
from dash.dependencies import Input, Output, Event
import dash_html_components as html
import dash_core_components as dcc
from datetime import datetime
import numpy as np
import pandas as pd
app = dash.Dash(__name__)
def compute_expensive_data():
t=datetime.now()
dat = np.array([t.minute, t.second])
#d = {'time' : pd.Series(np.array([t.minute, t.second]), index=['minute', 'second'])}
#dat = pd.DataFrame(d)
return dat
dat = compute_expensive_data()
print(dat)
app.layout = html.Div([
html.H3('Original Time: Minute = ' + str(dat[0]) + ': Second = ' + str(dat[1])),
#html.H3('Original Time: Minute = ' + str(dat['time']['minute']) + ': Second = ' + str(dat['time']['second'])),
html.Div(id='title-line-children'),
dcc.RadioItems(
id='time-dropdown',
options=[
{'label': 'Minute', 'value': 'minute'}, {'label': 'Second', 'value': 'second'},
],
value='minute'
),
# Hidden div inside the app that stores the intermediate value
html.Div(id='intermediate-value', style={'display': 'none'}, children = dat),
dcc.Interval(
id='interval-component',
interval=20*1000 # 20 seconds in milliseconds
)
])
@app.callback(
Output('title-line-children', 'children'),
[Input('time-dropdown', 'value'), Input('intermediate-value', 'children')])
def render(value,dat1):
if value == 'minute':
printStr = str(dat1[0])
#printStr = str(dat1['time']['minute'])
outStr = 'Minute = ' + printStr
elif value == 'second':
printStr = str(dat1[1])
#printStr = str(dat1['time']['second'])
outStr = 'Second = ' + str(dat1[1])
return outStr
@app.callback(Output('intermediate-value', 'children'),
events=[Event('interval-component', 'interval')])
def update_global_var():
return compute_expensive_data()
if __name__ == '__main__':
app.run_server(debug=True)