如何在执行耗时任务时临时禁用Submit按钮并完成后启用?
实现Dash按钮点击后临时禁用并执行耗时任务
当然可以实现你要的效果——点击按钮后立即禁用,执行耗时任务,完成后自动恢复启用状态。核心是要把耗时任务放到后台线程执行,避免阻塞Dash的主线程,同时通过状态存储跟踪任务进度,触发按钮状态更新。
下面是完整的实现代码:
import dash from dash import Dash, html, dcc, Input, Output, State, callback import dash_bootstrap_components as dbc import threading import time app = Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP]) app.layout = html.Div([ dbc.Button('Clickme', id='btn', disabled=False), # 用dcc.Store存储任务完成状态,避免全局变量的多用户冲突 dcc.Store(id='task-status', data={'completed': False}) ]) @callback( Output("btn", "disabled"), Output("task-status", "data"), Input("btn", "n_clicks"), State("btn", "disabled"), prevent_initial_call=True # 避免页面加载时触发回调 ) def start_task_and_disable_btn(n_clicks, disabled): # 仅在按钮未禁用时执行逻辑 if not disabled: # 定义后台执行的耗时任务 def background_task(): # 替换成你的实际计算逻辑,这里用sleep模拟耗时 time.sleep(5) # 任务完成后更新状态存储,并触发回调更新按钮 app.callback_context.outputs_list[1]['value']['completed'] = True dash.callback_context.triggered_id = 'task-status' app.run_callback(update_btn_status, [{'prop_id': 'task-status.data', 'value': {'completed': True}}]) # 启动后台线程 threading.Thread(target=background_task).start() # 立即返回禁用状态,同时初始化任务状态 return True, {'completed': False} # 如果按钮已禁用,不做任何更新 return dash.no_update @callback( Output("btn", "disabled", allow_duplicate=True), # 允许同一个Output被多个回调更新(Dash 2.7+支持) Input("task-status", "data"), prevent_initial_call=True ) def update_btn_status(task_status): # 任务完成后,恢复按钮启用状态 if task_status['completed']: return False return dash.no_update if __name__ == '__main__': app.run_server(debug=True)
关键说明:
- 后台线程处理耗时任务:直接在回调里执行耗时任务会阻塞Dash主线程,导致页面无响应。用
threading.Thread把任务放到后台,保证页面交互正常。 - 状态存储跟踪进度:用
dcc.Store存储任务完成状态,比全局变量更安全,适合多用户场景。 - 分阶段更新按钮状态:
- 点击按钮后,第一个回调立即返回
True禁用按钮,同时启动后台任务。 - 后台任务完成后,更新
dcc.Store的状态,触发第二个回调返回False,重新启用按钮。
- 点击按钮后,第一个回调立即返回
- 回调配置:
prevent_initial_call=True避免页面加载时误触发;allow_duplicate=True允许同一个按钮的disabled属性被两个回调更新(需Dash 2.7及以上版本)。
内容的提问来源于stack exchange,提问作者Kai-Chun Lin
相关产品推荐
相关产品推荐

