Dash is an easy-to-use framework that can be used to build interactive Python web applicacations and dashboards. This lab is an introduction to the basics of Dash suitable for students near the end of CS1 and later.
This lab covers using Dash UI components and accompanies an associated assignment that introduces interactive data visualizations in Dash.
Dash Tutorial: https://dash.plotly.com/
Dash is a Python framework for creating interactive web-based applications.
If you have not done so already, install the dash
package with pip
. This lab also uses requests
, pandas
, and plotly
which you may also need to install.
python3 -m ensurepip --upgrade
python3 -m pip install requests
python3 -m pip install pandas
python3 -m pip install plotly
python3 -m pip install dash
Copy the following code into a file called dash_hello.py
and run it. When it runs, it should display a message like this in your terminal:
It appears that the program is stuck doing nothing, but it is actually running a little webserver right on your computer. You can see the web page that it is generating by opening a web browser and going to http://127.0.0.1:8050/
#these are the components we need from Sash
from dash import Dash, html, dcc
#this creates an object representing your application
#you should have this line for all Dash apps
app = Dash(__name__)
#the layout describes all of the pieces that display on the page
#the html.Div allows you to pass a list of things to display
app.layout = html.Div([
dcc.Markdown(
children =
"""
## Hello there!
This is my __Dash__ web application.
Isn't it _neat_?
"""
),
dcc.Markdown(
children = """
It can have multiple markdown cells.
* with
* bullets
* even
"""
)
])
#this will launch your application in a web server
#you should have this line for all Dash apps
if __name__ == '__main__':
app.run_server(debug=True)
When you open your browser, you should see the "Hello there!" message on the page that looks something like this:
Add a third Markdown element with a new message that you come up with.
children
- this is where you put the text you want displayed.<control>
key on your keyboard and hit the c
key.Dash has many other components besides just Markdown
.
The following code has an Input
component which allows users to type into an input text box.
Notice that each component may also be given an id
by assigning a value to the id
parameter when creating that object. This will be useful for referring to this component in other parts of the code.
It also has a callback function which is defined to run any time the value in the Input
textbox changes.
Notice the @app.callack
decorator which describes the inputs (i.e., parameters) and outputs (i.e., returns) of the function.
Update your dash_hello
app with this code and check it out. If your development web server is still running, you may not have to restart it to see the changes, but if anything goes wrong, you can always stop the server (with <control>
-c
) and re-run it.
from dash import Dash, html, dcc
from dash.dependencies import Input, Output
app = Dash(__name__)
app.layout = html.Div(children = [
dcc.Markdown(
id = "name_prompt",
children = "## Enter your name"
),
dcc.Input(
id = "name_input",
value = "" #initially there is no value for the user input
),
dcc.Markdown(
id = "output_message",
children = "" #initially the Markdown string is empty
)
])
@app.callback(
Output("output_message","children"),
Input("name_input","value"),
)
def my_cool_message_generator(user_name):
my_message = "Hello "+user_name+"!"
return my_message
if __name__ == '__main__':
app.run_server(debug=True)
Your application should now be interactive - try typing your name in the input box and watch the message update.
The following code introduces a new kind of component - the Radioitem
. Run this code and answer the following questions:
Radioitem
?from dash import Dash, html, dcc
from dash.dependencies import Input, Output
app = Dash(__name__)
app.layout = html.Div(children = [
dcc.Markdown(
id = "name_prompt",
children = "## Enter your name"
),
dcc.Input(
id = "name_input",
value = "" #initially there is no value for the user input
),
dcc.Markdown(
id = "major_prompt",
children = "What is your major?"
),
dcc.RadioItems(
id = "major_radio_items",
options = ["Computer Science","Data Analytics","Artificial Intelligence","Information Systems","Other"],
value = "Computer Science"
),
dcc.Markdown(
id = "name_output_message",
children = "" #initially the Markdown string is empty
),
dcc.Markdown(
id = "major_output_message",
children = "" #initially the Markdown string is empty
)
])
@app.callback(
Output("name_output_message","children"),
Input("name_input","value"),
)
def my_cool_message_generator(user_name):
my_message = "Hello "+user_name+"!"
return my_message
@app.callback(
Output("major_output_message","children"),
Input("major_radio_items","value"),
)
def message_for_major(user_major):
my_message = "Dash is great for "+user_major+" applications."
return my_message
if __name__ == '__main__':
app.run_server(debug=True)
Dropdown
is another component that is similar to Radioitems
. What do you think that is supposed to do differently? In the code above, change the Radioitems
to Dropdown
and run it.
You can find documentation and examples on how to use each of these components here:
Browse through the other components on the left side of the page to get an idea of what other options you have.
You can make multiple inputs affect the same output by listing multiple Input
objects with a single callback function.
Notice how the inputs are related to the parameters in the example below:
from dash import Dash, html, dcc
from dash.dependencies import Input, Output
app = Dash(__name__)
app.layout = html.Div(children = [
dcc.Markdown(
id = "name_prompt",
children = "## Enter your name"
),
dcc.Input(
id = "name_input",
value = "" #initially there is no value for the user input
),
dcc.Markdown(
id = "major_prompt",
children = "What is your major?"
),
dcc.RadioItems(
id = "major_radio_items",
options = ["Computer Science","Data Analytics","Artificial Intelligence","Information Systems","Other"],
value = "Computer Science"
),
dcc.Markdown(
id = "output_message",
children = "" #initially the Markdown string is empty
)
])
@app.callback(
Output("output_message","children"),
Input("name_input","value"),
Input("major_radio_items","value"),
)
def my_cool_message_generator(user_name,user_major): #the two params come from the two Input()
if user_name == "": #the user hasn't entered a name yet
return "" #so return blank for the output message
else:
my_message = user_name + " is learning about " + user_major
return my_message
if __name__ == '__main__':
app.run_server(debug=True)
Even though global variables are usually a bad idea, you can use them to retain data (so you don't have to reload from a file or Web API every time the callback function runs).
In this example DATA
is a global variable with COVID country data we worked with previously.
from dash import Dash, html, dcc
from dash.dependencies import Input, Output
import requests
#read the country-level data from the API
response = requests.get("https://api.covid19api.com/summary")
DATA = response.json()
app = Dash(__name__)
app.layout = html.Div(children = [
dcc.Markdown(
id = "country_prompt",
children = "## Enter the name of a country"
),
dcc.Input(
id = "country_input",
value = "" #initially there is no value for the user input
),
dcc.Markdown(
id = "output_message",
children = "" #initially the Markdown string is empty
)
])
@app.callback(
Output("output_message","children"),
Input("country_input","value"),
)
def display_covid_data(country_name):
data_display = ""
for curr_country in DATA["Countries"]:
if curr_country["Country"] == country_name:
data_display = "### "+country_name+"\n\n"
data_display += "New confirmed cases: "+str(curr_country["NewConfirmed"])+"\n\n"
data_display += "Total deaths: "+str(curr_country["TotalDeaths"])+"\n\n"
data_display += "as of "+DATA["Date"]+"\n\n"
return data_display
if __name__ == '__main__':
app.run_server(debug=True)
Run this and try typing in the name of some countries
Change the above application so that all of the countries listed in the records of DATA["Countries"]
appear as options in a dropdown list. Then allow the user to look up stats based on that dropdown instead of the input box.