我正在使用Dash制作一个web应用的原型,Dash通过串行外围设备定期执行一些测量。至于现在,我正在使用dcc.Interval
组件定期从我的传感器中获取测量值,然后绘制并存储它们
但是,无论是使用Firefox还是Chrome,当选项卡不在前台时,web浏览器的性能调节机制都会大大降低dcc.Interval组件的触发频率。在某些情况下,后台计时器甚至完全停止
我能够创建以下最低限度的工作示例,它仅在控制台输出中起作用:
import dash
from dash.dependencies import Input, Output
import dash_html_components as html
import dash_core_components as dcc
app = dash.Dash(__name__)
app.layout = html.Div([
html.Div("A div", id="my_div"),
dcc.Interval(id='my_interval', disabled=False, n_intervals=0),
])
@app.callback(Output('my_div', 'children'), [Input('my_interval', 'n_intervals')])
def update(n_intervals):
print(n_intervals)
return dash.no_update
if __name__ == '__main__':
app.run_server(debug=True, host='0.0.0.0', port=5000)
当选项卡处于活动状态时,这种方法可以很好地工作,每秒一个接一个地计数(两次触发dcc.Interval
组件之间的默认延迟),但当web浏览器切换到其他选项卡时,这种方法不再起作用。特别是,一段时间后,两次点火之间的间隔增加
对于这个非常简单的示例,根据您的浏览器和机器,情况并不总是如此。但对于一个更复杂的应用程序,在后台进行两次回调之间的时间可以达到几秒,甚至几十秒
你知道一种解决方法,还是迫使浏览器把应用程序看作“<>强><强> >的方式,因此不节制它?主要目标是,在任何时候,应用程序中嵌入的dcc.Interval
组件都会以相同的速度继续启动
到目前为止,我测试了三种解决方案,但收效甚微:
dash_devices
dcc.Interval
放在层次结构的更高位置dash_devices
我偶然发现了this thread和^{
播放音频(如果您不想听,可以低音量播放或禁用制表符声音播放)是一个很好的解决方案to keep the tab in the foreground from the browser point of view
但是,如果我能够使用以下代码段:
html.Audio(autoPlay=True, src='http://www.hochmuth.com/mp3/Haydn_Cello_Concerto_D-1.mp3', loop=True)
使用路径而不是URL的相同代码,即src='/path/to/my_audio_file.mp3'
或src='file:///path/to/my_audio_file.mp3'
似乎不起作用(没有播放音频),我不知道为什么
根据我读到的here和there,我还尝试了一些base64
元素,但只有第一个元素起作用:
import dash
from dash.dependencies import Input, Output
import dash_html_components as html
import dash_core_components as dcc
import base64
haydn_path = "/Users/XXX/Haydn.mp3"
encoded_haydn = base64.b64encode(open(haydn_path, 'rb').read())
app = dash.Dash(__name__)
app.layout = html.Div([
html.Div("A div", id="my_div"),
dcc.Interval(id='my_interval', disabled=False, n_intervals=0),
html.Audio(autoPlay=True, src='http://www.hochmuth.com/mp3/Haydn_Cello_Concerto_D-1.mp3', loop=True),
html.Audio(autoPlay=True, src=haydn_path, loop=True),
html.Audio(autoPlay=True, src='file://' + haydn_path, loop=True),
html.Audio(autoPlay=True, src='data:audio/mp3;base64,{}'.format(encoded_haydn), loop=True),
])
@app.callback(Output('my_div', 'children'), [Input('my_interval', 'n_intervals')])
def update(n_intervals):
print(n_intervals)
return dash.no_update
if __name__ == '__main__':
app.run_server(debug=True)
在显示的四个音频播放器中,只有第一个能够播放某些内容(另一个呈灰色,好像没有指定音频文件)
注意事项:
dcc.Interval
放在层次结构的更高位置如here所述,在某些情况下,将dcc.Interval
组件定位在层次结构的更高位置似乎是可行的。然而,我尝试了它却没有成功
因此,我非常感谢任何能在这方面帮助我的人。通过以下方式之一:
dcc.Interval
元素的方法html.Audio
组件播放本地存储的音频文件的方法最初于2021年4月9日询问
根据emher's answer,我重新定向到一个具有两个不同线程的独立体系结构:
while True / time.sleep()
例程)使用纯Python编写的串行外围设备池,并存储结果它在一个全局变量中的池李>dcc.Interval
)读取上述全局变量李>下面是一个简单的工作示例:
import dash
from dash.dependencies import Input, Output
import dash_html_components as html
import dash_core_components as dcc
import threading
import time
counter = 0
app = dash.Dash(__name__)
class DashThread(threading.Thread):
def __init__(self, name):
threading.Thread.__init__(self)
self.name = name
def run(self):
global counter
global app
app.layout = html.Div([
dcc.Interval(id='my_interval', disabled=False, n_intervals=0),
html.Div("Counter :", style={"display": "inline-block"}),
html.Div(children=None, id="cnt_val", style={"display": "inline-block", "margin-left": "15px"}),
])
@app.callback(Output('cnt_val', 'children'), [Input('my_interval', 'n_intervals')])
def update(_):
return counter
app.run_server(dev_tools_silence_routes_logging=True) # , debug=True)
class CountingThread(threading.Thread):
def __init__(self, name):
threading.Thread.__init__(self)
self.name = name
def run(self):
global counter
while True:
counter += 1
print(counter)
time.sleep(1)
a = DashThread("The Dash Application")
b = CountingThread("An Independent Thread")
b.start()
a.start()
a.join()
b.join()
请注意第行末尾注释掉的debug=True
参数:
app.run_server(dev_tools_silence_routes_logging=True) # , debug=True)
这是因为Dash调用Flask的方式不允许在Dash应用程序从非主线程启动时启用调试模式。完整的问题记录在案here
无论Dash应用程序是在浏览器中加载还是在前台加载,此脚本每秒只计算一次。脚本仍在运行时,计数器也会运行,在选项卡中打开Dash应用程序或将其置于前台只会更新计数器的显示
免责声明:与最初提出问题时的dcc.Interval
方法相反,上面的代码将逐步取消同步。实际上,使用dcc.Interval
,关联的callback
每1.0秒调用一次,而不管之前调用的callback
是否已完成运行
因此,如果我们在t=0s运行程序,并假设选项卡位于前台,我们将看到以下调用:
t=0s : callback()
t=1s : callback()
t=2s : callback()
t=3s : callback()
[...]
相反,使用多线程方法,如果要运行的代码(上面:counter += 1 ; print(counter)
)需要执行时间dt,我们将看到以下调用:
t=0s : callback()
t=1s + dt : callback()
t=2s + 2*dt : callback()
t=3s + 3*dt : callback()
[...]
也就是说,执行链正在逐步从预期的“每秒一次回调”行为中取消同步。在某些情况下,这可能很棘手,在这种情况下,请参阅here了解解决方法
对于这种用例,我通常更喜欢一种架构,其中一个单独的进程收集数据并插入缓存(例如Redis),UI(在您的例子中是Dash)从中读取数据。此解决方案比直接从UI获取数据要健壮得多
如果您坚持直接从UI获取数据,我建议使用^{} 组件而不是
Interval
组件。虽然我还没有进行广泛的测试,但我的印象是连接将在most platforms上保持活动状态相关问题 更多 >
编程相关推荐