Plotly Dash健身图表回调问题:如何同时实现数据存储与图表更新?
问题描述
我正在用Plotly Dash开发健身体重追踪图表,需求是用户输入体重数据后保存到Excel文件,同时更新图表。目前只能在app.callback中用单个函数单独实现一项功能,无法同时完成数据存储与图表更新。以下是示例数据、MVP代码及报错信息:

MVP代码
import openpyxl import dash from dash import html, dcc, Input, Output, State import pandas as pd import plotly.express as px import plotly.graph_objects as go app = dash.Dash(__name__) app.layout = html.Div([ dcc.Input(id='weight', placeholder='Enter Your Weight', type='text'), html.Button(id='submit-button', type='submit', children='Submit'), html.A(html.Button('Refresh'), href='/'), dcc.Graph( id='chart') ]) @app.callback(Output('chart', 'figure'), [Input('submit-button', 'n_clicks')], [State('weight', 'value')], ) def display_time_series(n_clicks, input_value): xlsx = pd.read_excel('Weight Tracker.xlsx') df = xlsx fig = px.line(df, x="DATE", y="ACTUAL WEIGHT") fig.add_trace( go.Scatter(x=df['DATE'], y=df['HIGH ESTIMATE'], name="HIGH ESTIMATE", line=dict(color="green", dash="dash")), secondary_y=False, ) fig.add_trace( go.Scatter(x=df['DATE'], y=df['LOW ESTIMATE'], name="LOW ESTIMATE", line=dict(color="red", dash="dash")), secondary_y=False, ) if n_clicks is not None: wb = openpyxl.load_workbook('Weight Tracker.xlsx') sheet = wb.active # Finds the last open row in Column B or the 'Actual Weight' Column last_empty_entry = max((b.row for b in sheet['B'] if b.value is not None)) + 1 c1 = sheet.cell(row=last_empty_entry, column=2) c1.value = int(input_value) wb.save("Weight Tracker.xlsx") print("Excel has been saved.") return fig if __name__ == '__main__': app.run_server(debug=True)
报错信息
Cannot read properties of null (reading 'data') at f.value (http://127.0.0.1:8050/_dash-component-suites/dash/dcc/async-graph.js:1:4493) at f.value (http://127.0.0.1:8050/_dash-component-suites/dash/dcc/async-graph.js:1:9778) at callComponentWillReceiveProps (http://127.0.0.1:8050/_dash-component-suites/dash/deps/react-dom@16.v2_3_1m1648990364.14.0.js:13111:16) at updateClassInstance (http://127.0.0.1:8050/_dash-component-suites/dash/deps/react-dom@16.v2_3_1m1648990364.14.0.js:13313:9) at updateClassComponent (http://127.0.0.1:8050/_dash-component-suites/dash/deps/react-dom@16.v2_3_1m1648990364.14.0.js:17242:22) at beginWork (http://127.0.0.1:8050/_dash-component-suites/dash/deps/react-dom@16.v2_3_1m1648990364.14.0.js:18755:18) at HTMLUnknownElement.callCallback (http://127.0.0.1:8050/_dash-component-suites/dash/deps/react-dom@16.v2_3_1m1648990364.14.0.js:182:16) at Object.invokeGuardedCallbackDev (http://127.0.0.1:8050/_dash-component-suites/dash/deps/react-dom@16.v2_3_1m1648990364.14.0.js:231:18) at invokeGuardedCallback (http://127.0.0.1:8050/_dash-component-suites/dash/deps/react-dom@16.v2_3_1m1648990364.14.0.js:286:33) at beginWork$1 (http://127.0.0.1:8050/_dash-component-suites/dash/deps/react-dom@16.v2_3_1m1648990364.14.0.js:23338:9)
解决方案
问题根源
- 回调无初始化返回值:页面加载时
n_clicks为None,函数未返回图表对象,导致图表组件接收null值触发报错。 - 数据更新后未同步:保存新体重后未重新读取Excel生成最新图表,必须手动刷新才能更新。
- 数据缺失日期:仅保存体重值,未对应写入日期,数据逻辑不完整。
修改后代码
import openpyxl from datetime import datetime import dash from dash import html, dcc, Input, Output, State import pandas as pd import plotly.express as px import plotly.graph_objects as go app = dash.Dash(__name__) app.layout = html.Div([ dcc.Input(id='weight', placeholder='输入你的体重', type='number'), # 改为数字类型避免转换错误 html.Button(id='submit-button', type='submit', children='提交'), dcc.Graph(id='chart') # 移除手动刷新按钮,改为提交后自动更新图表 ]) @app.callback(Output('chart', 'figure'), [Input('submit-button', 'n_clicks')], [State('weight', 'value')], prevent_initial_call=False # 允许初始化时触发回调生成图表 ) def update_chart_and_save_data(n_clicks, input_value): # 读取Excel基础数据 df = pd.read_excel('Weight Tracker.xlsx') # 处理提交逻辑 if n_clicks is not None and input_value is not None: wb = openpyxl.load_workbook('Weight Tracker.xlsx') sheet = wb.active # 获取最后一行,写入日期和体重 last_row = sheet.max_row sheet.cell(row=last_row + 1, column=1).value = datetime.now().strftime('%Y-%m-%d') sheet.cell(row=last_row + 1, column=2).value = input_value wb.save("Weight Tracker.xlsx") # 重新读取最新数据 df = pd.read_excel('Weight Tracker.xlsx') # 生成图表(始终返回图表对象) fig = px.line(df, x="DATE", y="ACTUAL WEIGHT", title="体重追踪曲线") fig.add_trace( go.Scatter(x=df['DATE'], y=df['HIGH ESTIMATE'], name="预估上限", line=dict(color="green", dash="dash")), secondary_y=False, ) fig.add_trace( go.Scatter(x=df['DATE'], y=df['LOW ESTIMATE'], name="预估下限", line=dict(color="red", dash="dash")), secondary_y=False, ) return fig if __name__ == '__main__': app.run_server(debug=True)
关键修改点
- 强制返回图表:无论是否点击提交,函数最后都返回图表对象,解决初始化报错问题。
- 提交后刷新数据:保存新体重后重新读取Excel,确保图表使用最新数据。
- 自动写入日期:提交时自动添加当前日期,保证数据完整性。
- 优化输入类型:将输入框改为
number类型,避免手动转换整数时的错误。 - 移除手动刷新:提交后自动更新图表,无需手动刷新页面。
内容的提问来源于stack exchange,提问作者Brandon Jacobson
相关产品推荐
相关产品推荐

