我需要创建一个热图,在绘制热图之前,我需要从数据库下载大量数据,这需要大约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
上面不起作用,不知道哪个是错误的?
我这几天一直在努力解决这个问题。老实说,很难相信没有更好的文档来说明如何将后台回调与进度条链接起来,因为这似乎是一件很常见的事情。
无论如何,我通过结合以下两个链接中的示例找到了如何正确执行此操作:
这是一个简单的虚拟应用程序的概念证明,其中附加了一个耗时的函数的后台回调,该函数用当前状态更新进度条:
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)