I’m using Plotly offline, which means that I cannot access chart studios to edit plots on the fly, so I’ve decided to start making one that will work offline with Dash. I’m currently unable to figure out the correct callbacks in order to achieve what I want. Below will attempt to explain my thinking, and I will provide some source code.
- Make a Figure in a Python script
- Save the figure as a json file to some location
- [if the user wants to edit this plot later they will use this tool]
- Open the figure.json file or drag it into the Dash app
- Populate different drop-downs pertaining to that specific figure.
- Update the figure based on user selection from drop-downs and fields etc.
import plotly.graph_objects as go
import dash
import dash_core_components as dcc
import dash_html_components as html
import json
import base64
from dash.dependencies import Input, Output, State
def make_dropdown(idname,values,label,**kwargs):
default = kwargs.get('default','')
return html.Label([
label,
dcc.Dropdown(id=idname,
options=[{'label': i, 'value':i} for i in values],
value = default
)
])
def add_file_select(idname,ftype):
return dcc.Upload(
id=idname,
children=html.Div([
'Drag and Drop or ',
html.A(html.Button(f'Select {ftype} File'))
]),
style={
'width': '100%',
'height': '60px',
'lineHeight': '60px',
'borderWidth': '1px',
'borderStyle': 'dashed',
'borderRadius': '5px',
'textAlign': 'center'
})
app = dash.Dash(__name__)
app.layout = html.Div(children=[
add_file_select('uploaded-figure','Plotly Figure'),
make_dropdown('Lines',[],'Lines'),
make_dropdown('Colors',[],'Colors'),
dcc.Graph(id='edit-graph')
],
style={})
@app.callback(Output('edit-graph', 'figure'),
[
Input('uploaded-figure', 'contents'),
Input('uploaded-figure', 'filename'),
Input('Lines','value'),
Input('Colors','value'),
],
state=[State('edit-graph','figure')])
def update_edit_figure(contents, filename,name,colorvalue, fig):
ctx = dash.callback_context
if ctx.triggered:
ctxtid = ctx.triggered[0]['prop_id'].split('.')[0]
else:
ctxtid = ''
print(ctxtid)
if ctxtid == 'uploaded-figure':
try:
content_type, content_string = contents.split(',')
decoded = base64.b64decode(content_string).decode('utf-8')
fig = go.Figure(json.loads(decoded))
content = fig.to_dict()
except:
content = {}
return content
elif ctxtid == 'Colors':
if colorvalue:
if fig:
fig.update_traces(marker=dict(color=colorvalue,selector={'name':name}))
return fig
return {}
elif ctxtid == 'Line':
if fig:
return fig
return {}
else:
return {}
@app.callback([Output('Lines','options'),
Output('Lines','value')],
[Input('edit-graph','figure')])
def update_lines(fig):
if fig:
names = [{'label':i['name'],'value':i['name']} for i in fig['data']]
value = content['data'][0]['name']
else:
names = []
value = ''
return names,value
@app.callback([Output('Colors','options'),
Output('Colors','value')],
[Input('edit-graph','figure'),
Input('Lines','value')
])
def update_properties(fig,value):
d = {}
color = []
color_val = ''
if fig and 'data' in fig:
for line in fig['data']:
if line['name'] == value:
d = line
break
if d:
colors = ['red','blue','yellow','orange']
color = [{'label':color,'value':color} for color in colors]
return color,color_val
if __name__ == '__main__':
app.run_server(debug=True)
I keep getting circular dependencies no matter what I try to do. Any ideas on how to combat that?
Also, sorry i cannot seem to get the imports formatted correctly for the code.