Correct way to share multiple variables across callbacks


#1

I have been attempting to get caching to work for my app as it requires a call to a time intensive function from several callbacks. The call to the function is based on the input of 10+ files, a combination of check boxes, drop-downs and input fields. In my app, I cannot seem to get the final graphing callbacks to fire.

I am using the caching example below as a template. This is a slightly modified version of what is posted in the docs. In the original example, a single value is passed between the callbacks and cached function. As far as I can tell, the best way to share many values is to return them as a list from the ‘primary’ called function. Is this correct, or is the a better way to do this so that the variables don’t have to be re-declared in the function?

Alternatively, if we could have multiple outputs from a single callback then these work arounds wouldn’t be needed in many cases ;). I know this is being worked on and should be released soon! Link

import os
import copy
import time
import datetime

import dash
import dash_core_components as dcc
import dash_html_components as html
import numpy as np
import pandas as pd
from dash.dependencies import Input, Output
from flask_caching import Cache


external_stylesheets = [
    # Dash CSS
    'https://codepen.io/chriddyp/pen/bWLwgP.css',
    # Loading screen CSS
    'https://codepen.io/chriddyp/pen/brPBPO.css']

app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
CACHE_CONFIG = {
    'CACHE_TYPE': 'filesystem',
    'CACHE_THRESHOLD':20,
    'CACHE_DIR':'c:\\temp\\cache'
}
cache = Cache()
cache.init_app(app.server, config=CACHE_CONFIG)

N = 100

df = pd.DataFrame({
    'category': (
        (['apples'] * 5 * N) +
        (['oranges'] * 10 * N) +
        (['figs'] * 20 * N) +
        (['pineapples'] * 15 * N)
    )
})
df['x'] = np.random.randn(len(df['category']))
df['y'] = np.random.randn(len(df['category']))

app.layout = html.Div([
    dcc.Dropdown(
        id='dropdown',
        options=[{'label': i, 'value': i} for i in df['category'].unique()],
        value='apples'
    ),

    dcc.Input(
        id='txtinput',
        placeholder='input rate',
        type='number',
        step=0.1,
        min=0.1,
        max=5.0,
        value=0.8,
        ),

        html.Div([dcc.Graph(id='graph-3')]),


    # hidden signal value
    html.Div(id='signal', style={'display': 'none'})
])


# perform expensive computations in this "global store"
# these computations are cached in a globally available
# redis memory store which is available across processes
# and for all time.
@cache.memoize()
def global_store(value,txtvalue):
    # simulate expensive query
    print('Computing value with {}'.format(value))
    time.sleep(1)
    df['x'] = df['x']*txtvalue
    return df[df['category'] == value]


def generate_figure(value, txtvalue,figure):
    fig = copy.deepcopy(figure)
    filtered_dataframe = global_store(value,txtvalue)
    fig['data'][0]['x'] = filtered_dataframe['x']
    fig['data'][0]['y'] = filtered_dataframe['y']
    fig['layout'] = {'margin': {'l': 20, 'r': 10, 'b': 20, 't': 10}}
    return fig


@app.callback(Output('signal', 'children'), [Input('dropdown', 'value'), Input('txtinput','value')])
def compute_value(value,txtvalue):
    # compute value and send a signal when done
    global_store(value,txtvalue)
    return [value,txtvalue]


@app.callback(Output('graph-3', 'figure'), [Input('signal', 'children')])
def update_graph_3(return_list):
    value=return_list[0]
    txtvalue=return_list[1]
    return generate_figure(value, txtvalue,{
        'data': [{
            'type': 'histogram2d',
        }]
    })


if __name__ == '__main__':
    app.run_server(debug=True)