如何在html.Div中输出线程生成的随机值?Dash实现问询
解决方案
要将线程生成的随机值显示到指定的html.Div中,需要解决线程与Dash回调系统的通信问题。Dash的回调基于事件触发,无法直接从后台线程更新UI,因此可以通过以下步骤实现:
- 线程安全存储随机值:在
Nanotec线程类中添加线程安全的变量存储最新生成的随机值。 - 定期触发UI更新:使用
dcc.Interval组件定期触发回调,读取线程中的最新值并更新UI。 - 调整按钮回调逻辑:确保按钮仅控制线程状态,不直接修改UI,避免冲突。
修改后的完整代码
from dash import Dash, html, dcc, Input, Output, ctx, no_update import dash_bootstrap_components as dbc import time import threading import random app = Dash( __name__, external_stylesheets=[dbc.themes.SPACELAB, dbc.icons.FONT_AWESOME], title='MT', prevent_initial_callbacks="initial_duplicate" ) class Nanotec(threading.Thread): def __init__(self, *args, **kwargs): super(Nanotec, self).__init__(*args, **kwargs) self.__flag = threading.Event() # 线程暂停标志 self.__flag.set() # 初始为运行状态 self.__running = threading.Event() # 线程停止标志 self.__running.set() # 初始为启动状态 self.latest_value = 0.0 # 存储最新随机值 self.lock = threading.Lock() # 线程安全锁 def run(self): while self.__running.isSet(): self.__flag.wait() # 暂停时阻塞 print("acquisition is on") # 线程安全更新随机值 with self.lock: self.latest_value = random.random() time.sleep(1) def pause(self): self.__flag.clear() # 设置为暂停状态 print("pausing") def resume(self): self.__flag.set() # 恢复运行 print("resuming") def stop(self): self.__flag.set() # 先恢复线程(如果已暂停) self.__running.clear() # 停止线程 def get_latest_value(self): # 线程安全读取最新值 with self.lock: return self.latest_value def is_running(self): # 判断线程是否处于运行状态(未停止且未暂停) return self.__running.isSet() and self.__flag.isSet() @app.callback( Output('dummy', 'children'), Input('btn_start_nanotec', 'n_clicks'), Input('btn_pause_nanotec', 'n_clicks'), Input('btn_resume_nanotec', 'n_clicks') ) def handle_buttons(btn_start_nanotec, btn_pause_nanotec, btn_resume_nanotec): button_id = ctx.triggered_id if ctx.triggered_id else 'No clicks yet' if button_id == "btn_start_nanotec": # 避免重复启动线程 if not my_thread_nanotec.is_alive(): my_thread_nanotec.start() else: # 如果线程已存在,直接恢复运行 my_thread_nanotec.resume() elif button_id == "btn_pause_nanotec": my_thread_nanotec.pause() elif button_id == "btn_resume_nanotec": my_thread_nanotec.resume() return no_update @app.callback( Output('container', 'children'), Input('interval-component', 'n_intervals'), prevent_initial_call=False ) def update_container(n): if my_thread_nanotec.is_running(): value = my_thread_nanotec.get_latest_value() return f"当前随机值: {value:.6f}" else: return "采集未运行或已暂停" my_thread_nanotec = Nanotec() app.layout = html.Div([ html.Button('Start', id='btn_start_nanotec'), html.Button('Pause', id='btn_pause_nanotec'), html.Button('Resume', id='btn_resume_nanotec'), html.Div(id='container', style={'fontSize': 24, 'marginTop': 20}), # 定期触发UI更新的定时器 dcc.Interval( id='interval-component', interval=1000, # 每秒更新一次 n_intervals=0 ), # 用于接收按钮回调输出的隐藏组件 html.Div(id='dummy', style={'display': 'none'}), html.Br(), ]) if __name__ == "__main__": app.run_server(debug=True)
关键修改说明
线程安全的变量存储:
- 添加
self.latest_value存储最新随机值,并用threading.Lock确保多线程访问时的安全性。 - 新增
get_latest_value()和is_running()方法,供回调线程安全读取线程状态和数据。
- 添加
定时器触发UI更新:
- 使用
dcc.Interval组件每秒触发一次回调,读取线程中的最新随机值并更新container的内容。 - 根据线程状态显示不同的文本(运行时显示随机值,暂停/未启动时提示状态)。
- 使用
按钮回调优化:
- 将按钮回调的输出改为隐藏的
dummy组件,避免与定时器回调的输出冲突。 - 添加线程存活判断,防止重复启动线程导致的错误。
- 将按钮回调的输出改为隐藏的
内容的提问来源于stack exchange,提问作者2WFR
相关产品推荐
相关产品推荐

