I had a similar problem these days and solved it as shown below.
The value of a slider is talken as the input to a throttle routine which outputs to a hidden <div id='slider-output-throttle'>
only if some time has passed, otherwise I send no_update
, which is meanwhile defined for clientside callbacks as well.
The time of the last update is stored in the clientside namespace of the output.
The server side callback is then attached to the throttled output, which returns the output and redraws the figure.
import dash
import dash_html_components as html
import dash_core_components as dcc
from dash.dependencies import Input, Output
app = dash.Dash(__name__, suppress_callback_exceptions = True)
app.layout = html.Div([
dcc.Slider(
id='my-slider',
min=0,
max=20,
step=0.01,
value=10,
updatemode='drag'
),
html.Div(id='slider-output-throttle', hidden = True),
html.Div(id='slider-output-backend'),
dcc.Graph(id='my-graph')
])
app.clientside_callback(
"""
function throttle(value) {
let t_throttle = 150;
// ns refers to the clientside namespace
ns.t0 = ns.t0 || 0;
let dt = Date.now() - ns.t0;
if ( dt < t_throttle) {
return window.dash_clientside.no_update
} else {
ns.t0 = Date.now();
return value
}
}
""",
Output('slider-output-throttle', 'children'),
[Input('my-slider', 'value')])
@app.callback(
Output('slider-output-backend', 'children'),
[Input('slider-output-throttle', 'children')])
def update_output(value):
return 'Server says "{}"'.format(value)
@app.callback(Output("my-graph", "figure"), [Input("slider-output-throttle", "children")])
def update_output(value):
figure = {
'data': [
{'x': [1, 2, 3], 'y': [4, 1, 2], 'type': 'bar', 'name': 'SF'},
{'x': [1, 2, 3], 'y': [2, value, 5], 'type': 'bar', 'name': u'Montréal'},
],
'layout': { 'title': 'Dash Data Visualization' }
};
return figure
if __name__ == "__main__":
app.run_server(port=8070)
The same is also possible from Julia! Although clientside callbacks are not yet documented ( ) they work almost as in Python, except that always the same routine callback!()
is used. Decision between serverside and clientside is done by the magic of multiple dispatch.
using Dash
using DashHtmlComponents
using DashCoreComponents
app = dash(external_stylesheets = ["https://codepen.io/chriddyp/pen/bWLwgP.css"])
app.layout = html_div() do
dcc_graph(id = "my-graph", style = (width = "600px",) ),
html_div(dcc_slider(
id = "my-slider",
min = 0,
max = 20,
step = 0.1,
value = 10,
updatemode = "drag"
), style = (width = "600px",)),
html_div(id = "my-slider-throttle", hidden = true),
html_div(id = "my-slider-backend")
end
# clientside callback, if a function is passed as a (javascript) string
callback!("""
function throttle(value) {
let t_throttle = 150;
// ns refers to the clientside namespace
ns.t0 = ns.t0 || 0;
let dt = Date.now() - ns.t0;
if ( dt < t_throttle) {
return window.dash_clientside.no_update
} else {
ns.t0 = Date.now();
return value
}
}
""", app, Output("my-slider-throttle","children"), Input("my-slider", "value"))
# serverside callback, if a native julia function is passed
# (here by a pure function with `do` block syntax)
callback!(app, [Output("my-slider-backend", "children"), Output("my-graph", "figure")], Input("my-slider-throttle", "children")) do input_value
figure = (
data=[
(x = [1, 2, 3], y = [4, 1, 2], type = "bar", name = "SF"),
(x = [1, 2, 3], y = [2, input_value, 5], type = "bar", name = "Montréal")
],
layout = (title = "Dash Data Visualization",)
)
return input_value, figure
end
run_server(app, "0.0.0.0", 8080)