Hi @empet,
Sorry for a long time without an answer, but after some time I solved it myself. Anyway, the issue turned out to be animate
property of dcc.Grpah
object.
To duplicate it, please run below code snippet. Run it with the animate
option enabled and disabled (line #124). When it will be set to True, you will be able to see the issue.
Below you can find the code snippet with a data.csv file for storing the data.
data.csv:
timestamp,value
2020-06-24 13:49:58.930175,0.6804218634026458
Dash app:
import dash
from dash.dependencies import Input, Output, State, MATCH
import dash_html_components as html
import dash_core_components as dcc
import plotly.graph_objects as go
import random
from dash.exceptions import PreventUpdate
import pandas as pd
import datetime
import csv, json
app = dash.Dash(__name__)
app.layout = html.Div([
html.Div([
html.H3('Extend trace'),
html.Button('Add graph', id='add_graph'),
html.Div(id='graph_section'),
]),
dcc.Interval(
id='generator',
interval=1000,
n_intervals=0),
html.Div(
id='hidden_div',
hidden=True
)
])
@app.callback(
Output('hidden_div', 'children'),
[Input('generator', 'n_intervals')]
)
def gen_data(n_intervals):
with open('data.csv', 'a', newline='') as csv_file:
writer = csv.DictWriter(csv_file, ['timestamp', 'value'])
writer.writerow({'timestamp': str(datetime.datetime.now()), 'value': random.random()})
return 'anything'
@app.callback(
Output('graph_section', 'children'),
[Input('add_graph', 'n_clicks')]
)
def add_graph(click):
if click is None:
raise PreventUpdate
else:
df = read_csv()
data = [
go.Scatter(
x=df['timestamp'],
y=df['value'],
mode='lines',
name='some name',
line={'color': '#FF0000'},
text='some text'
)
]
data.append(
go.Scatter(
x=df['timestamp'],
y=df['value'] * (-1),
mode='lines',
name='some name',
line={'color': '#00FF00'},
text='some text'
)
)
children = []
for i in range(click):
div = html.Div(
[
dcc.Graph(
id={'index': i, 'type': 'graph'},
figure=dict(
data=data,
layout={
'yaxis': {
'title': {'text': 'some titile'},
'range': [-1,1],
'autorange': False
},
'xaxis': {
'title': {'text': 'Time (& date) [hh:mm:SS Month Day, Year]'},
'autorange': True,
'rangemode': 'normal',
'rangeselector': {
'buttons': [
dict(
count=10,
label=' 10 seconds ',
step='second',
stepmode='backward'
),
dict(
count=1,
label=' 1 minute ',
step='minute',
stepmode='backward'
),
dict(
count=10,
label=' 10 minutes ',
step='minute',
stepmode='backward'
)
]
},
'rangeslider': {
'visible': True,
'yaxis': {
'rangemode': 'auto'
}
},
'type':'date'
},
'showlegend': True
}
),
animate=True
),
dcc.Interval(
id={'index': i, 'type': 'interval'},
interval=1000,
n_intervals=0),
dcc.Input(id={'index': i, 'type': 'input'}, type='number',
placeholder='Set graph scale', value=1,
min=1, step=1, max=10)
]
)
children.append(div)
return children
def read_csv(from_date: datetime.datetime = None):
if from_date is None:
return pd.read_csv('data.csv')
else:
df = pd.read_csv('data.csv')
return df[df['timestamp'] > from_date]
@app.callback(
Output({'index': MATCH, 'type': 'graph'}, 'extendData'),
[Input({'index': MATCH, 'type': 'interval'}, 'n_intervals')],
[State({'index': MATCH, 'type': 'graph'}, 'figure')]
)
def update_extend_traces_traceselect(n_intervals, existing):
if n_intervals is None:
raise PreventUpdate
old_x = existing['data'][0]['x']
old_data = {'timestamp': old_x}
old_df = pd.DataFrame(old_data)
last_update_timestamp = old_df.iloc[-1]['timestamp']
new_df = read_csv(last_update_timestamp)
if new_df is None:
raise PreventUpdate
data = dict(
x = [new_df['timestamp'].to_list(), new_df['timestamp'].to_list()],
y = [new_df['value'].to_list(), (new_df['value'] * (-1)).to_list()]
)
return [data, [0, 1]]
@app.callback(
Output({'index': MATCH, 'type': 'graph'}, 'figure'),
[Input({'index': MATCH, 'type': 'input'}, 'value')],
[State({'index': MATCH, 'type': 'graph'}, 'figure')]
)
def change_y_max_range(value, figure):
if value is None:
raise PreventUpdate
else:
print(figure['layout'])
figure['layout']['yaxis']['range'] = [-value, value]
return figure
if __name__ == '__main__':
app.run_server(debug=True)
Please let me know if you can also duplicate this. Do you think it is a plotly bug, or I used this option incorrectly?
Thanks!