Hello all,
Is it possible to use Partial Property Updates with progress updates in Background Callbacks? Consider this (modified) example from the background callbacks documentation at https://dash.plotly.com/background-callbacks:
import time
from dash import Dash, DiskcacheManager, Input, Output, html, dcc, callback
import plotly.graph_objects as go
# Diskcache for non-production apps when developing locally
import diskcache
cache = diskcache.Cache("./cache")
background_callback_manager = DiskcacheManager(cache)
def make_progress_graph(progress, total, figure=None):
if figure:
figure['data'][0]['x'] = [progress]
else:
figure = (
go.Figure(data=[go.Bar(x=[progress])])
.update_xaxes(range=[0, total])
.update_yaxes(
showticklabels=False,
)
.update_layout(height=100, margin=dict(t=20, b=40))
)
return figure
app = Dash(__name__, background_callback_manager=background_callback_manager)
app.layout = html.Div(
[
html.Div(
[
html.P(id="paragraph_id", children=["Button not clicked"]),
dcc.Graph(id="progress_bar_graph", figure=make_progress_graph(0, 10)),
]
),
html.Button(id="button_id", children="Run Job!"),
html.Button(id="cancel_button_id", children="Cancel Running Job!"),
]
)
@callback(
output=Output("paragraph_id", "children"),
inputs=Input("button_id", "n_clicks"),
background=True,
running=[
(Output("button_id", "disabled"), True, False),
(Output("cancel_button_id", "disabled"), False, True),
(
Output("paragraph_id", "style"),
{"visibility": "hidden"},
{"visibility": "visible"},
),
(
Output("progress_bar_graph", "style"),
{"visibility": "visible"},
{"visibility": "hidden"},
),
],
cancel=[Input("cancel_button_id", "n_clicks")],
progress=Output("progress_bar_graph", "figure"),
progress_default=make_progress_graph(0, 10),
prevent_initial_call=True
)
def update_progress(set_progress, n_clicks):
total = 10
figure = None
for i in range(total):
figure = make_progress_graph(i, 10, figure=figure)
time.sleep(0.5)
set_progress(figure)
return [f"Clicked {n_clicks} times"]
if __name__ == "__main__":
app.run(debug=True)
An entire new figure is passed each set_progress
is called. When I rewrite make_progress_graph
like so:
def make_progress_graph(progress, total, figure=None, new_figure=False):
if new_figure:
figure = (
go.Figure(data=[go.Bar(x=[progress])])
.update_xaxes(range=[0, total])
.update_yaxes(
showticklabels=False,
)
.update_layout(height=100, margin=dict(t=20, b=40))
)
return figure
if not figure:
figure = Patch()
figure['data'][0]['x'] = [progress]
return figure
So that on each update a Patch is returned instead of a new figure, I’ve also modified the callback to account for the change. Now, running the callback gives the error:
Failed component prop type: Invalid component prop `figure` key `__dash_patch_update` supplied to Graph.
Bad object: {
"__dash_patch_update": "__dash_patch_update",
"operations": [
{
"operation": "Assign",
"location": [
"data",
0,
"x"
],
"params": {
"value": [
1
]
}
}
]
}
Valid keys: [
"data",
"layout",
"frames"
]
It looks like Background Callbacks don’t support Partial Property Updating for the progress outputs, but is there a way to do this? And if not, how can I request this as a feature? I understand that I’m trying to use these features together in a way that they (probably) weren’t designed to do, but I’ve got an unique set of circumstances that led me to using Background Callbacks to draw some graphs in my app and passing full figures around is slowing things down for me and I believe that using Patches would be my best bet for a speed up given my constraints.
Thanks in advance.