dcc.Store#

dcc.Store 组件用于在浏览器中存储 JSON 数据。

存储 Clicks#

from dash import Dash, html, dcc, Output, Input, State, callback
from dash.exceptions import PreventUpdate

# 这个样式表使按钮和表格看起来很漂亮。
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']

app = Dash(__name__, external_stylesheets=external_stylesheets)

app.layout = html.Div([
    # memory 存储在每次页面刷新时都会恢复到默认状态。
    dcc.Store(id='memory'),
    # local 存储只会在页面首次加载时获取初始数据,并保留数据直到被清除。
    dcc.Store(id='local', storage_type='local'),
    # 与 local 存储相同,但在浏览器/标签页关闭时会丢失数据。
    dcc.Store(id='session', storage_type='session'),
    html.Table([
        html.Thead([
            html.Tr(html.Th('Click to store in:', colSpan="3")),
            html.Tr([
                html.Th(html.Button('memory', id='memory-button')),
                html.Th(html.Button('localStorage', id='local-button')),
                html.Th(html.Button('sessionStorage', id='session-button'))
            ]),
            html.Tr([
                html.Th('Memory clicks'),
                html.Th('Local clicks'),
                html.Th('Session clicks')
            ])
        ]),
        html.Tbody([
            html.Tr([
                html.Td(0, id='memory-clicks'),
                html.Td(0, id='local-clicks'),
                html.Td(0, id='session-clicks')
            ])
        ])
    ])
])

# 为每个存储创建两个回调
for store in ('memory', 'local', 'session'):
    # 为适当的存储添加点击事件
    @callback(Output(store, 'data'),
              Input('{}-button'.format(store), 'n_clicks'),
              State(store, 'data'))
    def on_click(n_clicks, data):
        if n_clicks is None:
            # 防止存储组件的 None 回调非常重要。
            # 您不希望无故更新存储。
            raise PreventUpdate
        # 如果没有数据,提供默认的数据字典,点击次数为 0
        data = data or {'clicks': 0}

        data['clicks'] = data['clicks'] + 1
        return data

    # 在表格单元格中输出存储的点击次数
    @callback(Output('{}-clicks'.format(store), 'children'),
              # 由于我们在输出中使用了 data 属性,
              # 我们无法在加载时通过 data 属性获取初始数据。
              # 为了解决这个问题,您可以使用 modified_timestamp 作为输入,将数据作为状态。
              # 这个限制是由于初始的 None 回调造成的
              # https://github.com/plotly/dash-renderer/pull/81
              Input(store, 'modified_timestamp'),
              State(store, 'data'))
    def on_data(ts, data):
        if ts is None:
            raise PreventUpdate

        data = data or {}

        return data.get('clicks', 0)


# if __name__ == '__main__':
#     app.run(debug=True, port=8077, threaded=True)

在回调之间共享数据#

from dash import Dash, html, dcc, Input, Output, dash_table, callback
from dash.exceptions import PreventUpdate

import collections
import pandas as pd


app = Dash(__name__)

df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/gapminderDataFiveYear.csv')


app.layout = html.Div([
    dcc.Store(id='memory-output'),
    dcc.Dropdown(df.country.unique(),
                 ['Canada', 'United States'],
                 id='memory-countries',
                 multi=True),
    dcc.Dropdown({'lifeExp': 'Life expectancy', 'gdpPercap': 'GDP per capita'},
                 'lifeExp',
                 id='memory-field'),
    html.Div([
        dcc.Graph(id='memory-graph'),
        dash_table.DataTable(
            id='memory-table',
            columns=[{'name': i, 'id': i} for i in df.columns]
        ),
    ])
])


@callback(Output('memory-output', 'data'),
              Input('memory-countries', 'value'))
def filter_countries(countries_selected):
    if not countries_selected:
        # Return all the rows on initial load/no country selected.
        return df.to_dict('records')

    filtered = df.query('country in @countries_selected')

    return filtered.to_dict('records')


@callback(Output('memory-table', 'data'),
              Input('memory-output', 'data'))
def on_data_set_table(data):
    if data is None:
        raise PreventUpdate

    return data


@callback(Output('memory-graph', 'figure'),
              Input('memory-output', 'data'),
              Input('memory-field', 'value'))
def on_data_set_graph(data, field):
    if data is None:
        raise PreventUpdate

    aggregation = collections.defaultdict(
        lambda: collections.defaultdict(list)
    )

    for row in data:

        a = aggregation[row['country']]

        a['name'] = row['country']
        a['mode'] = 'lines+markers'

        a['x'].append(row[field])
        a['y'].append(row['year'])

    return {
        'data': [x for x in aggregation.values()]
    }


# if __name__ == '__main__':
#     app.run(debug=True, threaded=True, port=10450)

Storage 的限制#

  • 浏览器的最大存储空间由以下因素决定:

    • 手机或笔记本电脑。

    • 在浏览器中,一个复杂的算法在配额管理(Quota Management)中实现。

    • UTF-16 的存储编码最终只能节省 UTF-8 的一半大小。

    • 一般来说,在大多数环境中存储 2MB 是安全的,而在大多数只有桌面的应用程序中存储 5~10MB 是安全的。

  • modified_timestamp 为只读。

检索初始存储数据#

如果使用 data 属性作为输出,则无法使用 data 属性获得加载时的初始数据。为了应对这种情况,可以使用 modified_timestamp 作为 Input,使用 data 作为 State

dcc.Store 属性#

id(字符串;必需):此组件的 ID,用于在回调中识别 Dash 组件。ID 需要在应用程序的所有组件中是唯一的。

  • clear_data(布尔;默认 False):设置为 True 删除 data_key 中包含的数据。

  • data(dict | list | number | string | boolean;可选):id 的存储数据。

  • modified_timestamp(数字;默认 -1):上次修改存储的时间。

  • storage_type(’local’, ‘session’, ‘memory’(默认)):网络存储的类型。memory:只保留在内存中,刷新页面时重置。 localwindow.localStorage,浏览器退出后保留数据。session:window.sessionStorage,一旦浏览器退出,数据将被清除。