在 app.callback 期间使用 python plotly/dash 时访问自身
Accessing self when using python plotly/dash during app.callback
我正在使用 plotly/dash 和 python 创建实时更新图表。不幸的是,在 python 中传递变量 self
会引发错误。我在下面做了一个最小的例子来概述问题。
import dash
from dash.dependencies import Output, Input
import dash_core_components as dcc
import dash_html_components as html
import plotly
import random
import plotly.graph_objs as go
from collections import deque
import time
import numpy as np
class PlotlyDashboard:
_X = deque(maxlen = 20)
_X.append(1)
_Y = deque(maxlen = 20)
_Y.append(1)
_app = dash.Dash(__name__)
def __init__(self):
self._app.layout = html.Div(
[
dcc.Graph(id = 'live-graph', animate = True),
dcc.Interval(
id = 'graph-update',
interval = 1000,
n_intervals = 0
),
]
)
@_app.callback(
Output('live-graph', 'figure'),
[Input('graph-update', 'n_intervals') ]
)
def update_graph_scatter(self, n):
data = plotly.graph_objs.Scatter(
x=list(self._X),
y=list(self._Y),
name='Scatter',
mode= 'lines+markers'
)
return {'data': [data],
'layout' : go.Layout(xaxis=dict(range=[min(self._X),max(self._X)]),yaxis = dict(range = [min(self._Y),max(self._Y)]),)}
def update(self, y):
self._X.append(self._X[-1] + 1)
self._Y.append(y)
def start(self):
self._app.run_server(port=8050)
live_plotter = PlotlyDashboard()
live_plotter.start()
while True:
live_plotter.update(np.random.normal(0,1))
time.sleep(1)
图表每秒更新一次,使用通过 update
函数提供的新数据。 update
函数更新存储在 self
中的数据队列。在 @app.callback
期间,它每秒也会发生一次,它会在绘图之前咨询 self
以获取数据的当前状态,但是这样做时出现以下错误
output_value = func(*func_args, **func_kwargs) # %% callback invoked %%
TypeError: update_graph_scatter() missing 1 required positional argument: 'n'
如果我在 update_graph_scatter
方法中将 self
作为 arg 删除,错误就会消失,但是该方法的内部无法访问 self,这会导致不同的错误。任何可以提供的关于我如何访问 self 或更新方法和 @app.callback 标记方法都可以咨询的等效公共存储的帮助将不胜感激。
编辑:通过使用 rob 的解决方案和一些线程,我能够解决问题。请参阅下面关于如何在一个线程中实例化 class 的解决方案(必须完成,因为 app_server 正在阻塞)
然后更新 class 使用主线程绘制的数据。
import dash
from dash.dependencies import Output, Input
import dash_core_components as dcc
import dash_html_components as html
import plotly
import random
import plotly.graph_objs as go
from collections import deque
import time
import numpy as np
from threading import Thread
class PlotlyDashboard:
_X = deque(maxlen = 20)
_X.append(1)
_Y = deque(maxlen = 20)
_Y.append(1)
_app = dash.Dash(__name__)
def __init__(self):
self._app.layout = html.Div(
[
dcc.Graph(id = 'live-graph', animate = True),
dcc.Interval(
id = 'graph-update',
interval = 1000,
n_intervals = 0
),
]
)
if self._app is not None and hasattr(self, "callbacks"):
self.callbacks(self._app)
def callbacks(self, _app):
@_app.callback(
Output("live-graph", "figure"), [Input("graph-update", "n_intervals")]
)
def update_graph_scatter(n):
# let's update data here to show class callbacks are working
data = plotly.graph_objs.Scatter(
x=list(self._X), y=list(self._Y), name="Scatter", mode="lines+markers"
)
return {
"data": [data],
"layout": go.Layout(
xaxis=dict(range=[min(self._X), max(self._X)]),
yaxis=dict(range=[min(self._Y), max(self._Y)]),
),
}
def update(self, y):
self._X.append(self._X[-1] + 1)
self._Y.append(y)
def start(self):
self._app.run_server(port=8050)
live_plotter = PlotlyDashboard()
def starter():
live_plotter.start()
plot_thread = Thread(target=starter)
plot_thread.start()
while True:
live_plotter.update(np.random.normal(0,1))
time.sleep(1)
- 我已经回答了你问题的范围。如何将 回调 作为 class 方法
- 从根本上将它们定义为 class 方法中的静态方法。
- 您假设
run_server()
是非阻塞的,即 returns 在服务器启动后立即控制回到控制进程。它不是 - 因此我已将对 self.update()
的调用放入回调
import dash
from dash.dependencies import Output, Input
import dash_core_components as dcc
import dash_html_components as html
import plotly
import random
import plotly.graph_objs as go
from collections import deque
import time
class PlotlyDashboard:
_X = deque(maxlen=20)
_X.append(1)
_Y = deque(maxlen=20)
_Y.append(1)
_app = dash.Dash(__name__)
def __init__(self):
self._app.layout = html.Div(
[
dcc.Graph(id="live-graph", animate=True),
dcc.Interval(id="graph-update", interval=1000, n_intervals=0),
]
)
if self._app is not None and hasattr(self, "callbacks"):
self.callbacks(self._app)
def callbacks(self, _app):
@_app.callback(
Output("live-graph", "figure"), [Input("graph-update", "n_intervals")]
)
def update_graph_scatter(n):
# let's update data here to show class callbacks are working
self.update()
data = plotly.graph_objs.Scatter(
x=list(self._X), y=list(self._Y), name="Scatter", mode="lines+markers"
)
return {
"data": [data],
"layout": go.Layout(
xaxis=dict(range=[min(self._X), max(self._X)]),
yaxis=dict(range=[min(self._Y), max(self._Y)]),
),
}
def update(self):
self._X.append(self._X[-1] + 1)
self._Y.append(self._Y[-1] + self._Y[-1] * random.uniform(-0.1, 0.1))
def start(self):
print("starting")
self._app.run_server(port=8050)
# this will never be seen !!!!
print("started")
live_plotter = PlotlyDashboard()
live_plotter.start()
# this code will never run as run_server() is a blocking not a sub-process
while True:
live_plotter.update()
time.sleep(1)
我正在使用 plotly/dash 和 python 创建实时更新图表。不幸的是,在 python 中传递变量 self
会引发错误。我在下面做了一个最小的例子来概述问题。
import dash
from dash.dependencies import Output, Input
import dash_core_components as dcc
import dash_html_components as html
import plotly
import random
import plotly.graph_objs as go
from collections import deque
import time
import numpy as np
class PlotlyDashboard:
_X = deque(maxlen = 20)
_X.append(1)
_Y = deque(maxlen = 20)
_Y.append(1)
_app = dash.Dash(__name__)
def __init__(self):
self._app.layout = html.Div(
[
dcc.Graph(id = 'live-graph', animate = True),
dcc.Interval(
id = 'graph-update',
interval = 1000,
n_intervals = 0
),
]
)
@_app.callback(
Output('live-graph', 'figure'),
[Input('graph-update', 'n_intervals') ]
)
def update_graph_scatter(self, n):
data = plotly.graph_objs.Scatter(
x=list(self._X),
y=list(self._Y),
name='Scatter',
mode= 'lines+markers'
)
return {'data': [data],
'layout' : go.Layout(xaxis=dict(range=[min(self._X),max(self._X)]),yaxis = dict(range = [min(self._Y),max(self._Y)]),)}
def update(self, y):
self._X.append(self._X[-1] + 1)
self._Y.append(y)
def start(self):
self._app.run_server(port=8050)
live_plotter = PlotlyDashboard()
live_plotter.start()
while True:
live_plotter.update(np.random.normal(0,1))
time.sleep(1)
图表每秒更新一次,使用通过 update
函数提供的新数据。 update
函数更新存储在 self
中的数据队列。在 @app.callback
期间,它每秒也会发生一次,它会在绘图之前咨询 self
以获取数据的当前状态,但是这样做时出现以下错误
output_value = func(*func_args, **func_kwargs) # %% callback invoked %%
TypeError: update_graph_scatter() missing 1 required positional argument: 'n'
如果我在 update_graph_scatter
方法中将 self
作为 arg 删除,错误就会消失,但是该方法的内部无法访问 self,这会导致不同的错误。任何可以提供的关于我如何访问 self 或更新方法和 @app.callback 标记方法都可以咨询的等效公共存储的帮助将不胜感激。
编辑:通过使用 rob 的解决方案和一些线程,我能够解决问题。请参阅下面关于如何在一个线程中实例化 class 的解决方案(必须完成,因为 app_server 正在阻塞) 然后更新 class 使用主线程绘制的数据。
import dash
from dash.dependencies import Output, Input
import dash_core_components as dcc
import dash_html_components as html
import plotly
import random
import plotly.graph_objs as go
from collections import deque
import time
import numpy as np
from threading import Thread
class PlotlyDashboard:
_X = deque(maxlen = 20)
_X.append(1)
_Y = deque(maxlen = 20)
_Y.append(1)
_app = dash.Dash(__name__)
def __init__(self):
self._app.layout = html.Div(
[
dcc.Graph(id = 'live-graph', animate = True),
dcc.Interval(
id = 'graph-update',
interval = 1000,
n_intervals = 0
),
]
)
if self._app is not None and hasattr(self, "callbacks"):
self.callbacks(self._app)
def callbacks(self, _app):
@_app.callback(
Output("live-graph", "figure"), [Input("graph-update", "n_intervals")]
)
def update_graph_scatter(n):
# let's update data here to show class callbacks are working
data = plotly.graph_objs.Scatter(
x=list(self._X), y=list(self._Y), name="Scatter", mode="lines+markers"
)
return {
"data": [data],
"layout": go.Layout(
xaxis=dict(range=[min(self._X), max(self._X)]),
yaxis=dict(range=[min(self._Y), max(self._Y)]),
),
}
def update(self, y):
self._X.append(self._X[-1] + 1)
self._Y.append(y)
def start(self):
self._app.run_server(port=8050)
live_plotter = PlotlyDashboard()
def starter():
live_plotter.start()
plot_thread = Thread(target=starter)
plot_thread.start()
while True:
live_plotter.update(np.random.normal(0,1))
time.sleep(1)
- 我已经回答了你问题的范围。如何将 回调 作为 class 方法
- 从根本上将它们定义为 class 方法中的静态方法。
- 您假设
run_server()
是非阻塞的,即 returns 在服务器启动后立即控制回到控制进程。它不是 - 因此我已将对self.update()
的调用放入回调
import dash
from dash.dependencies import Output, Input
import dash_core_components as dcc
import dash_html_components as html
import plotly
import random
import plotly.graph_objs as go
from collections import deque
import time
class PlotlyDashboard:
_X = deque(maxlen=20)
_X.append(1)
_Y = deque(maxlen=20)
_Y.append(1)
_app = dash.Dash(__name__)
def __init__(self):
self._app.layout = html.Div(
[
dcc.Graph(id="live-graph", animate=True),
dcc.Interval(id="graph-update", interval=1000, n_intervals=0),
]
)
if self._app is not None and hasattr(self, "callbacks"):
self.callbacks(self._app)
def callbacks(self, _app):
@_app.callback(
Output("live-graph", "figure"), [Input("graph-update", "n_intervals")]
)
def update_graph_scatter(n):
# let's update data here to show class callbacks are working
self.update()
data = plotly.graph_objs.Scatter(
x=list(self._X), y=list(self._Y), name="Scatter", mode="lines+markers"
)
return {
"data": [data],
"layout": go.Layout(
xaxis=dict(range=[min(self._X), max(self._X)]),
yaxis=dict(range=[min(self._Y), max(self._Y)]),
),
}
def update(self):
self._X.append(self._X[-1] + 1)
self._Y.append(self._Y[-1] + self._Y[-1] * random.uniform(-0.1, 0.1))
def start(self):
print("starting")
self._app.run_server(port=8050)
# this will never be seen !!!!
print("started")
live_plotter = PlotlyDashboard()
live_plotter.start()
# this code will never run as run_server() is a blocking not a sub-process
while True:
live_plotter.update()
time.sleep(1)