I want to create a file download button and on successful upload a ConfirmationDialog shall be displayed. I don’t understand why my solution below doesn’t work. The problem is the following: When I upload the first file, the confirmation message appears correctly after upload is finished. From then on, I get unwanted confirmation messages. When I upload another file I get at least two confirmation messages, one before the upload actually starts and one after.
On a side note, I’d really like to dynamically change the message content of the confirm dialog depending on the outcome of the file upload (like ‘20 line were updated’).
# -*- coding: utf-8 -*-
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
app = dash.Dash(name=__name__)
app.layout = html.Div(
children=[
dcc.Upload(
id='upload_button',
children=html.Button('File Upload'),
),
dcc.ConfirmDialog(
id='upload_success',
message="Upload successful",
),
]
)
@app.callback(Output('upload_success', 'displayed'),
[Input('upload_button', 'contents')],
[State('upload_button', 'filename'),
State('upload_button', 'last_modified')])
def load_file(contents, filename, date):
print("CALLBACK: load_file:", filename, date)
if contents is not None and filename is not None:
# upload process
import time
time.sleep(2) # simulation download process (not related to the problem)
return True # I expect this to trigger a single displaying of the popup
return False
if __name__ == '__main__':
app.run_server(debug=False)
You have three Input with the upload props, it will trigger three callback each time you upload. Consider having only one Input with the contents and the rest as State. Combined with the __main__ run without threaded or multiprocress, these requests will run synchronously meaning you’ll get one popup after 2 sec, then another after 4, etc.
Thank you Philippe. I changed my callback to react only on only one Input . Strangely that doesn’t change the behaviour. When I press the Button the second time, there is still the unexpected displaying of the pop-up before the Load_file is even triggered.
@app.callback(Output('upload_success', 'displayed'),
[Input('upload_button', 'contents')],
[State('upload_button', 'filename'),
State('upload_button', 'last_modified')])
def Load_file(contents, filename, date):
print("CALLBACK: Load_file:", filename, date)
if contents is not None and filename is not None:
# upload process
import time
time.sleep(2) # simulation download process (not related to the problem)
return True # I expect this to trigger a single displaying of the pop-up
return False
I have changed the original posts using State as Philippe suggested. I still get extra pop-up windows and I do not understand why. Can someone please take a look?
Came across this post and while trying a few times I could find a solution.
Creating a dummy function to “consume” the submit_n_click prevents the dialog to appear before the Load_file function is called.
@app.callback(
Output('output-confirm', 'children'), [
Input('upload_success', 'submit_n_clicks')
]
)
def update_output(submit_n_clicks):
"""Function to consume the n_click.
Without this function, the dialog appears right when we click instead
of when files have been downloaded.
:param submit_n_clicks: The user's clicks
:type submit_n_clicks: dict
:returns: A dummy text
:rtype: str
"""
if submit_n_clicks:
return "dummy"
The “output-confirm” can be a hidden div like this
Thanks miguel.aveiro, your “solution” does solve my original problem, although I don’t really get, why this is necessary.
Me not getting the point is probably the reason why I can’t figure out the next steps myself. This time I want to show another dialog after the first one. I add a second dummy to consume the second confirm dialog, but this time it doesn’t work. At least I assume that from the browser message that it prevents to open further dialogs.
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
app = dash.Dash(name=__name__)
app.layout = html.Div(
children=[
html.Button(
'Button',
id='upload_button'),
dcc.ConfirmDialog(
id='upload_success_1',
message="I want to say this...",
),
dcc.ConfirmDialog(
id='upload_success_2',
message="... and I want to say that.",
),
html.Div(id='output-confirm_1', style={'display': 'none'}),
html.Div(id='output-confirm_2', style={'display': 'none'}),
]
)
@app.callback(Output('upload_success_2', 'displayed'),
[Input('upload_success_1', 'submit_n_clicks')])
def second_confirm_dialog(n_clicks):
print("second_confirm_dialog", n_clicks)
if n_clicks:
return True
@app.callback(Output('upload_success_1', 'displayed'),
[Input('upload_button', 'n_clicks')])
def first_confirm_dialog(n_clicks):
print("first_confirm_dialog", n_clicks)
if n_clicks:
# do something
return True
return False
@app.callback(
Output('output-confirm_1', 'children'),
[Input('upload_success_1', 'submit_n_clicks')]
)
def update_output_1(submit_n_clicks):
print("first dummy", submit_n_clicks)
if submit_n_clicks:
return "dummy"
@app.callback(
Output('output-confirm_2', 'children'),
[Input('upload_success_2', 'submit_n_clicks')]
)
def update_output_2(submit_n_clicks):
print("second dummy", submit_n_clicks)
if submit_n_clicks:
return "dummy"
if __name__ == '__main__':
app.run_server(debug=False)