如何在回调中更新进度条?

问题描述 投票:0回答:1

我需要创建一个热图,在绘制热图之前,我需要从数据库下载大量数据,这需要大约5分钟的时间,我想在从oracle数据库下载数据时显示一个进度条,让我知道是否可以正在从 oracle 下载数据。

我用谷歌搜索了很多,幸运的是我找到了一个网站,它使用 dbc.Progress() 以及如何通过使用文件连接到 tqdm 来更新进度栏。但我仍然不确定如何以我自己的例子来做。我试过了,但没用,有人能帮我吗?非常感谢您的帮助。

https://towardsdatascience.com/long-callbacks-in-dash-web-apps-72fd8de25937

这是我的代码

我定义了一个选项卡,我使用 dbc.Progress() 和图表包含进度条

    progress_bar_heatmap=dbc.Progress(value=25, striped=True, animated=True,
                                          children=['25%'],color='success',
                                          style={'height':'20px'},
                                          id="progress_bar_heatmap")
    loading_timer_progress = dcc.Interval(id='loading_timer_progress',
                                  interval=1000)
    
    heatmap_graph = dcc.Graph(id="heatmap-graph", **graph_kwargs)     
    
    #wrap contour in dcc.loading's chilren so we can see loading signal
    heatmap_loading=dcc.Loading( 
                                    id='loading-heatmap',
                                    type='default',
                                    children=heatmap_graph     # wrap contour in loading's children 
                                    )
                                    
    dcc.Tab(
        [progress_bar_heatmap,loading_timer_progress, heatmap_loading],  
        label=label,
        value='heatmap',
        id='heatmap-tab',
        className="single-tab",
        selected_className="single-tab--selected",
        )

在回调中,我从上面的网站复制了一些代码,


@app.callback(
    [
        Output("heatmap-graph", "figure"),       
        Output("progress_bar_dts_heatmap", "value"),
        
        
    ],
    [
        Input("plot-dts", "n_clicks"),
        Input('loading_timer_progress', 'n_intervals'),
    ],

    prevent_initial_call=True,   # disable output in the first load
)

def change_plot(n_clicks,n_intervals):

    progress_bar_value=0

    import sys
    
    try:
        with open('progress.txt', 'r') as file:
            str_raw = file.read()
        last_line = list(filter(None, str_raw.split('\n')))[-1]
        percent = float(last_line.split('%')[0])
        
    except:   # no progress file created meansing it is creating
        
        percent = 0
        std_err_backup = sys.stderr
        file_prog = open('progress.txt', 'w')
        sys.stderr = file_prog
        
        df=time_consuming_function()

        result_str = f'Long callback triggered by {btn_name}. Result: {x:.2f}'
        file_prog.close()
        sys.stderr = std_err_backup

    finally:   # must do under all circustances
        
        text = f'{percent:.0f}%'
        
        
        fig=create_fig(df)

耗时函数内部


def time_consuming_function():

   download_data_from_oracle()
   # after that, I added below as website did
    for i in tqdm(range(20)):
        time.sleep(0.5)
  return df


上面不起作用,不知道哪个是错误的?

plotly-dash
1个回答
0
投票

我这几天一直在努力解决这个问题。老实说,很难相信没有更好的文档来说明如何将后台回调与进度条链接起来,因为这似乎是一件很常见的事情。

无论如何,我通过结合以下两个链接中的示例找到了如何正确执行此操作:

这是一个简单的虚拟应用程序的概念证明,其中附加了一个耗时的函数的后台回调,该函数用当前状态更新进度条:

import time
from uuid import uuid4
import diskcache
from dash import Dash, html, DiskcacheManager, CeleryManager, Input, Output, callback
import dash_bootstrap_components as dbc

launch_uid = uuid4()
cache = diskcache.Cache("./cache")


background_callback_manager = DiskcacheManager(
    cache, cache_by=[lambda: launch_uid], expire=60
)

app = Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP],
           background_callback_manager=background_callback_manager)

# Simple layout with Start and Cancel buttons as well as a progress bar
app.layout = html.Div(
    [
        html.Div(
            [
                dbc.Row(children=[
                    dbc.Col(),
                    dbc.Col(
                        children=[
                            html.P(id="paragraph_id", children=["Button not clicked"]),
                            # Progress component sets up the loading bar
                            dbc.Progress(id="progress-component", striped=True, value=5)
                        ]
                    ),
                    dbc.Col()
                ]
                ),
                dbc.Row(
                    children=[
                        dbc.Col(),
                        dbc.Col(
                            children=[
                                # Button kicks off the background callback
                                html.Button(id="button_id", children="Run Job!"),
                                # Button cancels the background callback
                                html.Button(id="cancel_button_id", children="Cancel Running Job!"),
                            ]
                        ),
                        dbc.Col(),
                    ]
                )
            ]
        ),
    ]
)


def calculate_percent_progress(iteration, total):
    percent = int((iteration / total) * 100)
    f_percent = f"{int(percent)} %"
    return percent, f_percent


def slow_process(set_progress):
    """
    This would be the slow function, need to set the progress on each iteration to update the progress
    bar.

    The 'set_progress' is a special argument with a function handle to update the app on current progress
    see: https://dash.plotly.com/background-callbacks

    :param set_progress:
    :return:
    """
    total = 10
    for i in range(total):
        time.sleep(0.5)
        percent, f_percent = calculate_percent_progress(iteration=i+1, total=total)
        set_progress([int(percent), f"{int(percent)} %"])


@callback(
    Output("paragraph_id", "children"),
    Input("button_id", "n_clicks"),
    running=[
        (Output("button_id", "disabled"), True, False),
        (Output("cancel_button_id", "disabled"), False, True),
        (
            Output("paragraph_id", "style"),
            {"visibility": "hidden"},
            {"visibility": "visible"},
        )
    ],
    cancel=[Input("cancel_button_id", "n_clicks")],
    progress=[
        Output("progress-component", "value"),
        Output("progress-component", "label")
    ],
    interval=1000,
    background=True
)
def callback(set_progress, n_clicks):

    slow_process(set_progress)

    return [f"Clicked {n_clicks} times"]


if __name__ == "__main__":
    app.run(debug=True)

© www.soinside.com 2019 - 2024. All rights reserved.