Callback mysteriously stopped working

Unfortunately, I don’t know what I changed in the code that would cause this callback to stop working.

This callback works like a champ:

@app.callback(
    dash.dependencies.Output('selecteddie', 'style'),
    [dash.dependencies.Input('refresh', 'n_clicks')]
)
def worknow(refreshdata):
    print('WORKNOW!', refreshdata)
    raise PreventUpdate

This callback refuses to acknowledge that button ‘refresh’ has ever been pushed:

@app.callback(
    [
        dash.dependencies.Output('Bin_Wafermap', 'figure'),
        dash.dependencies.Output('selecteddie', 'children'),
        dash.dependencies.Output('dbstate', 'children'),
        dash.dependencies.Output('dbstate', 'style')
    ],
    [dash.dependencies.Input('refresh', 'n_clicks'),
     dash.dependencies.Input('Bin_Wafermap', 'clickData'),
     dash.dependencies.Input('idwafer', 'value'),
     dash.dependencies.Input('product', 'value'),
     dash.dependencies.Input('subproduct', 'value'),
     dash.dependencies.Input('idlot', 'value'),
     ]
)
def update_click_data(refreshdata, clickData, wfrval, myprod, subprod, mylot):
    print("LAUNCH THE CALLBACK", refreshdata)
    raise PreventUpdate

why did my callback stop working? I tried to strip everything out of it, Dash still pretends that it doesn’t exist anymore :frowning:

Also tried rebooting and updating DASH with pip. Also of note: I have “debounce” on for the text input boxes but also tried turning “debounce” off and in both cases the text input boxes DO call the callback.

Thanks, – Ben

Hi @bebuck, could you please share a complete standalone app (including layout in particular) so that we can reproduce the issue? Do you execute the app with debug=True and if yes, are there any error messages?

Thanks for the response. Here’s what I’m running, and there are no messages from DASH; the Callback launches properly for everything but two of the buttons, “do access” and “verify state”

import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.exceptions import PreventUpdate
import pandas as pd
import plotly.graph_objs as go

app = dash.Dash(__name__)

sample_colormap = [[0.0, 'black'],[0.1,'black'],[0.1,'yellow'],[0.2, 'yellow'],[0.2,'orange'],[0.4,'orange'],[0.4,'green'],
                   [0.6,'green'],[0.6,'red'],[0.8,'red'],[0.8,'blue'],[1,'blue'], [1, 'brown']]

basedir = r'c:\temp' + '\\'


def initialize_storedata(curwafer='No Wafer'):
    serializable = {
        'Wafergz': [],
        'ROW': [],
        'COL': [],
        'Claimed': [],
        'Comment': []
    },
    wafertable = {
        'LOT': [],
        'WAFER_ID': []
    }
    storedata = {'loading': False, 'scratchsrc': serializable, 'curwafer': curwafer, 'fireup': False,
                 'lastx': None, 'lasty': None, 'refreshclicks': 0, 'subproducts': {'': []}, 'dbstate': False,
                 'subdefvals': {'': ''}, 'myprods': [], 'defval': '', 'lotlist': [], 'wafertable': wafertable}
    return storedata


app.layout = html.Div([
    dcc.Store(id='memory'),
    html.Div([
        dcc.Input(
            id='input_username',
            type='text',
            placeholder='Username',
            debounce=True,
            size='50'
        ),
        dcc.Input(
            id='input_password',
            type='password',
            placeholder='Password',
            debounce=True,
            size='50'

        ),
        html.Button('Do Access', id='dosomething')
    ]),
    html.Div([
        dcc.Dropdown(
            id='product',
            options=[],
            value=''
        )
    ]),
    html.Div([
        dcc.Dropdown(
            id='subproduct',
            options=[],
            value=''
        )
    ]),
    html.Div([
        dcc.Dropdown(
            id='idlot',
            options=[],
            value=''
        )
    ]),
    html.Div([
        dcc.Dropdown(
            id='idwafer',
            options=[],
            value=''
        )
    ]),
    html.Div(html.Button('Verify State', id='refresh'), id='refbutton'),
    html.Div(['Not successfully logged on to thesytem'], id='dbstate', style={'color': 'red'}),
    html.Div(id='grossreality'),
    html.Div(id='selecteddie'),
    html.Div(id='emptybox'),
    html.Div(id='emptyboxtwo'),
    html.Div(id='emptyboxthree'),
    html.Div([
    dcc.Graph(
        id='Bin_Wafermap',
        figure={
            'data': [
                {
                    'y': [0, 0, 1, 1],
                    'x': [0, 1, 0, 1],
                    'text': ['A', 'B', 'C', 'D'],
                    'type': 'scatter',
                    'mode': 'markers+text',
                    'marker':
                    {
                        'symbol': ['square'] * 4,
                        'size': [24] * 4,
                        'colorscale': sample_colormap,
                        'color': [1] * 4,
                        'cmin': 0,
                        'cmax': 1
                    }
                }
            ],
            'layout': go.Layout(
                xaxis={'title': 'ROW', 'visible': False},
                yaxis={'title': 'COL', 'visible': False},
                margin={'l': 150, 'b': 50, 't': 10, 'r': 10},
                legend={'x': 1, 'y': 0},
                hovermode='closest',
                clickmode='event+select',
                height=720,
                width=960
            )
        }
    )
    ]),
    html.Div([html.Button('Claim Die', id='dieclaim'),
              html.Button('Leave Comment', id='diecomment')], id='claimbutton'),
    html.Div(dcc.Input(id='commentsdata', type='text', debounce=True, size='120'))
    ])


