determine which input is triggering in callback
See original GitHub issueSince only a single Output is allowed in a callback, and all Inputs must feed into it, how can we determine which input is being triggered during a callback? I have three time-series line plots that I want to cross-filter, and update, any time a range is selected in any of the other plots. For example, the range of Plot A should update when either the range of Plot B or Plot C is updated. But the callback collects the ranges from all plots simultaneously. Is there a way to get the id of which input was triggered during a callback?
Here is my code but it only updates with data from plotB, when it is triggered. When I update Plot C the datac variable is indeed updated but the data from plotB is also passed in again, so the callback function has no knowledge of which plot is actually triggering new input.
@app.callback(
Output('plotA', 'figure'),
[Input('plotB', 'relayoutData'),
Input('plotC', 'relayoutData')])
def display_selected_data(data, datac):
print('data', data)
print('datac', datac)
startx = 'xaxis.range[0]' in data if data else None
endx = 'xaxis.range[1]' in data if data else None
# define the new xrange
if startx and endx:
xrange = [data['xaxis.range[0]'], data['xaxis.range[1]']]
elif startx and not endx:
xrange = [data['xaxis.range[0]'], thedates.max()]
elif not startx and endx:
xrange = [thedates.min(), data['xaxis.range[1]']]
else:
xrange = None
traces = [go.Scatter(
x=thedates,
y=mdf['uniqvisits'])]
return {
'data': traces,
'layout': get_layout('Unique Visits', 'Date', 'Unique Visits', xrange=xrange)
}
Issue Analytics
- State:
- Created 6 years ago
- Reactions:7
- Comments:13 (2 by maintainers)

Top Related StackOverflow Question
Great question @havok2063 !
This isn’t possible in Dash.
One core concept with Dash is that the app is described entirely by its current state and not by the order of events. This concept makes its easier to reason about the app’s logic and it forces the user interface to be consistent with exactly what you see on the screen (and not by the steps that were taken to get there).
I think that in most cases, the Dash app developer shouldn’t need to know the order of events. If they do, it usually means that the component needs to be patched in a way to make it more stateful or the UI needs to be redesigned. I’ll leave this issue open to invite examples of good UIs that are impossible to create without supporting “order-of-events”.
In this case, our app logic shouldn’t depend on the most recent action, it should depend on the current state of the other graphs. For example, if
Graph Adepends onGraph BandGraph C, the data should be filtered by the intersect of the regions selected or zoomed inBandC, not just the most recently zoomed.Here is how I recommend doing crossfiltering for now. A few things to note: 1 - We’re using
selectedDatainstead of zoom data withrelayoutDataso that the viewer can see the whole picture and can easily edit their selection. 2 - We’re styling the selected graph’s points themselves to better indicate which points were selected. 3 - We’re displaying the most recently “selectedData” by drawing a rectangle shape (documentation on shapes). This requires a recent version ofdash-core-components:pip install dash-core-components==0.5.3. 4 - To keep the code DRY 🌴 we’re generating the callback functions and calling the decorators directly. For more on generating functions and generators in Python, I recommend this SO answer/essay on decorators. 5 - You’re free to customize the style of the selected points or of the non-selected points. In this case, we’re dimming the non-selected points by settingopacity=0.1. 6 - The numbers displayed on top of the points are just the IDs of the points to help you better understand how filtering is working. In practice, we would probably hide these or display a more descriptive ID.@havok2063 States are arguments passed to the function of the callback that won’t trigger the callback itself. So changing the code you provided:
In the above code only changes on the relayoutData of plotB would trigger the callback, changes to the layout of plotC would not trigger the callback itself. plotC’s relayoutData will be passed into
def display_selected_dataas datac and can be accessed when the callback itself is triggered but layout changes on plotC will not directly trigger the callback.In terms of what you’re trying to do, it might be bit tough to figure out directly which input triggered the callback, but one thing you could do is to have two hidden
html.Pelement that you tie to separate callbacks and when either plotB changes or plotC changes you set the value of thehtml.Pelement associated with either one to a known value - for example, you could set the element to hold the value of 1 if the plot has changed and a 0 otherwise. You could then pass the paragraph elements as inputs:And to reset the values of
pHolderPlotBandpHolderPlotCafter you have redrawn plotA you could create a third callback that will reset the values of bothpHolderPlotBandpHolderPlotCback to 0 whenplotAhas changed.There might be easier ways to do this but this is one that came to mind.
Let me know if my answer was hard to follow or if I could clarify something.