Hi community,
Recently I came across the editable
property available in the config options of the figure
object or dcc.Graph
.
If you are not aware config
can be passed to the fig.show()
and dcc.Graph
function if using Dash. It allows us to control a bunch of cool chart configuration options.
Editable graphs in Plotly
editable
(boolean; optional): We can edit titles, move annotations, etc - sets all pieces ofedits
unless a separateedits
config item overrides individual parts.
As mentioned in the docs, this flag if set to True
allows you to edit titles (both chart and axes), annotations text, legend items text, and move legend, color bar, and annotations in the graph.
Below is the complete list of possible edits:
annotationPosition
annotationTail
annotationText
axisTitleText
colorbarPosition
colorbarTitleText
legendPosition
legendText
shapePosition
titleText
We can choose to only allow selected of the above properties to be editable by passing edits
in combination with the editable
flag to the config
dictionary.
Example
import plotly.express as px
df = px.data.tips()
fig = px.histogram(
df, x="sex", y="total_bill", color="smoker", barmode="group"
)
fig.add_annotation(
x="Female", y=1000, text="Text annotation", showarrow=True, arrowhead=1
)
fig.show(config=dict(editable=True))
The user edits does not persist, which means the graph will reset on refresh.
Persisting user edits with Dash
In Dash we can store the modified figure by using dcc.Store
to store the figure by listening to the relayoutData
property of the graph via a callback.
By setting the storage_type = "session"
of dcc.Store we can persist the data on page refresh.
Code:
from dash import Dash, html, dcc, callback, Input, State, Output
import plotly.express as px
import plotly.graph_objects as go
# function to create graph
def generate_graph():
df = px.data.tips()
fig = px.histogram(df, x="sex", y="total_bill", color="smoker", barmode="group")
fig.add_annotation(
x="Female", y=1000, text="Text annotation", showarrow=True, arrowhead=1
)
return fig
app = Dash(__name__)
app.layout = html.Div(
children=[
html.Br(),
dcc.Graph(
id="example-graph",
figure=generate_graph(),
config=dict(editable=True),
),
dcc.Store(id="fig-store", storage_type="session"),
]
)
# This callback renders the chart
@callback(
Output("example-graph", "figure"),
Input("fig-store", "data"),
)
def update(fig_data):
if fig_data:
fig = go.Figure(fig_data)
else:
fig = generate_graph()
return fig
# This callback stores the current state of the figure after each edits in dcc store
@callback(
Output("fig-store", "data"),
Input("example-graph", "relayoutData"),
State("example-graph", "figure"),
prevent_initial_call=True,
)
def update(relayoutData, fig):
return fig
if __name__ == "__main__":
app.run_server(debug=True)