有没有办法从绘图图中提取当前帧?
Is there a way to extract the current frame from a plotly figure?
基本上,我有一个情节动画,它使用滑块和 pause/play 按钮来浏览数据集。我想在 Dash 回调中提取当前帧的编号(即滑块所在的“步骤”/“帧”列表中的当前索引),以便我可以更新 table 基于主图。
例如,在这种情况下:
Dash app with slider
我希望能够从图中得到‘6’,即当前步数。
这是一些带有玩具数据集的示例代码,但基本 UI 和结构相同(从上面看,减去按钮以减少代码块的长度):
import pandas as pd
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import plotly.graph_objects as go
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
# Dataset
x = [10, 1, 3, 4, 5, 6, 7, 8, 9, 10]
y = [10, 1, 3, 4, 5, 6, 7, 8, 9, 10]
df = pd.DataFrame(list(zip(x, y)), columns = ['x', 'y'])
# Adding a trace
trace = go.Scatter(x=df.x[0:2], y=df.y[0:2],
name='Location',
mode='markers',
marker=dict(color="white",
size=10,
line=dict(
color='DarkSlateGrey',
width=2)
)
)
# Adding frames
frames = [dict(name=k,data= [dict(type='scatter',
x=df.x[k:k + 1],
y=df.y[k:k + 1],
),
],
traces = [0],
) for k in range(len(df) - 1)]
fig = go.Figure(data=[trace], frames=frames)
# Adding a slider
sliders = [{
'yanchor': 'top',
'xanchor': 'left',
'active': 1,
'currentvalue': {'font': {'size': 16}, 'prefix': 'Steps: ', 'visible': True, 'xanchor': 'right'},
'transition': {'duration': 200, 'easing': 'linear'},
'pad': {'b': 10, 't': 50},
'len': 0.9, 'x': 0.15, 'y': 0,
'steps': [{'args': [[k], {'frame': {'duration': 200, 'easing': 'linear', 'redraw': False},
'transition': {'duration': 0, 'easing': 'linear'}}],
'label': k, 'method': 'animate'} for k in range(len(df) - 1)
]}]
fig['layout'].update(sliders=sliders)
app.layout = html.Div(children=[
html.Div([
dcc.Graph(
id= 'my-graph',
figure=fig
),
html.Br(),
html.Div(id='my-output'),
])
])
@app.callback(
Output(component_id='my-output', component_property='children'),
Input(component_id='my-graph', component_property='figure')
)
# How to get the current frame index here?
def update_output_div(figure):
return 'Output: {}'.format(figure['layout']['sliders'][0])
if __name__ == '__main__':
app.run_server(debug=True)
基本上,在那个回调中,我只想获取滑块的当前索引,即动画所在的当前帧。它由滑块上方的“步骤”标签显示,因此它显然存在于某处,但我终生找不到它(尝试查看 Github 源代码,但找不到它) .
如果能提供帮助,我将不胜感激!我的数据集相当大(20 mb)并且不适合浏览器内存,所以我对使用 dcc.Slider 和 dcc.Graph 的 Dash 解决方案运气不太好,它仍然是高性能的。
- https://community.plotly.com/t/trigger-callback-on-animation-frame-change/46049 无法在帧更改时触发回调
- 已切换到使用 破折号 滑块和按钮
- 现在可以使用回调控制动画,并且
setFrame()
回调作用于滑块的帧变化
- 已包含 plotly 按钮和滑块作为注释代码以供参考
import plotly.graph_objects as go
import numpy as np
from jupyter_dash import JupyterDash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
# construct a figure with frames
frames=[go.Frame(name=n, data=go.Scatter(y=np.random.uniform(1, 5, 50)))
for n in range(8)]
fig = go.Figure(data=frames[0].data, frames=frames)
# fig = fig.update_layout(
# updatemenus=[{"buttons": [{"args": [None, {"frame": {"duration": 500, "redraw": True}}],
# "label": "▶",
# "method": "animate",},],
# "type": "buttons",}],
# sliders=[{"steps": [{"args": [[f.name],{"frame": {"duration": 0, "redraw": True}, "mode": "immediate",},],
# "label": f.name, "method": "animate",}
# for f in frames],
# }],)
# Build App
app = JupyterDash(__name__)
app.layout = html.Div(
[dcc.Graph(id="graph", figure=fig),
html.Button("Play", id="dashPlay", n_clicks=0),
dcc.Slider(id="dashSlider", min=0, max=len(frames)-1, value=0, marks={i:{"label":str(i)} for i in range(len(frames))}),
dcc.Interval(id="animateInterval", interval=400, n_intervals=0, disabled=True),
html.Div(id="whichframe", children=[]),
],
)
# core update of figure on change of dash slider
@app.callback(
Output("whichframe", "children"),
Output("graph", "figure"),
Input("dashSlider", "value"),
)
def setFrame(frame):
if frame:
tfig = go.Figure(fig.frames[frame].data, frames=fig.frames, layout=fig.layout)
try:
tfig.layout['sliders'][0]['active'] = frame
except IndexError:
pass
return frame, tfig
else:
return 0, fig
# start / stop Interval to move through frames
@app.callback(
Output("animateInterval","disabled"),
Input("dashPlay", "n_clicks"),
State("animateInterval","disabled"),
)
def play(n_clicks, disabled):
return not disabled
@app.callback(
Output("dashSlider", "value"),
Input("animateInterval", "n_intervals"),
State("dashSlider", "value")
)
def doAnimate(i, frame):
if frame < (len(frames)-1):
frame += 1
else:
frame = 0
return frame
# Run app and display result inline in the notebook
app.run_server(mode="inline")
基本上,我有一个情节动画,它使用滑块和 pause/play 按钮来浏览数据集。我想在 Dash 回调中提取当前帧的编号(即滑块所在的“步骤”/“帧”列表中的当前索引),以便我可以更新 table 基于主图。
例如,在这种情况下:
Dash app with slider
我希望能够从图中得到‘6’,即当前步数。
这是一些带有玩具数据集的示例代码,但基本 UI 和结构相同(从上面看,减去按钮以减少代码块的长度):
import pandas as pd
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import plotly.graph_objects as go
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
# Dataset
x = [10, 1, 3, 4, 5, 6, 7, 8, 9, 10]
y = [10, 1, 3, 4, 5, 6, 7, 8, 9, 10]
df = pd.DataFrame(list(zip(x, y)), columns = ['x', 'y'])
# Adding a trace
trace = go.Scatter(x=df.x[0:2], y=df.y[0:2],
name='Location',
mode='markers',
marker=dict(color="white",
size=10,
line=dict(
color='DarkSlateGrey',
width=2)
)
)
# Adding frames
frames = [dict(name=k,data= [dict(type='scatter',
x=df.x[k:k + 1],
y=df.y[k:k + 1],
),
],
traces = [0],
) for k in range(len(df) - 1)]
fig = go.Figure(data=[trace], frames=frames)
# Adding a slider
sliders = [{
'yanchor': 'top',
'xanchor': 'left',
'active': 1,
'currentvalue': {'font': {'size': 16}, 'prefix': 'Steps: ', 'visible': True, 'xanchor': 'right'},
'transition': {'duration': 200, 'easing': 'linear'},
'pad': {'b': 10, 't': 50},
'len': 0.9, 'x': 0.15, 'y': 0,
'steps': [{'args': [[k], {'frame': {'duration': 200, 'easing': 'linear', 'redraw': False},
'transition': {'duration': 0, 'easing': 'linear'}}],
'label': k, 'method': 'animate'} for k in range(len(df) - 1)
]}]
fig['layout'].update(sliders=sliders)
app.layout = html.Div(children=[
html.Div([
dcc.Graph(
id= 'my-graph',
figure=fig
),
html.Br(),
html.Div(id='my-output'),
])
])
@app.callback(
Output(component_id='my-output', component_property='children'),
Input(component_id='my-graph', component_property='figure')
)
# How to get the current frame index here?
def update_output_div(figure):
return 'Output: {}'.format(figure['layout']['sliders'][0])
if __name__ == '__main__':
app.run_server(debug=True)
基本上,在那个回调中,我只想获取滑块的当前索引,即动画所在的当前帧。它由滑块上方的“步骤”标签显示,因此它显然存在于某处,但我终生找不到它(尝试查看 Github 源代码,但找不到它) .
如果能提供帮助,我将不胜感激!我的数据集相当大(20 mb)并且不适合浏览器内存,所以我对使用 dcc.Slider 和 dcc.Graph 的 Dash 解决方案运气不太好,它仍然是高性能的。
- https://community.plotly.com/t/trigger-callback-on-animation-frame-change/46049 无法在帧更改时触发回调
- 已切换到使用 破折号 滑块和按钮
- 现在可以使用回调控制动画,并且
setFrame()
回调作用于滑块的帧变化 - 已包含 plotly 按钮和滑块作为注释代码以供参考
import plotly.graph_objects as go
import numpy as np
from jupyter_dash import JupyterDash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
# construct a figure with frames
frames=[go.Frame(name=n, data=go.Scatter(y=np.random.uniform(1, 5, 50)))
for n in range(8)]
fig = go.Figure(data=frames[0].data, frames=frames)
# fig = fig.update_layout(
# updatemenus=[{"buttons": [{"args": [None, {"frame": {"duration": 500, "redraw": True}}],
# "label": "▶",
# "method": "animate",},],
# "type": "buttons",}],
# sliders=[{"steps": [{"args": [[f.name],{"frame": {"duration": 0, "redraw": True}, "mode": "immediate",},],
# "label": f.name, "method": "animate",}
# for f in frames],
# }],)
# Build App
app = JupyterDash(__name__)
app.layout = html.Div(
[dcc.Graph(id="graph", figure=fig),
html.Button("Play", id="dashPlay", n_clicks=0),
dcc.Slider(id="dashSlider", min=0, max=len(frames)-1, value=0, marks={i:{"label":str(i)} for i in range(len(frames))}),
dcc.Interval(id="animateInterval", interval=400, n_intervals=0, disabled=True),
html.Div(id="whichframe", children=[]),
],
)
# core update of figure on change of dash slider
@app.callback(
Output("whichframe", "children"),
Output("graph", "figure"),
Input("dashSlider", "value"),
)
def setFrame(frame):
if frame:
tfig = go.Figure(fig.frames[frame].data, frames=fig.frames, layout=fig.layout)
try:
tfig.layout['sliders'][0]['active'] = frame
except IndexError:
pass
return frame, tfig
else:
return 0, fig
# start / stop Interval to move through frames
@app.callback(
Output("animateInterval","disabled"),
Input("dashPlay", "n_clicks"),
State("animateInterval","disabled"),
)
def play(n_clicks, disabled):
return not disabled
@app.callback(
Output("dashSlider", "value"),
Input("animateInterval", "n_intervals"),
State("dashSlider", "value")
)
def doAnimate(i, frame):
if frame < (len(frames)-1):
frame += 1
else:
frame = 0
return frame
# Run app and display result inline in the notebook
app.run_server(mode="inline")