Dash 客户端回调

Dash Clientside Callbacks

我正在努力处理 Dash 客户端回调。我希望创建一个流畅的动画,所以我需要客户端回调具有快速更新率。我有一个似乎可以复制问题的例子;我有一个正常的回调,并且按预期工作。当我将相同的回调转换为客户端时,它不再有效。但是,当我对客户端 return 执行 JSON.stringify 时,我看到数据字段正在更新。我不明白这个问题,但我认为这是我的 js 的问题。我不知道如何在客户端调试,所以任何错误记录的建议也将不胜感激。

这是有效的 'normal' 回调:

import dash

import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input,Output,State



fig_test={
        'data': [
            {'x': [1, 2, 3], 'y': [4, 1, 2], 'type': 'bar', 'name': 'SF'},
            {'x': [1, 2, 3], 'y': [2, 4, 5], 'type': 'bar', 'name': u'Montréal'},
        ],
        'layout': {
            'title': 'Dash Data Visualization'
        }
    }
app = dash.Dash(__name__)
app.layout = html.Div([
    html.Button("Button 1", id="btn1"),
    dcc.Graph(id="graph", figure=fig_test),
    dcc.Slider(
             id='interval-component',
             min=0,
             max=36,
             step=1,
             value=10,
         ),
    html.Div(id="log"),
    html.Pre(
        id='structure',
        style={
            'border': 'thin lightgrey solid', 
            'overflowY': 'scroll',
            'height': '275px'
        }
    )
])

@app.callback(
    Output("graph", "figure"), 
    Input('interval-component','value'),Input("graph", "figure"),Input("btn1", "n_clicks"))
def display_structure(value, figure, btn1):
    figure['data'][0]['y'][1] = value
    return {'data': figure['data'], 'layout':figure['layout']}
app.run_server(debug=False)

这是通过客户端实现的相同回调:


import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input,Output,State



fig_test={
        'data': [
            {'x': [1, 2, 3], 'y': [4, 1, 2], 'type': 'bar', 'name': 'SF'},
            {'x': [1, 2, 3], 'y': [2, 4, 5], 'type': 'bar', 'name': u'Montréal'},
        ],
        'layout': {
            'title': 'Dash Data Visualization'
        }
    }
app = dash.Dash(__name__)
app.layout = html.Div([
    html.Button("Button 1", id="btn1"),
    dcc.Graph(id="graph", figure=fig_test),
    dcc.Slider(
             id='interval-component',
             min=0,
             max=36,
             step=1,
             value=10,
         ),
    html.Div(id="log"),
    html.Pre(
        id='structure',
        style={
            'border': 'thin lightgrey solid', 
            'overflowY': 'scroll',
            'height': '275px'
        }
    )
])
app.clientside_callback(
    """
    function(value, figure, btn1){
        figure['data'][0]['y'][1] = value 
                
        return {'data': figure['data'], 'layout':figure['layout']};
    }
    """, Output("graph", "figure"), [Input('interval-component','value'),Input("graph", "figure"),Input("btn1", "n_clicks")])
app.run_server(debug=False)

如果我实现客户端以像这样对输出进行 jsonify:

app.clientside_callback(
    """
    function(value, figure, btn1){
        figure['data'][0]['y'][1] = value 
                
        return JSON.stringify({'data': figure['data'], 'layout':figure['layout']});
    }
    """, Output("log", "children"), [Input('interval-component','value'),Input("graph", "figure"),Input("btn1", "n_clicks")])

我可以看到正在更新的值,所以我不知道是什么问题。

所以我想出了 'smooth animation' 用于布局更新,其中 'extend data' 是不可能的,所以这个答案中的解决方案: 不适用。此外,它允许实时更新数据的平滑动画,而不依赖于 'animate' api。这不是我提出的问题的确切答案,而是解决了这个概念。 如果您不熟悉框架,或者不确定如何设置图形,请在此处查看 plotly 的示例:https://plotly.com/python/animations/ 设置伪代码:

## setup figure, 
for data in some_data:
    ##do something
    fig['frames'].append(frame)
fig1 = go.Figure(fig) 

为您的框架设置存储,并为要传递给客户端回调的框架设置存储。当我设置一个模拟实时数据采集的过程时,我有一个用于 'polling' 的计时器和一个用于动画的辅助计时器。如果您不希望计时器作为触发器,主要概念仍然相同;有一些 'animate' 触发器,在本例中为 'interval-component',以启动快速刷新辅助计时器。

app.layout = html.Div([
        dcc.Store(id='frames-stored', data=fig1['frames']),
        dcc.Store(id='frames'),
        dcc.Interval(
            id='interval-component',
            interval=1*500, # in milliseconds
            n_intervals=0
        ),
        dcc.Interval(id='graph-refresher',
                     interval=1*25,
                     n_intervals=0,
                     max_intervals=50,
                     disabled=True),
    dcc.Graph(id="graph", figure=fig1),
])

现在一个回调来捕获您的 'animation' 触发器并将帧传递给您的客户端回调:

@app.callback(
    Output("frames", "data"),Output("graph-refresher", "disabled"),
    Output("graph-refresher", "max_intervals"),Output('graph-refresher','n_intervals'),
    Input("interval-component", "n_intervals"),State("frames-stored", "data"))
def data_smoother(n_intervals,frames):
    ## can do whatever here as long as a list of frames are passed to the store
    selected_frames = frames[n_intervals]
    return selected_frames,False,'some_max',0

此回调打开客户端回调的计时器,并用 'some_max' 重置 max_intervals。这将取决于你在做什么。 现在处理 'animation'.

的客户端回调
app.clientside_callback(
    """
    function(n_intervals, frames){
            return {'data':frames[parseInt(n_intervals)]['data'], 'layout':frames[parseInt(n_intervals)]['layout']};
    }
     """,Output("graph", "figure"),Input('graph-refresher','n_intervals'), State("frames", "data")) 

我希望这对某人有用!