Hi Bryan and the community
Bryan - thank you for showing an interest in our issue.
Yes, of course you can see the callback.
The callback uses a monitor that I will document first - I think we found it in a code example somewhere:
Global variable:
appdebug=False # used in monitor_cb (which is used in appc.py)
def monitor_cb(cb,ctx):
if not ctx.triggered:
x = 'NONE'
else:
pid = ctx.triggered[0]['prop_id']
val = ctx.triggered[0]['value']
if pid == 'mytable.selected_rows':
x = 'import'
elif pid == 'memory.data':
x = 'memory'
elif pid == 'upload-paramobj.contents':
if val is None:
x = 'NONE'
else:
x = 'upload'
elif pid == 'button_appc.n_clicks':
x = 'appc-click'
else:
x = val
if appdebug:
print(cb+': '+str(x))
return x
As we can see from above code, the x = ‘import’ is what decides that appc.py shall import data from the table in the IMPORT tab,
which if I am correct means that callback is called as soon as the appc.py application is loaded,
and pid contains a value (= mytable.selected_rows) even before anyone has pushed any buttons. This is strange.
Here’s the callback in appc.py:
# When "Apply" is clicked, capture data, process, return data object as output
@app.callback([Output('memory','data'),
Output('myimport','children')],
[Input('button_appc','n_clicks'),
Input('mytable','selected_rows'),
Input('upload-paramobj','contents')],
appc_state_all) # appc_state_all -> [ids,datafile,memory]
def cb_appc_mem(n,r,c,manufacturer,brand,model,provider,date,comments,volt,git,
re,leb,rss,ke,le,bl,mms,c0,r0,f0,beta,lamb,sd,dd,
cms,rms,l3,qms,qes,qts,fs,vas,
temp,pres,ah,cs,rho,xmax,
fname,spk):
x = monitor_cb('appc_cb_mem ',callback_context) # monitoring what caused the callback
return appc_update_spk(x,r,c,manufacturer,brand,model,provider,date,comments,volt,git,
re,leb,rss,ke,le,bl,mms,c0,r0,f0,beta,lamb,sd,dd,
cms,rms,l3,qms,qes,qts,fs,vas,
temp,pres,ah,cs,rho,xmax,
fname,spk)
In the above comment it says when ‘Apply’ is clicked, but the comment is old/incomplete.
Actually monitor_cb is looking to see what caused the callback, and appc_update_spk then acts according to that.
All the variables (re, leb and so on) are elements in a loudspeaker datasheet,
which is loaded from the library of IMPORTS. For our problem, they can be ignored.
The IMPORT tab is one among several tabs in appc.py … below you see what it looks like in the code,
and it uses a global variable named tardict, which contains all the data in the IMPORT table
(but no information about which is selected, if any).
Same logic as before, this dictionary is a ‘constant’ and the content doesn’t change, never.
The fact that this data is loaded from a .tgz file is just our rudimentary version of a database
(which noone can write to - any changes here require we do it manually).
# The tardata information needs to be global (stored in tardict)
tardata = tarfile.open('assets/all.tgz')
tardict = []
for x in tardata.getmembers():
f = tardata.extractfile(x)
a = space_add(json.load(f))
tardict.append(a)
tardata.close()
#=======================================================================
# Assemble layout
#
def build_editortabs():
editortabs = []
editortabs.append(dcc.Tab(label='General',children=build_generaltab(),
style=tab_style,selected_style=tab_style_sel))
editortabs.append(dcc.Tab(label='Advanced',children=build_advancedtab(),
style=tab_style,selected_style=tab_style_sel))
editortabs.append(dcc.Tab(label='Simple',children=build_simpletab(),
style=tab_style,selected_style=tab_style_sel))
editortabs.append(dcc.Tab(label='Import',children=build_importtab(tardict),
style=tab_style,selected_style=tab_style_sel))
editortabs.append(dcc.Tab(label='JSON Summary',children=html.Div(id='tab-summary'),
style=tab_style,selected_style=tab_style_sel))
editortabs.append(dcc.Tab(label='Readme',children=readme_appc(),
style=tab_style,selected_style=tab_style_sel))
return editortabs
I hope this explains how we have implemented this functionality.
With kind regards,
Claus