@app.callback(
    [dash.dependencies.Output('product', 'options'),
     dash.dependencies.Output('subproduct', 'options'),
     dash.dependencies.Output('subproduct', 'value')],
    [dash.dependencies.Input('product', 'value'),
     dash.dependencies.Input('refresh', 'n_clicks'),
     dash.dependencies.Input('dosomething', 'n_clicks')],
    [dash.dependencies.State('memory', 'data')]
)
def update_products_and_subproducts(value, refbutton, pwbutton, storedata):
    if storedata is None:
        storedata = initialize_storedata()
    if not storedata['fireup']:
        raise PreventUpdate
    if value in storedata['subproducts'] and value in storedata['subdefvals']:
        return storedata['myprods'], storedata['subproducts'][value], storedata['subdefvals'][value]
    else:
        return storedata['myprods'], [], ''


@app.callback(
    dash.dependencies.Output('product', 'value'),
    [dash.dependencies.Input('refresh', 'n_clicks'),
     dash.dependencies.Input('dosomething', 'n_clicks')],
    [dash.dependencies.State('memory', 'data')]
)
def update_product_value(refbutton, pwbutton, storedata):
    if storedata is None:
        storedata = initialize_storedata()
    if not storedata['fireup']:
        raise PreventUpdate
    return storedata['defval']


@app.callback(
    [dash.dependencies.Output('idlot', 'options'),
     dash.dependencies.Output('idlot', 'value')],
    [dash.dependencies.Input('refresh', 'n_clicks'),
     dash.dependencies.Input('product', 'value'),
     dash.dependencies.Input('subproduct', 'value'),
     dash.dependencies.Input('refresh', 'n_clicks'),
     dash.dependencies.Input('dosomething', 'n_clicks')],
    [dash.dependencies.State('memory', 'data')]
)
def update_lots(refreshdata, value, subvalue, refresh, pwbutton, storedata):
    if storedata is None:
        storedata = initialize_storedata()
    if not storedata['fireup']:
        raise PreventUpdate
    if value is None:
        value = ''
    if subvalue is None:
        subvalue = ''
    raise PreventUpdate

@app.callback(
    [dash.dependencies.Output('idwafer', 'options'),
     dash.dependencies.Output('idwafer', 'value')
        ],
    [dash.dependencies.Input('refresh', 'n_clicks'),
     dash.dependencies.Input('product', 'value'),
     dash.dependencies.Input('subproduct', 'value'),
     dash.dependencies.Input('idlot', 'value')],
    [dash.dependencies.State('memory', 'data')]
)
def update_wafers(refreshdata, value, subvalue, lotvalue, storedata):
    if storedata is None:
        storedata = initialize_storedata()
    if not storedata['fireup']:
        raise PreventUpdate
    wafertable = pd.DataFrame(storedata['wafertable'])
    waferlist = wafertable[wafertable.LOT == lotvalue]['WAFER_ID'].unique()
    if value is None:
        value = ''
    if subvalue is None:
        subvalue = ''
    if lotvalue is None:
        lotvalue = ''
    raise PreventUpdate


