Suppose the following setting:
I have lots of different tables with lots of data in each of them, but I only want to display one at a time. I do this with a dropdown that will display a certain table depending on the selected dropdown value. Of course these tables have to be pre-computed and the only thing the callback of the dropdown does is showing the selected table and hiding all others like this:
@app.callback(
[Output('table_{}'.format(i), 'style') for i in range(NUM_TABLES)],
[Input('dropdown', 'value']
)
def show_selected_table(value):
return [
{'display': 'block'} if value == 'table_{}'.format(i) else {'display': 'none'}
for i in range(NUM_TABLES)
]
Below that I have a button that is supposed to do some calculation with the data from only the currently displayed table when clicked.
The problem:
What is the most elegant way to figure out which table to pull the data from? (In terms of performance, speed, memory, etc.)
And is there a recommended way of handling such things?
Here are some of the possible ways that I can think of (but don’t know if efficient or good):
1. Have a callback with the button press as Input, the dropdown value as state and also all tables data as state like this:
@app.callback(
Output('some_output', 'children'),
[Input('button', 'n_clicks')],
[State('dropdown', 'value')] +
[State('table_{}'.format(i), 'data') for i in range(NUM_TABLES)]
)
def do_some_calculation(clicks, selected_table, table_0, ....):
# find out which table is currently displayed with the dropdown value
# do calculation with the respective table_{} data
Problems with this approach and advantages:
Having to send the data of ALL tables over the network when really only one single one is needed. Considering that each table has LOTS of data, this is super inefficient.
The good thing about this though is that only a single callback is needed.
2. Have the initial callback figure out which table to pull data from and then forward a signal to another callback that PreventsUpdate when not being the selected one:
@app.callback(
[Output('signal_{}'.format(i), 'children') for i in range(NUM_TABLES)],
[Input('button', 'n_clicks')],
[State('dropdown', 'value')]
)
def filter_signal(clicks, value):
# raise PreventUpdate when clicks None
return [
'signal' if value == table else None
for table in TABLES
]
for i in range(NUM_TABLES):
app.callback(
[Output('forward_sig_{}'.format(i), 'children'),
Input('signal_{}'.format(i), 'children')])
def func(signal, rest...):
if signal is None:
raise PreventUpdate
return 'signal' # forward a signal again
for i in range(NUM_TABLES):
app.callback(
[Input('forward_sig_{}'.format(i), 'children')],
[State('table_{}'.format(i), 'data')]
)
def func(signal, data, rest...):
if signal is None:
raise PreventUpdate
# finally do the calculation on the table data
...
return result
Problems with this approach and advantages:
Lots, lots of ‘unnecessary’ callbacks and chains of callbacks.
The advantage compared to approach 1. though is that only the data of the selected table is sent to the server, not all.
3. Have a unique button component for each table that will also be hidden/displayed according to the dropdown value:
@app.callback(
[Output('table_{}'.format(i), 'style') for i in range(NUM_TABLES)] +
[Output('button_{}'.format(i), 'style') for i in range(NUM_TABLES)],
[Input('dropdown', 'value']
)
def show_selected_table(value):
res = [
{'display': 'block'} if value == 'table_{}'.format(i) else {'display': 'none'}
for i in range(NUM_TABLES)
]
return res + res
for i in range(NUM_TABLES)
app.callback(
[Input('button_{}'.format(i), 'n_clicks')],
[State('table_{}'.format(i), 'data')]
)
def func(...):
# the calculation...
return result
Problems with this approach and advantages:
Just like approach 2. multiple callbacks are needed. Instead of chains of callbacks now more button components have to be introduced so hiding/displaying callback gets a little heavier.
The advantage to 1. and 2. is that again only the selected table data is sent to the server and less callbacks are needed overall.