@chriddyp Here you go:
Notably, in constructing this I found that the problem only exists when Graph.animate = True, which would be consistent with the idea of diff’ing behavior being the cause.
import json
from textwrap import dedent as d
from copy import copy
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
import plotly.graph_objs as go
app = dash.Dash(__name__)
app.css.append_css({'external_url': 'https://codepen.io/chriddyp/pen/bWLwgP.css'})
styles = {
'pre': {
'border': 'thin lightgrey solid',
'overflowX': 'scroll'
}
}
black_square = dict(
xref = 'x',
yref = 'y',
xsizemode = 'pixel',
ysizemode = 'pixel',
type = 'path',
layer = 'below',
line = {'width':'5'},
fillcolor = 'rgb(0,0,0)',
path = 'M 0 0 L 20 0 L 20 20 L 0 20 Z',
xanchor = 0,
yanchor = 5,
)
down_arrow = dict(
x= 0,
y= 6,
xref='x',
yref='y',
showarrow=True,
arrowhead=1,
arrowcolor='rgb(200,0,100)',
ax=0,
ay= 6.5,
axref='x',
ayref='y'
)
initial_shapes = []
for x_pos in range(10):
black_square['xanchor'] = x_pos
initial_shapes.append(copy(black_square))
initial_annotations = []
for x_pos in range(10):
down_arrow['x'] = x_pos
down_arrow['ax'] = x_pos
initial_annotations.append(copy(down_arrow))
app.layout = html.Div([
html.Button('reduce and shift', id='do-it'),
dcc.Graph(
animate = True,
id='shapes-and-annotations',
figure={
'data': [{
'x': [0, 20],
'y': [0, 6],
'name': 'Trace 1',
'mode': 'markers',
'marker': {'size': 10}
}],
'layout': go.Layout(
title = 'Figure title bar',
titlefont = dict(size=16),
showlegend = False,
hovermode = 'closest',
margin = dict(b=0,l=0,r=0,t=0),
annotations = initial_annotations,
shapes = initial_shapes,
autosize = True,
dragmode = 'pan',
xaxis = dict(showgrid=False, zeroline=True, showticklabels=True),
yaxis = dict(showgrid=False, zeroline=True, showticklabels=True)
),
}
),
html.Div(className='row', children=[
html.Div([
dcc.Markdown(d("""
**Count**
Current number of shapes according to the application.
""")),
html.P(id='output-one')
], className='three columns'),
html.Div([
dcc.Markdown(d("""
**Shapes data**
contents of Graph['figure']['layout']['shapes'] as shown to the application.
""")),
html.Pre(id='output-two', style=styles['pre']),
], className='nine columns'),
])
])
@app.callback(
Output('shapes-and-annotations', 'figure'),
[Input('do-it', 'n_clicks')],
[State('shapes-and-annotations', 'figure')])
def reduce_and_shift_down(clicks, figure):
del figure['layout']['shapes'][-1:] # remove the last shape on the list
for shape in figure['layout']['shapes']: # move all the remaining shapes down one.
shape['yanchor'] -= 1
del figure['layout']['annotations'][-1:] # remove the last annotation on the list
for annotation in figure['layout']['annotations']: # move all the remaining annotations down one.
annotation['y'] -= 1
annotation['ay'] -= 1
return figure
@app.callback(
Output('output-one', 'children'),
[Input('shapes-and-annotations', 'figure')])
def display_shapes_count(figure):
return 'len(figure[\'layout\'][\'shapes\']) : ' + str(len(figure['layout']['shapes']))
@app.callback(
Output('output-two', 'children'),
[Input('shapes-and-annotations', 'figure')])
def display_shapes_content(figure):
return json.dumps(figure['layout']['shapes'], indent=2)
if __name__ == '__main__':
app.run_server(debug=True, port=8051)
It’s very important to my use case that I can keep animations working, so the active question still is:
Is there some other way to format the list so this behavior doesn’t happen?