I often find a problem is managing a large number of inputs, states and outputs in a callback.
Below is an implementation of a CB
class where you define the inputs, states and outputs as class members and in the process()
function read their values as variables, also setting the outputs as variables, and the callback function is auto-generated.
I wonder if anyone has done something similar, or preferably better?
"""try dataclass that allows better definitions for large callbacks"""
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State, DashDependency
from typing import Dict, Any
class CallbackException(Exception):
pass
app = dash.Dash('example_app')
app.layout = html.Div([
html.Div(html.Button('Change the dictionary', id='button')),
dcc.Store(id='store', storage_type='session', data={'a': 1, 'b': 2}),
html.Br(),
html.Div(id='my-output'),
])
class CB:
def process(self):
pass
def get_callback(self, app):
def get_dependencies(typ):
return {k:v for k, v in self.__class__.__dict__.items() if isinstance(v, typ)}
inputs: Dict[str, Input] = get_dependencies(Input)
states: Dict[str, State] = get_dependencies(State)
outputs: Dict[str, Output] = get_dependencies(Output)
all_inputs = inputs | states
@app.callback(list(outputs.values()), list(inputs.values()), list(states.values()))
def wrapper(*args):
n_args = len(args)
len_inputs = len(all_inputs)
if n_args != len_inputs:
raise CallbackException(f'expected number of args "{n_args}" to equal numbers of states/inputs: "{len_inputs}"')
for a, k in zip(args, all_inputs):
setattr(self, k, a)
self.process()
return [getattr(self, k) for k in outputs]
class ExampleCallback(CB):
out_children: Any = Output('my-output', 'children')
out_store: Any = Output('store', 'data')
n_clicks: Any = Input('button', 'n_clicks')
state_store: Any = State('store', 'data')
def process(self):
print(f'Store: {self.state_store}, n clicks: {self.n_clicks}')
self.out_store = self.state_store
self.out_store['a'] += 1
self.out_children = str(self.out_store)
ExampleCallback().get_callback(app)
if __name__ == '__main__':
app.run_server(debug=True, use_reloader=False)