在 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)