使用Dash实时更新图形(对应于ver0.37)


当我尝试Live Update时,我迷迷糊糊,因此决定将其作为备忘录的备忘录。

本文摘要

  • 当我尝试使用Dash定期更新图表时(出现问题),出现错误
  • 原因:缺少"事件"
  • 对应:将时间间隔组件设置为输入

什么是Dash

Python Web应用程序框架。使用Plotly轻松可视化。
详情请见官方。

背景

我通过参考有关制作Web应用程序的文章来尝试对图表进行实时更新,该文章可以通过此Plotly Dash轻松查看。
我遇到以下错误,有点麻烦。 ..
ImportError: cannot import name 'Event'

所引用文章的相关部分是以下修饰符。

1
2
3
@app.callback(Output('live-update-text', 'children'),
              events=[Event('interval-component', 'interval')])
def update_graph_live():

原因

根据Google老师的说法,"事件"似乎已从Dash的0.37版中消失。
还有其他人遇到麻烦了。
如何修复ImportError:无法从Dashly(python)导入Dash中的名称"事件"?
我应该怎么办?当我调查时,该方法是正式编写的。

信件

相应的方法与官方的实时更新组件相同,并且将事件重写为Input。
具体来说,我对上述相关部分进行了如下编辑。

1
2
3
@app.callback(Output('live-update-text', 'children'),
              [Input('interval-component', 'n_intervals')])
def update_graph_live(n):

如果您修改参考文章中的"系统监视器"以使其与ver0.37一起使用,它将如下所示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
import jupyterlab_dash #今回はJupyterlabを使用
import dash
import dash_html_components as html
#from dash.dependencies import Input, Output, Event
from dash.dependencies import Input, Output #Eventはもういない。。
import dash_core_components as dcc
import datetime
import plotly
import numpy as np
import pandas as pd
import psutil

viewer = jupyterlab_dash.AppViewer()

colors = {
    'background': '#111111',
    'text': '#7FDBFF'
}

app = dash.Dash(__name__)
app.layout = html.Div(children=[
    html.Div([
        html.H4('システムモニタ'),
        html.Div(id='live-update-text'),
        dcc.Graph(id='live-update-graph'),
        html.H4('プロセスリスト'),
        html.Div(id='live-update-proc'),
        dcc.Interval(
            id='interval-component',
            interval=1*1000, # in milliseconds
            n_intervals=0 #公式に沿って追加
        )
    ])
],
    style={'backgroundColor': colors['background'], 'color': colors['text']}
)


class Context:
    def __init__(self):
        self.t = []
        self.cpu = []
        self.per_cpu = [[] for x in range(psutil.cpu_count())]
        self.mem = []

    @classmethod
    def append_data(cls, d1, d2):
        n = len(d1)
        if n > 100:
            del d1[0:n - 99]
        d1.append(d2)


context = Context()

# The `dcc.Interval` component emits an event called "interval"
# every `interval` number of milliseconds.
# Subscribe to this event with the `events` argument of `app.callback`


@app.callback(Output('live-update-text', 'children'),
              #events=[Event('interval-component', 'interval')])
              [Input('interval-component', 'n_intervals')])
#def update_metrics():
def update_metrics(n):
    now = datetime.datetime.now()
    hour, minute, second = now.hour, now.minute, now.second
    style = {'padding': '5px', 'fontSize': '16px'}
    return [
        html.Span('CPU: {}%'.format(context.cpu[-1]), style=style),
        html.Span('Memory: {}%'.format(context.mem[-1]), style=style)
    ]


# Multiple components can update everytime interval gets fired.
@app.callback(Output('live-update-graph', 'figure'),
              #events=[Event('interval-component', 'interval')])
              [Input('interval-component', 'n_intervals')])
#def update_graph_live():
def update_graph_live(n):
    # global context
    context.append_data(context.t, datetime.datetime.now())
    context.append_data(context.cpu, psutil.cpu_percent())
    for data, pct in zip(context.per_cpu, psutil.cpu_percent(percpu=True)):
        context.append_data(data, pct)
    context.append_data(context.mem, psutil.virtual_memory().percent)

    # Create the graph with subplots
    fig = plotly.tools.make_subplots(rows=2, cols=1, vertical_spacing=0.2)
    fig['layout']['margin'] = {
        'l': 30, 'r': 10, 'b': 30, 't': 10
    }
    fig['layout']['plot_bgcolor'] = colors['background']
    fig['layout']['paper_bgcolor'] = colors['background']
    fig['layout']['font'] = {'color': colors['text']}
    fig['layout']['legend'] = {'x': 0, 'y': 1, 'xanchor': 'left'}
    fig['layout']['yaxis1'].update(range=[0, 100])
    fig['layout']['yaxis2'].update(range=[0, 100])

    fig.append_trace({
        'x': context.t,
        'y': context.cpu,
        'name': 'cpu',
        'mode': 'lines',
        'type': 'scatter',
    }, 1, 1)
    for i, y in enumerate(context.per_cpu):
        fig.append_trace({
            'x': context.t,
            'y': y,
            'name': 'cpu {}'.format(i),
            'mode': 'lines',
            'type': 'scatter',
        }, 1, 1)
    fig.append_trace({
        'x': context.t,
        'y': context.mem,
        'name': 'memory',
        'mode': 'lines',
        'type': 'scatter',
        'fill': 'tonexty',
    }, 2, 1)

    return fig


def get_proc_df():
    def get_proc(proc):
        try:
            pinfo = proc
        except psutil.NoSuchProcess:
            pass
        return (pinfo.pid, pinfo.name(), pinfo.memory_percent(), pinfo.cpu_percent())

    data = [get_proc(proc) for proc in psutil.process_iter()]
    df = pd.DataFrame(data, columns=['pid', 'name', 'memory', 'cpu'])
    df['memory'] = df['memory'].map(lambda x: '{:.2f}%'.format(x))
    df['cpu'] = df['cpu'] / psutil.cpu_count()
    df['cpu'] = df['cpu'].map(lambda x: '{:.2f}%'.format(x))
    return df.sort_values('cpu', ascending=False)


@app.callback(Output('live-update-proc', 'children'),
              #events=[Event('interval-component', 'interval')])
              [Input('interval-component', 'n_intervals')])
#def generate_table():
def generate_table(n):
    df = get_proc_df()
    max_rows = 10
    return html.Table(
        # Header
        [html.Tr([html.Th(col) for col in df.columns])] +

        # Body
        [html.Tr([
            html.Td(df.iloc[i][col], style={'width': '8em'}) for col in df.columns
        ]) for i in range(min(len(df), max_rows))]
    )

#if __name__ == '__main__':
#    app.run_server(debug=True)

viewer.show(app)