You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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)
解决方案

问题根源

  1. 回调无初始化返回值:页面加载时n_clicks为None,函数未返回图表对象,导致图表组件接收null值触发报错。
  2. 数据更新后未同步:保存新体重后未重新读取Excel生成最新图表,必须手动刷新才能更新。
  3. 数据缺失日期:仅保存体重值,未对应写入日期,数据逻辑不完整。

修改后代码

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 22:35:40