@app.callback(
    [dash.dependencies.Output('grossreality', 'children'),
     dash.dependencies.Output('grossreality', 'style')
     ],
    [dash.dependencies.Input('refresh', 'n_clicks'),
     dash.dependencies.Input('idlot', 'value'),
     dash.dependencies.Input('idwafer', 'value')],
    [dash.dependencies.State('memory', 'data')]
)
def checkrealitystate(refreshdata, lotvalue, wafervalue, storedata):
    if storedata is None:
        storedata = initialize_storedata()
    if lotvalue is None:
        lotvalue = ""
    if wafervalue is None:
        wafervalue = ""
    if str(lotvalue) + ' ' + str(wafervalue) != storedata['curwafer'] or storedata['validity'] == 'False' \
            or len(storedata['curwafer']) < 3:
        return 'Please Click Verify State before proceeding to ensure page is ready (if it doesn\'t respond, it isn\'t.)', {'color': 'red'}
    else:
        return 'Now showing:' + str(storedata['curwafer']), {'color': 'black'}


@app.callback(
    dash.dependencies.Output('emptybox', 'style'),
    [dash.dependencies.Input('refresh', 'n_clicks')]
)
def worknow(refreshdata):
    print('The Verify Button Successfully fired the alt callback', refreshdata)
    raise PreventUpdate


@app.callback(
    dash.dependencies.Output('emptyboxtwo', 'style'),
    [dash.dependencies.Input('dosomething', 'n_clicks')]
)
def worknowtwo(refreshdata):
    print('The Do Something Button Successfully fired the alt callback', refreshdata)
    raise PreventUpdate


@app.callback(
    dash.dependencies.Output('emptyboxthree', 'style'),
    [dash.dependencies.Input('dieclaim', 'n_clicks')]
)
def worknowthree(refreshdata):
    print('The Dieclaim Button Successfully fired the alt callback', refreshdata)
    raise PreventUpdate


@app.callback(
    [
        dash.dependencies.Output('memory', 'data'),
        dash.dependencies.Output('Bin_Wafermap', 'figure'),
        dash.dependencies.Output('selecteddie', 'children')
    ],
    [dash.dependencies.Input('refresh', 'n_clicks'),
     dash.dependencies.Input('Bin_Wafermap', 'clickData'),
     dash.dependencies.Input('idwafer', 'value'),
     dash.dependencies.Input('product', 'value'),
     dash.dependencies.Input('subproduct', 'value'),
     dash.dependencies.Input('idlot', 'value'),
     dash.dependencies.Input('dieclaim', 'n_clicks'),
     dash.dependencies.Input('diecomment', 'n_clicks'),
     dash.dependencies.Input('commentsdata', 'value'),
     dash.dependencies.Input('dosomething', 'n_clicks'),
     dash.dependencies.Input('input_username', 'value'),
     dash.dependencies.Input('input_password', 'value')
     ],
    [dash.dependencies.State('memory', 'data')]
)
def update_click_data(refreshdata, clickData, wfrval, myprod, subprod, mylot, dieclaim, diecomments, commentsdata, pwclicks, username, password, storedata):
    print("LAUNCH THE CALLBACK rfbutton", refreshdata, "dieclaim", dieclaim, "diecomments", diecomments, 'pwclicks', pwclicks)
    raise PreventUpdate

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

There might be a problem with your callback graph: see below the image obtained with the developers tools (click on the blue button on the bottom-right corner of your app). The refresh button triggers the callback modifying grossreality but also a callback modifying idwafer and another one modifying idlot, which can in turn modify grossreality. I think it is hard for Dash to know in which order to trigger these callbacks. The same situation happens for the dosomething button. Probably you need to simplify the graph of callbacks so that there is a single path starting from refresh to modify grossreality (and the same for dosomething).

In the spirit of “if you don’t ask it won’t happen”, is there a way to make ALL the callbacks run that consume data out of [dash.dependencies.State(‘memory’, ‘data’)] ANY time values change in that? (possibly excepting the one callback that has the privileges to update [dash.dependencies.State(‘memory’, ‘data’)].) That alone would make unnecessary and simplify down my callback chain. Thanks.

Hum, if you want callbacks to run when a component changes, I dont’ understand why you don’t put it as Input (instead of State)? The distinction exists so that an Input triggers the callback to run, while a State can be passed as a variable but will not trigger the callback. What am I missing :slight_smile: ?

I didn’t realize you could do that. Once I rename Input to State and State to Input on dash.dependencies, it seems to correct the issue, thanks