Callbacks#

It is a mechanism that allows you to create a function that will be called when you perform an action. It realised. This is implemented via the dash.callback decorator. More details in the documentation. I will focus on some practical features.

Sources#

Input/output format{#sec-input_output_section}#

Input/Output, implemented by dash.Input/dash.Output, which should be passed as arguments to the callback decorator. Constructors of the classes require the following syntax ("<object-id>", "<property>"), so you can choose which property to pass to the callback and which to change.

In the following example, I simply take dcc.Checklist.values and link it to ddc.Slider.marks - the markers on the dcc.slider will exactly match the selected checkboxes on the dcc.checklist.

from dash import dcc, html, Input, Output, callback
from jupyter_dash import JupyterDash
from IPython.display import clear_output

app = JupyterDash(__name__)
options = list(range(0,20))
value = [1,5]

lst_val_to_slider_marks = lambda value: {val:str(val) for val in value}

app.layout =  html.Div(
    [
        dcc.Checklist(
            options,
            value = value,
            id = "check-lst",
            inline = True
        ),
        dcc.Slider(
            min(options), max(options),
            step = None,
            marks = lst_val_to_slider_marks(value),
            id = "slider"
        )
    ],
    style={'display': 'flex', 'flex-direction': 'column'}
)

@callback(
    Output("slider", "marks"),
    Input("check-lst", "value")
)
def my_callback(val:list) -> dict:
    return lst_val_to_slider_marks(val)

if __name__ == '__main__':
    app.run_server(debug=True, port=8051)
clear_output()

In site it will looks like:

Any check box you click - it will add one more marker on slider.

State callbacks#

Any dash.dcc.Input will trigger the callback when the related item has changed. But sometimes it’s useful to have an element that sends its state, but only when some other element triggers the callback. Such purpose should be completed by dash.dcc.State.

So the following example is the same as in the Input/Output Format section, but it updates the slider not when the new checkbox is selected, but only when the button is pressed.

from dash import dcc, html, Input, Output, callback, State
from jupyter_dash import JupyterDash
from IPython.display import clear_output

app = JupyterDash(__name__)
options = list(range(0,20))
value = [1,5]

lst_val_to_slider_marks = lambda value: {val:str(val) for val in value}

app.layout =  html.Div(
    [
        dcc.Checklist(
            options,
            value = value,
            id = "check-lst",
            inline = True
        ),
        dcc.Slider(
            min(options), max(options),
            step = None,
            marks = lst_val_to_slider_marks(value),
            id = "slider"
        ),
        html.Button(
            "Update bar", id = "button"
        )
    ],
    style={'display': 'flex', 'flex-direction': 'column'}
)

@callback(
    Output("slider", "marks"),
    State("check-lst", "value"),
    Input("button", "n_clicks")
)
def my_callback(val: list, n_clicks : int) -> dict:
    '''
        Callback for button.

        Arguments
        -----------
        
        val : (list) value passed from check list by State
                caontains camptions of selected boxes;
        n_clicks : (int) n_clicks of button passed by Input;

        Returns
        -----------

        (dict) which maps values and it's markers on slider.
    '''
    return lst_val_to_slider_marks(val)

app.run_server(debug=True)
clear_output()

Chained callbacks#

This section is just a variant of that material. But better described.

This example shows how one event can trigger a chain of different callbacks.

  • So we have a button that adds a new option to the checkbox list with a button_click callback;

  • When something changes options in the checklist, it triggers check_list_options_changed, which only sets the last option selected;

  • When something changes selected options, check_list_values_changed will be triggered and show selected options like line.

from dash import dcc, html, Input, Output, callback
from IPython.display import clear_output
from jupyter_dash import JupyterDash

app = JupyterDash(__name__)
app.layout = html.Div([
    html.Button("Add new box", id = "add-button", n_clicks = 0),
    dcc.Checklist(id="check-list"),
    html.P(id="disp-sel-boxes")
])

@callback(
    Output("check-list", "options"),
    Input("add-button", "n_clicks")
)
def button_click(n_clicks):
    '''
        Callback makes in listbox exactly
        number of boxes as the number of
        button is clicked.

        Arguments
        ------------

        n_clicks : (int) count of clicks on button;

        Returns
        -----------

        (list) captions of boxes which will 
        be displayed in check-list.
    '''
    return [f"box {i}" for i in range(n_clicks+1)]

@callback(
    Output("check-list", "value"),
    Input("check-list", "options")
)
def check_list_options_changed(options):
    '''
        Callback that will be called when the list of
        of options in the checklist is updated. It sets
        only the last option as a value.

        Arguments
        -----------

        options : (list) list of available options.

        Returns
        -----------

        (list) contains only the last option.
    '''
    return [options[-1]]

@callback(
    Output("disp-sel-boxes", "children"),
    Input("check-list", "value")
)
def check_list_values_changed(value):
    '''
        Called when you select/unselect a check.

        Arguments
        -----------
        
        value : (list) captions of selected boxes;

        Returns
        -----------
        
        (str) line that describes selected options
        as "Selected boxes: <box1>, ..., <boxn>".
    '''
    return "Selected boxes: " + ", ".join([val for val in value])

app.run(debug=True)
clear_output()

Callback without output#

It turns out that dash has no callback mechanism without output. So the only tricky way is to create a dummy object and set it as the output object. In the following example, I use html.Div(id='dummy'), or rather its children property. I also print out some messages with changes to dcc.Checklist to prove that everything is working.
You can find py file with the following example in “callbacks_examples/no_output_callback.py”.

from dash import dcc, html, Input, Output, callback, Dash
from IPython.display import clear_output
from jupyter_dash import JupyterDash

app = Dash(__name__)

check_values = ["value1", "value2", "value3"]

app.layout = html.Div([
    html.Div(id='dummy'),
    dcc.Checklist(
        check_values,
        id = "check-lst"
    )
])

clicks_counter = 0

@callback(
    Output("dummy", "children"),
    Input("check-lst", "value")
)
def test_callback(checklist_value):
    global clicks_counter
    clicks_counter += 1

    print("==========================")
    print(f"    CLICK {clicks_counter}     ")
    print("==========================")
    
    print("-------value-------")
    print(checklist_value)
    return None

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

clear_output()