Through a couple of simple patterns, Dash abstracts away all of the technologies and protocols that are required to build an interactive web-based application.
is the
for
# ui.R (or app.R)
library(shiny)
selectInput(
inputId = 'my-shiny-dropdown',
label = strong('My Label'),
choices = c('Foo', 'Bar', 'Baz'),
selected = 'Foo',
)
# app.py
import dash_core_components as dcc
dcc.Dropdown(
id='my-dash-dropdown',
options=[
{'label': 'Foo', 'value': 'FOO'},
{'label': 'Bar', 'value': 'BAR'},
{'label': 'Baz', 'value': 'BAZ'},
],
value='FOO',
)
# ui.R (or app.R)
library(shiny)
sliderInput(
inputId = 'my-shiny-slider',
label = 'Number of observations:',
min = 0,
max = 1000,
value = 500,
step = 5,
),
# app.py
import dash_core_components as dcc
dcc.Slider(
id='my-dash-slider',
min=0,
max=1000,
step=5,
value=500,
)
pip install dash==0.19.0 # The core dash backend
pip install dash-renderer==0.11.1 # The dash front-end
pip install dash-html-components==0.8.0 # HTML components
pip install dash-core-components==0.14.0 # Supercharged components
pip install plotly --upgrade # Latest Plotly graphing library
app.layout = html.Div(children=[
html.H1(children='Hello Dash'),
html.Div(children='''
Dash: A web application framework for Python.
'''),
dcc.Graph(
id='example-graph',
figure={
'data': [
{'x': [1, 2, 3], 'y': [4, 1, 2], 'type': 'bar', 'name': 'SF'},
{'x': [1, 2, 3], 'y': [2, 4, 5], 'type': 'bar', 'name': u'Montréal'},
],
'layout': {
'title': 'Dash Data Visualization'
}
}
)
])
A hierarchical tree of components. Dash's layout is serialized as JSON and served to Dash's front-end.
# dash/dependencies.py
class Output:
def __init__(self, component_id, component_property):
self.component_id = component_id
self.component_property = component_property
class Input:
def __init__(self, component_id, component_property):
self.component_id = component_id
self.component_property = component_property
class State:
def __init__(self, component_id, component_property):
self.component_id = component_id
self.component_property = component_property
class Event:
def __init__(self, component_id, component_event):
self.component_id = component_id
self.component_event = component_event
What a component is, in relationship with another component
app = Dash(name=app_name, server=Flask(app_name))
@app.callback(
output=Output('my-table', 'rows'),
inputs=[Input('button', 'n_clicks')],
state=[State('my-slider', 'value'), State('my-dropdown', 'value')],
events=[Event('interval-component', 'interval')])
)
def update_table(n_clicks, slider_value, dropdown_value):
pass
Dash callbacks are functional (they don't contain any state) so you can easily add memoization caching.
from flask_caching import Cache
cache = Cache(app.server, config={
'CACHE_TYPE': 'filesystem', 'CACHE_DIR': 'cache',
'CACHE_THRESHOLD': 10, 'CACHE_DEFAULT_TIMEOUT': 30})})
@app.callback(
output=Output('memoized-children', 'children'),
inputs=[Input('memoized-dropdown', 'value')])
@cache.memoize()
def render(value):
children = 'Selected {}'.format(value)
return children
If 10 Output components (e.g. several graphs, a table, labels) depend on the same Input component (e.g. a Dropdown), then 10 requests are made at the same time in parallel.
None of these requests block each other: each component will update when its request finishes.
Dash callbacks should never mutate variables outside of their scope!
Perform expensive operations (e.g. downloading, querying data) in the global scope of the app instead of within the callback functions.
import dash
import dash_core_components as dcc
import dash_html_components as html
app = dash.Dash()
app.layout = html.Div([
# represents the URL bar, doesn't render anything
dcc.Location(id='url', refresh=False),
dcc.Link('Navigate to "/"', href='/'),
dcc.Link('Navigate to "/page-2"', href='/page-2'),
# content will be rendered in this element
html.Div(id='page-content')
])
@app.callback(
output=dash.dependencies.Output('page-content', 'children'),
inputs=[dash.dependencies.Input('url', 'pathname')],
)
def display_page(pathname):
children = html.Div([html.H3('You are on page {}'.format(pathname))])
return children
external_css = [
# dash stylesheet
'https://codepen.io/chriddyp/pen/bWLwgP.css',
'https://fonts.googleapis.com/css?family=Lobster|Raleway',
]
external_js = [
# google analytics with the tracking ID for this app
'https://codepen.io/jackdbd/pen/rYmdLN.js'
]
for js in external_js:
app.scripts.append_script({'external_url': js})
for css in external_css:
app.css.append_css({'external_url': css})
# in ipython, for example
>>> import dash_core_components as dcc
>>> help(dcc.Graph)
class Graph(dash.development.base_component.Component)
| A Graph component.
|
|
| Keyword arguments:
| - id (string; required)
| - clickData (dict; optional): Data from latest click event
| - hoverData (dict; optional): Data from latest hover event
| - clear_on_unhover (boolean; optional): If True, `clear_on_unhover` will clear the `hoverData` property
| when the user "unhovers" from a point.
| If False, then the `hoverData` property will be equal to the
| data from the last point that was hovered over.
...
# dash_core_components/__init__.py
_current_path = _os.path.dirname(_os.path.abspath(__file__))
_components = _dash.development.component_loader.load_components(
_os.path.join(_current_path, 'metadata.json'),
'dash_core_components'
)
_this_module = _sys.modules[__name__]
_js_dist = [
# some js files here
]
_css_dist = [
# some css files here
]
for component in _components:
setattr(_this_module, component.__name__, component)
setattr(component, '_js_dist', _js_dist)
setattr(component, '_css_dist', _css_dist)
Components are created when you instantiate the app.
# dash/development/component_loader.py
from .base_component import generate_class
def load_components(metadata_path, namespace='default_namespace'):
"""Load React component metadata into a format Dash can parse.
Usage: load_components('../../component-suites/lib/metadata.json')
Keyword arguments:
metadata_path -- a path to a JSON file created by
[`react-docgen`](https://github.com/reactjs/react-docgen).
Returns:
components -- a list of component objects with keys
`type`, `valid_kwargs`, and `setup`.
"""
...
The jsonified React.js components are loaded.
# dash/development/base_component.py
def generate_class(typename, props, description, namespace):
# Dynamically generate classes to have nicely formatted docstrings,
# keyword arguments, and repr
# Insired by http://jameso.be/2013/08/06/namedtuple.html
...
React.js components are translated into Python classes that include: argument validation, Python docstring, type, basic sets of methods.
def js_to_py_type(type_object):
js_type_name = type_object['name']
# wrapping everything in lambda to prevent immediate execution
js_to_py_types = {
'array': lambda: 'list',
'bool': lambda: 'boolean',
...
}
...
@jackdbd
giacomodebidda.com
c = '''class {typename}(Component):
"""{docstring}
"""
def __init__(self, {default_argtext}):
self._prop_names = {list_of_valid_keys}
self._type = '{typename}'
self._namespace = '{namespace}'
self.available_events = {events}
self.available_properties = {list_of_valid_keys}
for k in {required_args}:
if k not in kwargs:
raise Exception(
'Required argument `' + k + '` was not specified.'
)
super({typename}, self).__init__({argtext})
def __repr__(self):
if(any(getattr(self, c, None) is not None for c in self._prop_names
if c is not self._prop_names[0])):
return (
'{typename}(' +
', '.join([c+'='+repr(getattr(self, c, None))
for c in self._prop_names
if getattr(self, c, None) is not None])+')')
else:
return (
'{typename}(' +
repr(getattr(self, self._prop_names[0], None)) + ')')
'''