Black Lives Matter. Please consider donating to Black Girls Code today.

Global variable Updated by and Interval Componant

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():
    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()

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'])),
                {'label': 'Minute', 'value': 'minute'}, {'label': 'Second', 'value': 'second'},

        # Hidden div inside the app that stores the intermediate value
        html.Div(id='intermediate-value', style={'display': 'none'}, children = dat),

            interval=20*1000 # 20 seconds in milliseconds


    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__':

I figured it out in the end. The answer was pretty simple. I needed to jsonify the dataframe before saving it to a div. Just in case others are curious:

1 Like

Is this still the best of working with a global variable in Dash (i.e. storing it in a hidden div)?

Edit: seems like it is. Ref:

check this one…how to share global variable