考虑以下玩具 Python 类,它偶尔可以从某些源获取附加数据,并更新其显示:
# minimum_working_example.py
import plotly.graph_objects as go
import random
class MyData:
def __init__(self):
self.mydata = []
self.figure = go.FigureWidget()
self.figure.add_trace(go.Scatter(
name = 'mydata',
))
self.get_more_data()
def get_more_data(self):
self.mydata += [ random.random() for _ in range(10) ]
self.figure.update_traces(
selector=dict(name='mydata'),
y = self.mydata,
x = list(range(len(self.mydata))),
)
如果我进入 Jupyter 笔记本并说出类似的话
# interactive jupyter prompt
from minimum_working_example import MyData
mydata = MyData()
display(mydata.figure)
然后我得到一个可以摆弄的图形:缩放轴、切换哪些迹线可见等。(我的非最小示例有多个迹线。)因为该图形是
FigureWidget
,所以调用 mydata.get_more_data()
会发生变化图表上的轨迹,但它不会改变任何其他特征。
[
我想将此显示从交互式笔记本移动到带有“获取更多数据”按钮的 Dash 应用程序,但到目前为止,我尝试过的所有操作都会导致该图形重置所有修改,而不是就地更新。这是一个以教育方式失败的示例,基于部分属性更新的文档页面:
# mwe_dash.py
from dash import Dash, html, dcc, Input, Output, Patch, callback
import plotly.graph_objects as go
import datetime
import random
import minimum_working_example
app = Dash(__name__)
mydata = minimum_working_example.MyData()
app.layout = html.Div(
[
html.Button("Append", id="append-new-val"),
dcc.Graph(figure=mydata.figure, id="append-example-graph"),
]
)
@callback(
Output("append-example-graph", "figure"),
Input("append-new-val", "n_clicks"),
prevent_initial_call=True,
)
def put_more_data_on_figure(n_clicks):
mydata.get_more_data()
return mydata.figure
if __name__ == "__main__":
app.run(port=8056)
如果我用
python mwe_dash.py
单独运行它,那么每次我按下“获取更多数据”按钮时,该数字都会完全重置 - 就像上面文档页面上的示例一样。但如果我在 Jupyter 中运行它,使用
# in jupyter again
from mwe_dash import app, mydata
display(mydata.figure)
app.run(jupyter_server_url='localhost:8888',port=8057)
然后,当图形更新时,与 Jupyter 笔记本中图形的外观改变交互将应用到 Dash 应用程序中。但是,每次按下“获取更多数据”按钮时,Dash 应用程序中的数字都会重置为笔记本中的版本。
如何获取 Dash 应用程序中的图形以重现 Jupyter 笔记本中的更新而不重置行为?我假设我需要从图形到 Dash 应用程序进行一些额外的回调。即使我去掉了这个分析工具的笔记本部分,是否有必要在后台运行 Jupyter 服务器?
如果您想“将此显示从交互式笔记本移动到 Dash 应用程序”,请使用
go.Figure()
而不是 go.FigureWidget()
。
您没有进行部分更新:
fig.update_traces()
更新后端的图形,回调只是返回它(整个图形),因此在客户端您会丢失先前加载的图形的状态。您应该使用 Patch
类来扩展 x 和 y 中的值列表。
不要在 MyData 类中创建/更新图形,只需创建/更新数据并在需要时使用它们(单一责任原则)。
class MyData:
def __init__(self):
self.data = []
self.get_more_data()
def get_more_data(self):
newdata = [ random.random() for _ in range(10) ]
self.data += newdata
return newdata
app = Dash(__name__)
mydata = MyData()
y = mydata.data
x = list(range(len(y)))
fig = go.Figure(go.Scatter(x=x, y=y, name='mydata'))
app.layout = html.Div(
[
html.Button("Append", id="append-new-val"),
dcc.Graph(figure=fig, id="append-example-graph"),
]
)
@callback(
Output("append-example-graph", "figure"),
Input("append-new-val", "n_clicks"),
prevent_initial_call=True,
)
def put_more_data_on_figure(n_clicks):
i = len(mydata.data)
new_y = mydata.get_more_data()
new_x = list(range(i, i+len(new_y)))
patched_figure = Patch()
patched_figure['data'][0]['x'].extend(new_x)
patched_figure['data'][0]['y'].extend(new_y)
return patched_figure
if __name__ == "__main__":
app.run(debug=True)