【问题标题】:Global variable Updated by and Interval Componant全局变量更新者和间隔组件
【发布时间】:2026-02-03 08:35:01
【问题描述】:

当我的 dash 应用程序启动时,一个全局变量由一个计算量大的函数创建 (dat)。我希望 dat 定期更新,而不是通过任何用户操作。我知道由于内存共享的方式,普通的 python 全局变量不适合在破折号中使用,所以我使用隐藏的 div 来存储定期更新的数据。

我不希望在每个用户启动应用程序时更新数据,而是希望在后台进行更新,并最好使用更新后的数据更新所有用户的应用程序。如果必须使用页面刷新来更新应用程序,那也没关系,但更新后的数据将可供所有用户使用。

我在下面包含了一个示例,它执行我想要执行的任务,问题是如果它是字符串或 numpy 数组,则将数据存储在 div 中很好,但如果数据结构更复杂,例如pandas 数据框(参见注释的代码行)或 pandas 数据框字典,然后应用程序将运行,但页面不会在浏览器中加载(错误加载布局)。也许记忆是解决方案,尽管我看到的所有例子似乎都与这个有细微的不同,以至于我看不出它是如何使用的。对我来说,使用隐藏的 div 似乎有点小技巧,但情节帮助页面推荐使用

所以我的问题是如何使用比字符串或数组更复杂的数据结构在 dash 中创建和更新全局变量?

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)

【问题讨论】:

    标签: python plotly-dash


    【解决方案1】:

    好的,所以答案很简单。我只需要 jsonify 我的数据框。那么它基本上只是一个可以保存在隐藏div中的字符串。

    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()
        d = {'time' : pd.Series(np.array([t.minute, t.second]), index=['minute', 'second'])}
        dat = pd.DataFrame(d).to_json()
    
        return  dat
    
    dat = compute_expensive_data()
    print(dat)
    
    app.layout = html.Div([
            html.H3('Original Time: Minute = ' + str(pd.read_json(dat)['time']['minute']) + ': Second = ' + str(pd.read_json(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(pd.read_json(dat1)['time']['minute'])
            outStr = 'Minute = ' + printStr
        elif value == 'second':
            printStr = str(pd.read_json(dat1)['time']['second'])
            outStr = 'Second = ' + printStr
    
        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)
    

    【讨论】:

    • 那么这会在页面重新加载时重新更新全局变量吗?我基本上有一个更新的 JSON,我希望 dash 应用程序在每次我重新加载页面时重新加载最新版本....
    【解决方案2】:

    答案已更新至 Dash 1.8:

    import dash
    from dash.dependencies import Input, Output
    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()
        d = {'time' : pd.Series(np.array([t.minute, t.second]), index=['minute', 'second'])}
        dat = pd.DataFrame(d).to_json()
        return  dat
    
    dat = compute_expensive_data()
    print(dat)
    
    app.layout = html.Div([
            html.H3('Original Time: Minute = ' + str(pd.read_json(dat)['time']['minute']) + ': Second = ' + str(pd.read_json(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=2*1000, # 20 seconds in milliseconds
                n_intervals=0
            )
        ])
    
    @app.callback(
        Output('title-line-children', 'children'),
        [Input('time-dropdown', 'value'), Input('intermediate-value', 'children')])
    def render(value,dat1):
        if value == 'minute':
            printStr = str(pd.read_json(dat1)['time']['minute'])
            outStr = 'Minute = ' + printStr
        elif value == 'second':
            printStr = str(pd.read_json(dat1)['time']['second'])
            outStr = 'Second = ' + printStr
        return outStr
    
    @app.callback(Output('intermediate-value', 'children'),
                  [Input('interval-component', 'n_intervals')])
    def update_global_var(n):
        return compute_expensive_data()
    
    if __name__ == '__main__':
        app.run_server(debug=False)
    

    【讨论】: