🏥 🏭 Working on the COVID-19 response in Canada? Plotly & the Canadian government can help you and your organization. Learn more and get in touch.

Simple slider with JupyterDash

I’m trying to draw a simple sine wave with a frequency controlled by a slider.
However, the examples I can find on dash sliders are much too complicated - I cannot find out how to make my slider control the graph.
My program below works - the only thing missing is to connect the slider value to the graph. I tried (just guessing) to add the following, but that didn’t work:

def update_graph(value):
return {
‘data’: [go.Scatter(
y=np.sin(2valuenp.pi*xp))]
}

But what is the correct syntax?

Here is my preliminary program

from jupyter_plotly_dash import JupyterDash
import numpy as np
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import plotly.graph_objs as go

xp=np.linspace(0, 1,num=99,endpoint=True)
freq=2
yp=np.sin(2freqnp.pi*xp)

app = JupyterDash(‘JupyterDashTest’)

app.layout = html.Div([
dcc.Graph(
id=‘Sine wave’,
figure={
‘data’: [
go.Scatter(
x=xp,
y=yp,
text=‘Actual point’,
mode=‘lines+markers’,
opacity=0.8,
marker={
‘size’: 5,
‘line’: {‘width’: 0.5, ‘color’: ‘blue’}
}
)
],
‘layout’: go.Layout(
xaxis={‘type’: ‘linear’, ‘title’: ‘time (s)’},
yaxis={‘title’: ‘y (m)’},
hovermode=‘closest’
)
}
),
html.Label(‘Frequency slider’),
dcc.Slider(id=‘betaslider’,min=1,max=10,value=2,step=0.1,marks={i: i for i in range(10)}),
html.Div(id=‘slider-output-container’)
])

@app.callback(
dash.dependencies.Output(‘slider-output-container’, ‘children’),
[dash.dependencies.Input(‘betaslider’, ‘value’)])
def update_output(value):
freq=value
return ‘Selected frequency is {} Hz’.format(value)

app

Poul Riis
Denmark

In order to connect the slider value to the graph, you should add another Output in the callback in order to update the graph with the new calculated yp using the new value selected by the slider, so you end up with two Output callbacks one to update the text and another to update the graph, which means your return function should return an array and your Output callbacks should also go inside an array like it’s shown in this example ( you only need to modify the callback and return function ) :

@app.callback(
[dash.dependencies.Output('slider-output-container', 'children'),dash.dependencies.Output('Sine wave', 'figure')], [dash.dependencies.Input('betaslider', 'value')])

def update_output(value):
    global freq # don't forget to add this line since its a global variable
    freq=value
    yp=np.sin(2*freq*np.pi*xp) # calculate the new value
    graph= { # update the graph content with the new value
        'data': [
        go.Scatter(
        x=xp,
        y=yp,
        text='Actual point',
        mode='lines+markers',
        opacity=0.8,
        marker={
        'size': 5,
        'line': {'width': 0.5, 'color': 'blue'}
        }
        )
        ],
        'layout': go.Layout(
        xaxis={'type': 'linear', 'title': 'time (s)'},
        yaxis={'title': 'y (m)'},
        hovermode='closest'
        )
        }
    return ['Selected frequency is {} Hz'.format(value),graph] # pass the two outputs in Array

and i think you should also modify the marks content of your slider, since using your code i was having an error and the slider was not showing while using {i:i for i in range(10)} unitl updated to {i:"$i" for i in range(10)}

Hopefully it will work.

Wonderful - it works! Thank you so much!!!
Now I can proceed to my next exercise: The SIR model with two sliders. Maybe I’ll need someone’s help again…

BTW, I didn’t change i:i to i:"$i" because the former version works fine on my computer and the latter only prints $i as a string, not the value.

Poul Riis

Ok cool ! good luck and don’t forget to close the topic if the provided answers what you looking for.

Super!

As next step I would like to add a slider for the amplitude. Again, I cannot find the exact syntax. See my program below.

Poul Riis

from jupyter_plotly_dash import JupyterDash
import numpy as np
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import plotly.graph_objs as go

xp=np.linspace(0, 1,num=99,endpoint=True)
freq=2
amp=1
yp=np.sin(2freqnp.pi*xp)

app = JupyterDash(‘JupyterDashTest’)

app.layout = html.Div([
dcc.Graph(
id=‘Sine wave’,
figure={
‘data’: [
go.Scatter(
x=xp,
y=yp,
text=‘Actual point’,
mode=‘lines+markers’,
opacity=0.8,
marker={
‘size’: 5,
‘line’: {‘width’: 0.5, ‘color’: ‘blue’}
}
)
],
‘layout’: go.Layout(
xaxis={‘type’: ‘linear’, ‘title’: ‘time (s)’},
yaxis={‘title’: ‘y (m)’},
hovermode=‘closest’
)
}
),
html.Label(‘Frequency slider’),
dcc.Slider(id=‘freqslider’,min=1,max=10,value=2,step=0.1,marks={i:i for i in range(10)}),
html.Label(‘Amplitude slider’),
dcc.Slider(id=‘ampslider’,min=0,max=5,value=1,step=0.1,marks={i:i for i in range(5)}),
html.Div(id=‘slider-output-container’)
])

@app.callback(
[dash.dependencies.Output(‘slider-output-container’, ‘children’),
dash.dependencies.Output(‘Sine wave’, ‘figure’)],
[dash.dependencies.Input(‘freqslider’, ‘value’)])

def update_output(value):
global freq,amp # don’t forget to add this line since its a global variable
freq=value
yp=np.sin(amp2freqnp.pixp) # calculate the new value
graph= { # update the graph content with the new value
‘data’: [
go.Scatter(
x=xp,
y=yp,
text=‘Actual point’,
mode=‘lines+markers’,
opacity=0.8,
marker={
‘size’: 5,
‘line’: {‘width’: 0.5, ‘color’: ‘blue’}
}
)
],
‘layout’: go.Layout(
xaxis={‘type’: ‘linear’, ‘title’: ‘time (s)’},
yaxis={‘title’: ‘y (m)’},
hovermode=‘closest’
)
}
return [‘Selected frequency is {} Hz’.format(value),graph] # pass the two outputs in Array

app

What you are missing is, whenever you have more than one Input in your Callback (in your case the two Sliders), you should make sure that you added everything to Callback and to the arguments of the Callback function (here called update_output), see in your code you have only one Input and one argument in the function, that’s why the second Slider is not working since it’s not yet linked, you need to add the second argument to the function and the second Input in Callback in order to fix it, like this :

@app.callback(
    [dash.dependencies.Output(‘slider-output-container’, ‘children’),
    dash.dependencies.Output(‘Sine wave’, ‘figure’)],
    [dash.dependencies.Input(‘freqslider’, ‘value’),dash.dependencies.Input(‘ampslider’, ‘value’)])

def update_output(value,amp):

then use the amp variable inside your function to update that yp and everything should.

Btw, you can remove the global two variables (freq and amp) if you don’t really need them since those two values are passed in the update_output function

Tried the following but now the sliders have no effect on the graph…?
BTW, in other programming environments you normally get error messages when you do something wrong. That seems not to be the case here…?

Poul Riis

from jupyter_plotly_dash import JupyterDash
import numpy as np
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import plotly.graph_objs as go

xp=np.linspace(0, 1,num=99,endpoint=True)
freq=2
amp=1
yp=np.sin(2freqnp.pi*xp)

app = JupyterDash(‘JupyterDashTest’)

app.layout = html.Div([
dcc.Graph(
id=‘Sine wave’,
figure={
‘data’: [
go.Scatter(
x=xp,
y=yp,
text=‘Actual point’,
mode=‘lines+markers’,
opacity=0.8,
marker={
‘size’: 5,
‘line’: {‘width’: 0.5, ‘color’: ‘blue’}
}
)
],
‘layout’: go.Layout(
xaxis={‘type’: ‘linear’, ‘title’: ‘time (s)’},
yaxis={‘title’: ‘y (m)’},
hovermode=‘closest’
)
}
),
html.Label(‘Frequency slider’),
dcc.Slider(id=‘freqslider’,min=1,max=10,value=2,step=0.1,marks={i:i for i in range(10)}),
html.Label(‘Amplitude slider’),
dcc.Slider(id=‘ampslider’,min=0,max=5,value=1,step=0.1,marks={i:i for i in range(5)}),
html.Div(id=‘slider-output-container’)
])

@app.callback(
[dash.dependencies.Output(‘slider-output-container’, ‘children’),
dash.dependencies.Output(‘Sine wave’, ‘figure’)],
[dash.dependencies.Input(‘freqslider’, ‘value’),dash.dependencies.Input(‘ampslider’, ‘value’)])

def update_output(value,amp):
yp=np.sin(amp2valuenp.pixp) # calculate the new value
graph= { # update the graph content with the new value
‘data’: [
go.Scatter(
x=xp,
y=yp,
text=‘Actual point’,
mode=‘lines+markers’,
opacity=0.8,
marker={
‘size’: 5,
‘line’: {‘width’: 0.5, ‘color’: ‘blue’}
}
)
],
‘layout’: go.Layout(
xaxis={‘type’: ‘linear’, ‘title’: ‘time (s)’},
yaxis={‘title’: ‘y (m)’},
hovermode=‘closest’
)
}
return graph # pass the two outputs in Array

app

Maybe because you are returning just one value instead of an array composed of number of Output you got ( in your case two Outputs ), you should either remove the first Output dash.dependencies.Output(‘slider-output-container’, ‘children’) or add array in return not only graph but [“something”,graph] (careful the order here is very important it should be at same order as the declared Outputs in Callback )

Thanks, again, it works!

Two minor things:

  1. How can I put the actual values of the frequency and the amplitudes above (or below) the actual points of the sliders? Or on the graph itself, like ‘f=2.5 Hz’?
  2. How can I fix the scale on the axes? Something like set_ymax=5?

Poul Riis

from jupyter_plotly_dash import JupyterDash
import numpy as np
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import plotly.graph_objs as go

xp=np.linspace(0, 1,num=99,endpoint=True)
freq=2
amp=1
yp=np.sin(2freqnp.pi*xp)

app = JupyterDash(‘JupyterDashTest’)

app.layout = html.Div([
dcc.Graph(
id=‘Sine wave’,
figure={
‘data’: [
go.Scatter(
x=xp,
y=yp,
text=‘Actual point’,
mode=‘lines+markers’,
opacity=0.8,
marker={
‘size’: 5,
‘line’: {‘width’: 0.5, ‘color’: ‘blue’}
}
)
],
‘layout’: go.Layout(
xaxis={‘type’: ‘linear’, ‘title’: ‘time (s)’},
yaxis={‘title’: ‘y (m)’},
hovermode=‘closest’
)
}
),
html.Label(‘Frequency slider’),
dcc.Slider(id=‘freqslider’,min=1,max=10,value=2,step=0.1,marks={i:i for i in range(10)}),
html.Label(‘Amplitude slider’),
dcc.Slider(id=‘ampslider’,min=0,max=5,value=1,step=0.1,marks={i:i for i in range(5)}),
html.Div(id=‘slider-output-container’)
])

@app.callback(
[dash.dependencies.Output(‘slider-output-container’, ‘children’),
dash.dependencies.Output(‘Sine wave’, ‘figure’)],
[dash.dependencies.Input(‘freqslider’, ‘value’),dash.dependencies.Input(‘ampslider’, ‘value’)])

def update_output(freq,amp):
yp=ampnp.sin(2freqnp.pixp) # calculate the new value
graph= { # update the graph content with the new value
‘data’: [
go.Scatter(
x=xp,
y=yp,
text=‘Actual point’,
mode=‘lines+markers’,
opacity=0.8,
marker={
‘size’: 5,
‘line’: {‘width’: 0.5, ‘color’: ‘blue’}
}
)
],
‘layout’: go.Layout(
xaxis={‘type’: ‘linear’, ‘title’: ‘time (s)’},
yaxis={‘title’: ‘y (m)’},
hovermode=‘closest’
)
}
return [freq,amp],graph # pass the two outputs in Array

app

Just found the answer to my second question so forget that one.

For the Slider marks you add something like this

dcc.Slider(id='freqslider',min=1,max=10,value=2,step=0.1,
marks={i:"f= "+str(i)+" Hz" for i in range(10)}),

For the Graph you can add inside go.Scatter

hovertemplate="%{x}"

In this example the value of X will be displayed when hover

That’s not what I want.
The program writes the actual values of the frequency and the amplitude beneath the sliders, not separated by any space. Rather, I would like those values to be put at the respective sliders above or beneath the actual position of each of the sliders.

Furthermore, I would like to know how to do something like matplotlibs annotate, for instance
annotate(r’\gamma=’+str(gamma),xy=(0.7,0.6),xycoords=‘axes fraction’)

Poul Riis

Another minor question:
How can I control the texts of the legends? I want something else than trace 0, trace 1 and trace 2.

Poul Riis

For the legends text controle, you can set it by adding name value, like mentioned here, for example :

go.Scatter(
x,
y,
..
name = "My awsome trace"
..)

if you didn’t set it it will write by default trace 0, trace 1 etc