Edit, November 19, 2018 - This issue has been solved, see the solution below: post#29
Imagine following code, there are two buttons bound to the same callback. The target is to identify which button is pressed in the callback. What is the best way?
The application scenario is like one button for page up, the other button for page down. Press button-1 the figure shows data that is newer while press button-2 the figure shows data that is older.
# -*- coding: utf-8 -*-
import dash
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd
import plotly.graph_objs as go
from dash.dependencies import Input, Output, Event, State
app = dash.Dash()
def getLayout():
return html.Div([
html.H1(id='Title1',children='Multiple Button Test',style={'text-align':'center'}),
html.Div(id='Title2', children='''
Test multi button in a single page.
''',style={'margin-bottom':'50px','text-align':'center'}),
html.Div(dcc.Markdown('''''',id='div1')),
html.Button(id='btn1',children='button1'),
html.Button(id='btn2',children='button2'),
])
app.layout = getLayout
@app.callback(Output('div1', 'children'),
inputs=[Input('btn1', 'n_clicks'),
Input('btn2', 'n_clicks')])
def update_div_1(n1,n2):
print('$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$',n1,n2)
#
# How to tell which button is pressed in callback context
#
return '''button1 clicks *'''+str(n1)+'''* times\n'''+'''button2 clicks *'''+str(n2)+'''* times'''
if __name__ == '__main__':
app.run_server(debug=True, port=10092, host='0.0.0.0')
Itās currently not possible to tell which Input was fired. This may change in the future by introducing something like a PreviousState option in the app.callback.
For now, youāll have to figure out another way to represent this UI. Perhaps a slider that represents time?
I have a very similar problemā¦ I have to graphs with some data and text area + img which are supposed to be updated based on hover data from the currently active graph. As itās impossible to create two different callbacks with the same output Iām forced to implement something like this: @app.callback(
Output(āimage1ā, āsrcā),
[Input(āplot1ā, āhoverDataā), Input(āplot2ā, āhoverDataā)])
def display_product_image(hoverData, hoverData2):
print(hoverData)
print(hoverData2)
return "omgā¦"
As both hoverData and hoverData2 are filled I cannot say whatās the source of the update.
I think itās not a matter of my bad ui design, itās rather a limitation of Dash (for comparison it can be easily done in Shiny).
Any idea how to solve it?
@kfyao, you were very close to figuring this out! You can store the previous click state in a global variable (see warning below) and then compare the previous with the current state.
from collections import OrderedDict
import dash
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd
import plotly.graph_objs as go
from dash.dependencies import Input, Output, Event, State
app = dash.Dash()
N_CLICKS = OrderedDict([('btn1',0), ('btn2',0)])
def getLayout():
return html.Div([
html.H1(id='Title1',children='Multiple Button Test',style={'text-align':'center'}),
html.Div(id='Title2', children='''
Test multi button in a single page.
''',style={'margin-bottom':'50px','text-align':'center'}),
html.Div(dcc.Markdown('''''',id='div1')),
html.Button(id='btn1',children='button1'),
html.Button(id='btn2',children='button2'),
])
app.layout = getLayout
@app.callback(Output('div1', 'children'),
inputs=[Input('btn1', 'n_clicks'),
Input('btn2', 'n_clicks')])
def update_div_1(*new_clicks):
global N_CLICKS
btn_clicked = ''
n_clicks_clicked = 0
for (button, n_click_old), n_click_new in zip(N_CLICKS.items(), new_clicks):
if n_click_new > n_click_old:
btn_clicked = button
n_clicks_clicked = n_click_new
N_CLICKS[state_clicked] = n_clicks_clicked
return btn_clicked
if __name__ == '__main__':
app.run_server(debug=True, port=10092, host='0.0.0.0')
This is just an example, for actual production implementation remember to share data between callbacks safely and DO NOT USE A GLOBAL VARIABLEMore info
Remember, using global variables is not safe. Do not mutate global variables. Your apps will break if multiple people are using them at the same time or if they are run on multiple workers.
Just wondering if there is any progress on getting this implemented? I see that there has been no change on the PRs. This is the one feature that seems to be limiting my Dash apps at the moment ! Thanks
There have not been any updates on this yet. You could create a custom build based off of those PRs. I canāt say for sure when weāll have the time to wrap this up, however we can dedicate more resources to this feature if a company or organization is able to sponsor it.
Iāve run in to the same problem. I have a dropdown which selects from a list of values. I would like to have āPreviousā and āNextā buttons to make walking though the list easier.
What is the reason for only allowing one callback per output? Having multiple callback functions seems like the easiest solution.
Allowing multiple callbacks to share the same output causes too much ambiguity (what happens when there are overlapping inputs?) and violates the zen of python rule āthere should be one way to do thingsā.
What if you could allow multiple callbacks per Output but not allow overlapping Inputs across these shared callbacks? I canāt think of any practical reason to have two callbacks with the same Output and the same Input(s)? However, there is a use case for having the State of an element in one callback and the Input in another callback, both with the same Output.
I am too having lots of headache with this limitation. Example:
a table with actions on the table - copy, delete, subscribe, unsubscribe.
the simplest would be to register a callback for each button, perform necessary action and then output rows into table.
but you cant do it because after each button was clicked once you dont know which one was clicked anymore unless you create some really weird state control either via hidden divs (and more callbacks) or managing that state per use session (which is complicated, unreliable or requires some sort of additional services like redis or similar).
all is needed to specify a caller component (i.e. which input) triggered callback
OR
being able to setup multiple callbacks for the same output
UPDATE
i decided that i am spending too much time on workaround and went to the source
it does appear that adding anything that would indicate source of the change is a big change. so i opted out for introducing n_clicks_previous property in the Button component.
for this I modified the generator scripts at dash-html-components/scripts/generate-components.js with 3 lines of code:
add new property n_clicks_previous in the declaration,
set it to 0 in the default props
and set it to n_clicks after event gets fired in the onClick handler:
onClick={() => {
if (props.setProps) props.setProps({n_clicks: props.n_clicks + 1});
if (props.fireEvent) props.fireEvent({event: 'click'});
if (props.setProps) props.setProps({n_clicks_previous: props.n_clicks + 1});
}}
then run rest of the instructions to build and install locally.
that did the trick and I can now determine which of the buttons clicked based on whether their previous value is the same as current. interestingly, i have to use n_clicks_previous = n_clicks + 1 due to some reason which i dont entirely comprehend. using n_clicks_previous = n_clicks gives difference of 2 for clicked button and 1 for those that are not clicked.
This is clever way to make it work for buttons or things with n_clicks.
Iāll add my vote too that it would be nice to have incorporated into Dash ā something easy like this for buttons, or if there is a general and whatever better other way, to know which of the multiple inputs have changed to cause a particular callback. I want to a parameter which is a list of which inputs changed, upon call to callback, so the callback can be aware of what reason or event its being called for exactly.
Right now have to use hidden divs and timestamps. Its kinda works ok but a little messy with all the extra code to keep track of and imprecise.
Are you able to create a branch of dash_html_components and make a pull request? Maybe we can get this merged with the master branch for everyone to use. This adds a lot of functionality without changing the core dash library. Any thoughts @chriddyp ?
I canāt seem to get this to work. Iām new to npm, and javascript so probably doing something wrong. The button seems to have the property n_clicks_previous but it doesnāt update on click any advice? Also nothing is changing in the lib/ folder when I run npm run install-local, Iām not sure that this is why Iām having issues??
onClick={() => {
if (props.setProps) props.setProps({n_clicks: props.n_clicks + 1});
if (props.fireEvent) props.fireEvent({event: 'click'});
if (props.setProps) props.setProps({n_clicks_previous: props.n_clicks + 1});
}}
line 124:
n_clicks: 0,
n_clicks_previous: 0
3rd one is actually not that important.
after that run:
npm install
see that is completes without errors. I would build it on linux box as some commands do not work on windows
this will create a dash_html_components-0.8.0-py2.7.egg in the /dash-html-components/dist folder
you will then need to install it locally using
python -m easy_install dash_html_components-0.8.0-py2.7.egg.
i can share the egg file if you want (i.e. it is already precompiled and all you need to do is just do the python easy_install step)
Because the change is done at the generate-components.js level, it means that all dash-html components that have n_click property will have n_clicks_previous. that includes links, html.H1/2/3/4/5/6 and many many more. comes very handy!