dcc.Loading 仅在第一次加载时 (Python)

dcc.Loading on First Load Only (Python)

我正在做一个 Plotly Dash 项目,它有很多经过过滤、切片和切块的等值线图。这些是非常昂贵的计算,会减慢一切,所以我认为作为一个很好的接触,我可以在地图周围添加一个 dcc.loading 包装器,至少让用户知道它正在加载,而不是我的仪表板看起来滞后.

我遇到的问题是每次地图更改后都会出现加载图标。即使是不到 1 秒的快速更改,加载图标也会出现。我的挑战是我仍想使用 dcc.loading 包装器,但只在地图的初始加载时显示它。

我正在阅读 Plotly 社区网站上的这篇博客,它解决了同样的问题,但没有人能够提出解决方案:https://community.plotly.com/t/loading-states-and-loading-component/19650/35. Further, I was playing around with the "PreventUpdate" argument from the Dash help page here: https://dash.plotly.com/advanced-callbacks,但仍然找不到解决方案。

谁能帮我指明正确的方向?

这是一些示例代码:

import pandas as pd
import dash
from urllib.request import urlopen
import json
import plotly.express as px
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output 
#from dash.exceptions import PreventUpdate


with urlopen('https://raw.githubusercontent.com/plotly/datasets/master/geojson-counties-fips.json') as response:
    counties = json.load(response)

df = pd.read_csv("https://raw.githubusercontent.com/plotly/datasets/master/fips-unemp-16.csv",
                   dtype={"fips": str})

fips_choices = df['fips'].sort_values().unique()


app = dash.Dash(__name__)
server = app.server
app.layout = html.Div([
    dcc.Dropdown(id='dropdown1',
                 options=[{'label': i, 'value': i} for i in fips_choices],
                 value=fips_choices[0]
    ),
    dcc.Loading(
        id='loading',
        children=[
            dcc.Graph(id='us_map')
    ])
])



@app.callback(
    Output('us_map','figure'),
    Input('dropdown1','value'))

def update_map(county_select):
        new_df = df[df['fips']==county_select]
        fig = px.choropleth_mapbox(new_df, geojson=counties, locations='fips', color='unemp',
                           color_continuous_scale="Viridis",
                           range_color=(0, 12),
                           mapbox_style="carto-positron",
                           zoom=3, center = {"lat": 37.0902, "lon": -95.7129},
                           opacity=0.5
                          )
        fig.update_layout(margin={"r":0,"t":0,"l":0,"b":0})

        return fig

app.run_server(host='0.0.0.0',port='8051')

dcc.Loading

可能没有办法

这是一个对我有用的解决方案。

为您的地图设置 {"display":"none"}

样式
dcc.Graph(id="us_map", style={"display": "none"})

在回调中添加 us-map 样式作为输出和 return {"display":"inline"}

@app.callback(
    [Output("us_map", "figure"), Output("us_map", "style")], Input("dropdown1", "value")
)
def update_map(county_select):
    new_df = df[df["fips"] == county_select]
    fig = px.choropleth_mapbox(
        new_df,
        geojson=counties,
        locations="fips",
        color="unemp",
        color_continuous_scale="Viridis",
        range_color=(0, 12),
        mapbox_style="carto-positron",
        zoom=3,
        center={"lat": 37.0902, "lon": -95.7129},
        opacity=0.5,
    )
    fig.update_layout(margin={"r": 0, "t": 0, "l": 0, "b": 0})

    return fig, {"display": "inline"} 

如果你想要加载栏而不是空白区域,请将加载栏放在地图上方的 div 中,将加载栏作为输出添加到 div,添加一个return of {"display":"false"} 以便在地图加载时加载动画消失。你不能让它与 dcc.Loading 一起工作,所以我只是使用了一个自定义的 CSS 加载栏。

解决方案如 described in the thread you linked 和 redxman 的最后一段,但需要一些额外的调整。

使用父级 div 并将 dcc.Loading 组件和内容 div 作为同级。

html.Div([dcc.Graph(id='us_map'),
          dcc.Loading(id='loading')])
...

dcc.Loading 不仅会生成一个 div,还会为自己生成一个父 div。幸运的是,我们可以 通过在回调输出中使用 'parent_style' 属性 来访问它。

@app.callback(
    [Output('us_map','figure'),
    Output('loading', 'parent_style')],
    Input('dropdown1','value')
)
def update_map(county_select):
...
  return fig, {'display' : 'none'}

然后您就可以正确定位加载动画了。此实现的一个优点是能够保持 dcc.Loading 和地图交互。

import pandas as pd
import dash
from urllib.request import urlopen
import json
import plotly.express as px
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output

with urlopen('https://raw.githubusercontent.com/plotly/datasets/master/geojson-counties-fips.json') as response:
    counties = json.load(response)

df = pd.read_csv("https://raw.githubusercontent.com/plotly/datasets/master/fips-unemp-16.csv",
                   dtype={"fips": str})

fips_choices = df['fips'].sort_values().unique()

loading_style = {'position': 'absolute', 'align-self': 'center'}

app = dash.Dash(__name__)
server = app.server
app.layout = html.Div([
    dcc.Dropdown(id='dropdown1',
                 options=[{'label': i, 'value': i} for i in fips_choices],
                 value=fips_choices[0]
    ),
    html.Div([dcc.Graph(id='us_map', style={'flex-grow': '1'}),
              dcc.Loading(id='loading', parent_style=loading_style)
              ], style= {'position': 'relative', 'display': 'flex', 'justify-content': 'center'}
    )
])


@app.callback(
    [Output('us_map','figure'),
    Output('loading', 'parent_style')
     ],
    Input('dropdown1','value')
)
def update_map(county_select):
        new_loading_style = loading_style
        # Initial load only
        # new_loading_style['display'] = 'none'
        new_df = df[df['fips']==county_select]
        fig = px.choropleth_mapbox(new_df, geojson=counties, locations='fips', color='unemp',
                           color_continuous_scale="Viridis",
                           range_color=(0, 12),
                           mapbox_style="carto-positron",
                           zoom=3, center = {"lat": 37.0902, "lon": -95.7129},
                           opacity=0.5
                          )
        fig.update_layout(margin={"r":0,"t":0,"l":0,"b":0})

        return fig, new_loading_style

app.run_server(host='0.0.0.0',port='8051')