arr0w
arr0w

Reputation: 41

How to trigger functions with plotly-dash button inside a tab in python?

I am quite new to programming and to plotly dash as well. I have some tabs in my dashboard and in each tab i have a text input box (int) and a button that triggers some calculations using the input.

With the following script, when I select the tab, the callback is triggered and the calculation already starts with the empty value in the text, even before I click the button. I do have an if clause to return nothing when the text input box is empty. But that doesn't seem to be working. It directly goes to the final "else" clause. How can i solve this? Thank you!

@app.callback(Output('PRINTOUTPUT', 'children'),
              [Input('create_button', 'n_clicks'),
              Input('selection_tabs', 'value')],
              [State('text-input', 'value')])

def xyz (clicks, selected_tab, textinput):    
    
    changed_id = [p['prop_id'] for p in dash.callback_context.triggered][0] #To determine if n_clicks is changed. 

      
    if 'create_button' not in changed_id:
        return ""

    elif 'create_button' in changed_id:           
        if textinput =="":  #Do nothing if button is clicked and input num is blank.            
            return "No input"
        
        else: 
            #Do some calculations based on the selected tab and input
             return "Successful"

Upvotes: 1

Views: 5157

Answers (2)

Niko Fohr
Niko Fohr

Reputation: 33770

If there is a problem in your code, it is probably elsewhere than in the part you pasted to the question. I wrote an MWE around the callback you provided and it seems it is working as you intented:

MWE demo1

Improvements for the callback

  • The code below includes a bit improved callback
  • The prop_id attribute of the items in the list dash.callback_context.triggered is of the form component_id.component_property. Therefore, if you want to check whether it was 'create_button' that triggered the callback, you should compare against the part before the dot (or compare to 'create_button.n_clicks' directly).
  • Previously, only the first element in the list dash.callback_context.triggered was checked. You probably want to check against all the elements in the list, although in current version of dash it can only contain properties of single component: "is a length-1 list, unless two properties of a single component update simultaneously, such as a value and a timestamp or event counter." (dash.callback_context.triggered)
  • Increased readability by removing unnecessary nesting of if statements, since the execution of the function will not continue after a return.

Full code

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

external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']

app = dash.Dash(__name__, external_stylesheets=external_stylesheets)


tab_div = html.Div(
    dcc.Tabs(id='selection_tabs', value='tab-1', children=[
        dcc.Tab(label='Tab one', value='tab-1'),
        dcc.Tab(label='Tab two', value='tab-2'),
]))

app.layout = html.Div(children=[
    html.H1(children='Test app'),
    tab_div,
    dcc.Input(id="text-input", type="text", placeholder=""),
    html.Div(id='PRINTOUTPUT'),
    html.Button('Click me', id='create_button')
])

@app.callback(Output('PRINTOUTPUT', 'children'),
              [Input('create_button', 'n_clicks'),
              Input('selection_tabs', 'value')],
              [State('text-input', 'value')])

def xyz(clicks, selected_tab, textinput):    

    #To determine if n_clicks is changed. 
    changed_ids = [p['prop_id'].split('.')[0] for p in dash.callback_context.triggered]
    button_pressed = 'create_button' in changed_ids

    if not button_pressed:
        return ""

    if textinput == "":  #Do nothing if button is clicked and input num is blank.            
        return "No input"
    
    #Do some calculations based on the selected tab and input
    return "Successful"

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

Upvotes: 2

Ank
Ank

Reputation: 1704

Assuming that you are using dcc.Input component for your text input, it has an optional parameter value which could be set while defining it. By default, this value is set to python's None. So if you are not setting this parameter's value to empty string "" when defining it, then comparing it to "" in your if clause will result in False, and that is why it always ends up in the else clause.

You just need to check if your textinput is None. Try changing your if clause to if textinput is None: #Do nothing if button is clicked and input num is blank. Or you can also explicitly set the default value of your input box to "" at the place where you are defining it, but that seems a bit counter-intuitive to do.

Upvotes: 0

Related Questions