show_stopper
show_stopper

Reputation: 288

Change the value of n_click of button in Dash app

I am trying to create an app which keeps count of the number of times Button A and Button B are clicked. I am doing that by checking the values of n_click for each button and using them as inputs in call backs. What I additionally want to do is set the n_click to zero for both buttons if n_clicks of Button B exceeds n_clicks of Button A.

Is there anyway such can be achieved in Dash? Or do I need to store variables in storage and then update them everytime a button is clicked?

Edit: The accepted answer below requires Dash 1.20.0. I had to upgrade mine from 1.18.1 to 1.20.0 to get this functionality.

Upvotes: 1

Views: 6072

Answers (2)

bas
bas

Reputation: 15462

The approach below works for dash version 1.19.0 up to the current version 1.20.0 (and probably onwards).

[1.19.0] - 2021-01-19: Adds support for callbacks which have overlapping inputs and outputs. Combined with dash.callback_context this addresses many use cases which require circular callbacks.

https://github.com/plotly/dash/blob/dev/CHANGELOG.md#added-1


As you've already said you can use the n_clicks property of the buttons to get the number of times each button has been clicked.

n_clicks can also be set using a regular Output.

...Or do I need to store variables in storage and then update them everytime a button is clicked?

n_clicks and dash.callback_context track this for you so I don't think there is a need to track it yourself also.

In addition to event properties like n_clicks that change whenever an event happens (in this case a click), there is a global variable dash.callback_context, available only inside a callback.

https://dash.plotly.com/advanced-callbacks

Example of how you could go about it (Adjusted version of the advanced callback example in the documentation linked above):

import json
import dash
import dash_html_components as html
from dash.dependencies import Input, Output, State

app = dash.Dash(__name__)

app.layout = html.Div(
    [
        html.Button("Button A", id="btn-a"),
        html.Button("Button B", id="btn-b"),
        html.Div(id="container"),
    ]
)


@app.callback(
    Output("container", "children"),
    Output("btn-a", "n_clicks"),
    Output("btn-b", "n_clicks"),
    Input("btn-a", "n_clicks"),
    Input("btn-b", "n_clicks"),
    prevent_initial_call=True,
)
def display(n_clicks_a, n_clicks_b):
    ctx = dash.callback_context

    ctx_msg = json.dumps(
        {"states": ctx.states, "triggered": ctx.triggered, "inputs": ctx.inputs},
        indent=2,
    )

    if n_clicks_a is not None and n_clicks_b is not None and n_clicks_b > n_clicks_a:
        return html.Pre(ctx_msg), 0, 0

    return html.Pre(ctx_msg), n_clicks_a, n_clicks_b


if __name__ == "__main__":
    app.run_server(debug=True)

In addition to using Input you could also use State to get n_clicks if you don't want the callback to trigger on a change of n_clicks, but you do want access to its value.

Upvotes: 2

Michel
Michel

Reputation: 797

No need to do anything complicated, just put the n_clicks property of the buttons as output of your callback and return zero as needed. There is nothing preventing you from using the same properties as both Input and Output of the same callback. Here is a minimal example:

import dash
from dash.dependencies import Input, Output
import dash_html_components as html

app = dash.Dash(__name__)

app.layout = html.Div([
    html.Button('Button 1', id='btn1', n_clicks=0),
    html.Button('Button 2', id='btn2', n_clicks=0),
    html.Div(id='msg-container')
])

@app.callback(Output('msg-container', 'children'),
              Output('btn1', 'n_clicks'),
              Output('btn2', 'n_clicks'),
              Input('btn1', 'n_clicks'),
              Input('btn2', 'n_clicks'))
def displayClick(btn1, btn2):
    if btn2 > btn1:
        btn1, btn2 = 0, 0
    msg = "Button 1 clicked {} times, Button 2 clicked {} times".format(btn1, btn2)
    return msg, btn1, btn2

if __name__ == '__main__':
app.run_server(debug=True)

Upvotes: 1

Related Questions