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

Dash Plotly实时更新图表无法在浏览器自动刷新问题

问题

用Python的Plotly Dash开发实时加密货币追踪器,每分钟从API获取数据更新图表,但浏览器中图表无法自动刷新,必须手动操作(如平移图表)才会显示更新。控制台每分钟打印更新时间戳,确认数据获取和内部更新逻辑正常,但前端无自动更新。

原因分析

当前代码在回调中每次返回一个全新的dcc.Graph组件,Dash的组件渲染机制对重复创建同ID组件的更新支持不佳;另外animate=True参数会启用Plotly的动画过渡,反而可能阻碍全量数据更新的即时渲染。

解决方案
  1. 将dcc.Graph直接放在布局中,而非通过回调返回组件
  2. 回调直接更新dcc.Graph的figure属性,而非重新创建组件
  3. 移除animate=True参数,避免动画逻辑干扰数据更新
修改后的代码
import os
import datetime
import pandas as pd
import ccxt
import plotly.graph_objs as go
from dash import Dash, dcc, html
from dash.dependencies import Input, Output

symbol = 'BTC/USDT'
timeframe = '5m'

exchange = ccxt.binance({
    'rateLimit': 1200,
    'enableRateLimit': True,
})

def fetch_historical_data(symbol, timeframe, start_time, end_time):
    historical_data = exchange.fetch_ohlcv(symbol, timeframe, exchange.parse8601(start_time), exchange.parse8601(end_time))
    return historical_data

app = Dash(__name__)

# 布局直接包含dcc.Graph,不再通过回调返回
app.layout = html.Div(children=[
    html.H1(children=f'{symbol} {timeframe} Chart'),
    dcc.Interval(id='graph-update', interval=60 * 1000, n_intervals=0),
    html.Button('Refresh', id='refresh-button', n_clicks=0),
    # 直接定义Graph组件,ID固定
    dcc.Graph(id='live-graph')
])

# 回调输出改为Graph的figure属性
@app.callback(Output('live-graph', 'figure'),
              [Input('graph-update', 'n_intervals'),
               Input('refresh-button', 'n_clicks')])
def update_graph(interval, n_clicks):
    print("Update graph function called")
    current_time = datetime.datetime.now(datetime.timezone.utc)
    print(f"Current time: {current_time}")
    local_time = current_time + datetime.timedelta(hours=0)
    end_time = local_time.strftime("%Y-%m-%dT%H:%M:%SZ")
    start_time = (local_time - datetime.timedelta(hours=5)).strftime("%Y-%m-%dT%H:%M:%SZ")

    print(f"Fetching historical data from {start_time} to {end_time}")
    data = fetch_historical_data(symbol, timeframe, start_time, end_time)
    print(f"Fetched {len(data)} data points")

    timestamps = [kline[0] for kline in data]
    dates = [datetime.datetime.fromtimestamp(timestamp / 1000) for timestamp in timestamps]

    df = pd.DataFrame(data, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
    df['timestamp'] = dates
    df.set_index('timestamp', inplace=True)
    print(f"DataFrame created with {len(df)} rows")

    fig = go.Figure(data=[go.Candlestick(x=df.index, open=df['open'], high=df['high'], low=df['low'], close=df['close'])])
    fig.update_layout(title=f'{symbol} {timeframe} Chart (Last update: {local_time.strftime("%Y-%m-%d %H:%M")})', yaxis_title='Price')
    print("Figure created")

    # 直接返回figure对象,而非组件
    return fig

if __name__ == '__main__':
    app.run_server(debug=True)
额外说明
  • 这种方式符合Dash的更新逻辑:组件实例保持不变,仅更新其数据属性,前端会自动触发渲染
  • 若需要保留动画效果,可改用fig.update_layout(transition_duration=500)来设置平滑过渡,而非依赖animate=True

内容的提问来源于stack exchange,提问作者Fallyed

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 18:23:21