Dash web applications have a dash application instance, usually named app
, and initiated like this:
app = dash.Dash(__name__)
Then, callbacks are added to the application using a callback
decorator:
@app.callback(...)
def my_function(...):
# do stuff.
In most of the tutorials you find, the callbacks are defined with all of the application layout in the app.py
. This of course is just the MWE way of doing things. In a real application, separating code to modules and packages would greatly improve readability and maintainability, but naively separating the callbacks to and layouts just results into circular imports.
What would be the correct way to separate callbacks and layouts from the app.py
in a single page app?
Here is a minimal (non-)working example with the problem
.
├── my_dash_app
│ ├── app.py
│ └── views
│ ├── first_view.py
│ └── __init__.py
└── setup.py
import setuptools
setuptools.setup(
name='dash-minimal-realworld',
version='1.0.0',
install_requires=['dash>=1.12.0'],
packages=setuptools.find_packages(),
)
import dash
from my_dash_app.views.first_view import make_layout
app = dash.Dash(__name__)
app.layout = make_layout()
if __name__ == '__main__':
app.run_server(debug=True)
from dash.dependencies import Input, Output
import dash_core_components as dcc
import dash_html_components as html
from my_dash_app.app import app
def make_layout():
return html.Div([
dcc.Input(id='my-id', value='initial value', type='text'),
html.Div(id='my-div')
])
@app.callback(Output(component_id='my-div', component_property='children'),
[Input(component_id='my-id', component_property='value')])
def update_output_div(input_value):
return 'You\'ve entered "{}"'.format(input_value)
Running python ./my_dash_app/app.py
results into circular dependency:
ImportError: cannot import name 'make_layout' from 'my_dash_app.views.first_view' (c:\tmp\dash_minimal_realworld\my_dash_app\views\first_view.py)
If a Dash app has multiple callbacks, the dash-renderer requests callbacks to be executed based on whether or not they can be immediately executed with the newly changed inputs. If several inputs change simultaneously, then requests are made to execute them all.
Currently multiple inputs will make the functionality not work. I've put multi=true for my dcc dropdown - hasn't worked successfully yet. This is the code I have used. This is an example of the data being inputted.
Dash callbacks allow you to update your Cytoscape graph via other components like dropdowns, buttons, and sliders. If you have used Cytoscape. js before, you have probably used event handlers to interactively update your graph; with Dash Cytoscape, we will instead use callbacks.
The dcc. Store component is used to store JSON data in the browser.
A little late to the party but I have found this to be a straightforward way of doing it:
def get_callbacks(app):
@app.callback([Output("figure1", "figure")],
[Input("child1", "value")])
def callback1(figure):
return
@app.callback([Output("figure2", "figure")],
[Input("child2", "value")])
def callback2(figure):
return
import dash
from callbacks import get_callbacks
import layout
app = dash.Dash(__name__)
app.layout = layout.layout
get_callbacks(app)
I don't think (but I might be wrong) that there's a correct way of doing it per se, but what you could do it have a central module (maindash.py
) around your startup code app = dash.Dash(__name__)
, and have different callbacks simply import app
from my_dash_app.maindash
. This would set up the callbacks in their own separate modules but re-use that one central module for the app
instance.
It's easiest to show an overview of it like this:
app.py
being the main script called to start everything up. maindash.py
is in charge of creating the main app instance. first_view.py
is where the decorators are defined to set up all the callbacks.
Here's the result:
.
├── my_dash_app
│ ├── app.py
│ ├── maindash.py
│ └── views
│ ├── first_view.py
│ └── __init__.py
└── setup.py
Since imports are re-used in Python, there's no real harm in doing from my_dash_app.maindash import app
several times from different other modules, such as event handlers and the main script. They'll share the same import instance - thus re-using the dash.Dash()
instance as well.
Just make sure you import the central module before setting up the handlers, and you should be good to go.
Here's the code snippets separated for testing:
from my_dash_app.maindash import app
from my_dash_app.views.first_view import make_layout
if __name__ == '__main__':
app.layout = make_layout()
app.run_server(debug=True)
import dash
app = dash.Dash(__name__)
from my_dash_app.maindash import app
from dash.dependencies import Input, Output
import dash_core_components as dcc
import dash_html_components as html
def make_layout():
return html.Div([
dcc.Input(id='my-id', value='initial value', type='text'),
html.Div(id='my-div')
])
@app.callback(Output(component_id='my-div', component_property='children'),
[Input(component_id='my-id', component_property='value')])
def update_output_div(input_value):
return 'You\'ve entered "{}"'.format(input_value)
I know it is too late to answer your question here, but maybe someone else will find it useful.
I wanted to be able to create callbacks in separate files, however I think that although importing an app from main dash module works well, it may be unclear for other people who read the code.
I created a callback manager used to initialize callbacks. This manager is attached to an app in the main app module.
from dataclasses import dataclass, field
from typing import Callable, List, Union
from dash.dependencies import handle_callback_args
from dash.dependencies import Input, Output, State
@dataclass
class Callback:
func: Callable
outputs: Union[Output, List[Output]]
inputs: Union[Input, List[Input]]
states: Union[State, List[State]] = field(default_factory=list)
kwargs: dict = field(default_factory=lambda: {"prevent_initial_call": False})
class CallbackManager:
def __init__(self):
self._callbacks = []
def callback(self, *args, **kwargs):
output, inputs, state, prevent_initial_call = handle_callback_args(
args, kwargs
)
def wrapper(func):
self._callbacks.append(Callback(func,
output,
inputs,
state,
{"prevent_initial_callback": prevent_initial_call}))
return wrapper
def attach_to_app(self, app):
for callback in self._callbacks:
app.callback(
callback.outputs, callback.inputs, callback.states, **callback.kwargs
)(callback.func)
import dash
from callback_manager import CallbackManager
callback_manager = CallbackManager()
@callback_manager.callback(
dash.dependencies.Output('label', 'children'),
[dash.dependencies.Input('call_btn', 'n_clicks')])
def update_label(n_clicks):
if n_clicks > 0:
return "Callback called!"
import dash
import dash_html_components as html
from callbacks import callback_manager
app = dash.Dash(__name__)
callback_manager.attach_to_app(app)
app.layout = html.Div([
html.Div(id="label"),
html.Button('Call callback', id='call_btn', n_clicks=0),
])
if __name__ == '__main__':
app.run_server(debug=True)
Note that you can have multiple files with callbacks and import them with as
keyword:
from callbacks1 import callback_manager as callback_manager1
from callbacks2 import callback_manager as callback_manager2
app = dash.Dash(__name__)
callback_manager1.attach_to_app(app)
callback_manager2.attach_to_app(app)
I believe doing it this way is more explicit.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With