'Children' Property in Dash Python html - python

I'm pretty confused by what 'children' even does in Dash html. Why does it even exist? Why would you use it? I tried reading the documentation, but it hasn't been very helpful.
With reference to the block of code below:
What does the children property even do in the first line?
Couldn't you replace children with something like 'figure' for the plots?
Block of Code:
app.layout = html.Div(children=[
# TODO1: Add title to the dashboard
html.H1("Airline Dashboard by CK", style = {'text-align':'center'}),
# REVIEW2: Dropdown creation
# Create an outer division
html.Div([
# Add an division
html.Div([
# Create an division for adding dropdown helper text for report type
html.Div(
[
html.H2('Report Type:', style={'margin-right': '2em'}),
]
),
# TODO2: Add a dropdown
dcc.Dropdown(id = 'input-type',
options = [{'label':'Yearly Airline Performance Report', 'value': 'OPT1'},
{'label':'Yearly Average Flight Delay Statistics', 'value': 'OPT2'}],
multi = False,
placeholder = 'Select a Report Type',
style={'width': '80%', 'padding': '3px', 'font-size': '20px', 'text-align-last': 'center'}
)
# Place them next to each other using the division style
], style={'display': 'flex'}),
# Add next division
html.Div([
# Create an division for adding dropdown helper text for choosing year
html.Div(
[
html.H2('Choose Year:', style={'margin-right': '2em'})
]
),
dcc.Dropdown(id='input-year',
# Update dropdown values using list comphrehension
options=[{'label': i, 'value': i} for i in year_list],
placeholder="Select a year",
style={'width': '80%', 'padding': '3px', 'font-size': '20px', 'text-align-last': 'center'}),
# Place them next to each other using the division style
], style={'display': 'flex'}),
]),
# Add Computed graphs
# REVIEW3: Observe how we add an empty division and providing an id that will be updated during callback
html.Div([], id='plot1'),
html.Div([
html.Div([], id='plot2'),
html.Div([], id='plot3')
], style={'display': 'flex'}),
# TODO3: Add a division with two empty divisions inside. See above disvision for example.
html.Div([
html.Div([], id='plot4'),
html.Div([], id='plot5')
], style = {'display':'flex'})
])
# Callback function definition
# TODO4: Add 5 ouput components
#app.callback(
[Input(component_id='input-type', component_property='value'),
Input(component_id='input-year', component_property='value')],
# REVIEW4: Holding output state till user enters all the form information. In this case, it will be chart type and year
[Output("plot1", 'children'), Output("plot2", "children"),
Output("plot3", "children"), Output("plot4", "children"),
Output("plot5", "children")
])

From this page of the docs:
The children property is special. By convention, it's always the first attribute which means that you can omit it: html.H1(children='Hello Dash') is the same as html.H1('Hello Dash'). Also, it can contain a string, a number, a single component, or a list of components.
Some components, such as html.Div and html.P, accept a value for their children prop. Others, such as dcc.Graph or dcc.Dropdown do not, and require other props in order to function properly.
As #KarlKnechtel mentioned in his comment, when one component is a child of another, it represents a nesting of the first component inside the other. The following are analogous:
In Dash:
html.Div(
children=[
html.H1('This is some text'),
html.P('This is also some text'),
]
)
In HTML:
<div>
<h1>This is some text</h1>
<p>This is also some text</p>
</div>
I hope that answers your questions.
Edit:
Adding style after children to this html.Div would allow you to change the styles of the Div, which may impact the styles of the components nested inside it, but that is not the purpose of the children prop. As the docs mention, you can either explicitly set children= whatever, or you can pass the same value in first, with no explicit key word argument, and Dash will treat that as the children prop. Either way, behind the scenes, the component is still receiving a value for its children property.
The purpose of the children property is to allow users to nest components, just as we do in raw HTML. Without the children prop, it would not be possible to do things like group related items together by containing them in the same parent element (ex. putting navigation items inside a top navigation bar).

Related

Dash dropdown wont reset values once x clicked

I created 2 Dash dropdowns where one dropdown (the lower) is based on the selection in the first dropdown (the upper)
The selection and everything work fine.
However, when I click the X button to remove all the options from the area-dropdown, it does remove all the options but still, city-dropdown values are the same as when I clicked the X button.
why won't it reset?
This is the code I'm using:
#app.callback(
Output("city-dropdown", "options"),
Input("area-dropdown", "value"),
)
def update_city_dropdown(areas):
if areas is None or None in areas or areas == []:
return []
_area_codes = area_codes['area'][area_codes['name'].isin(areas)]
cities = city_codes['name'][city_codes['area'].isin(_area_codes)]
return [{'label': city, 'value': city} for city in cities]
where:
area_dropdown = dcc.Dropdown(
options=area_codes['name'],
placeholder="Select an area",
multi=True,
style=DROPDOWN_STYLE,
id='area-dropdown'
)
city_dropdown = dcc.Dropdown(
placeholder="Select a city",
options=[],
multi=True,
style=DROPDOWN_STYLE,
id='city-dropdown'
)
Thank you
The city_dropdown.value isn't updated, so upon re-selection of an area, the cities show-up again (since those values are now again in the refreshed options).
You can add the following callback:
#app.callback(
Output("city-dropdown", "value"),
Input("area-dropdown", "value")
)
def clear_city_dropdown(value):
return []
Note that the above will reset your cities when switching areas.

How to save input from a dbc.Modal and write to mySql database in Python Dash app?

I have created a button when clicked will pop up a dash boostrap component, dbc.Modal with a couple input fields. Now I'm wondering how can I save this input information and write to a database like MySQL.
I know how to read/write to a DB in the context of a regular html button in Dash with a callback function that triggers when button clicked. But lost on how to use with a modal and callback. I couldn't find any examples online and documentations don't show how to use a modal to save input data and connect with external DB. Please advise. Below is a segment of my code:
Edited Code:
dbc.Modal(
[
dbc.ModalHeader("Add Favorite Faculty"),
dbc.ModalBody(
dbc.Form(
[
dbc.Row(
[
dbc.Label("Enter Name:", id="fav-name"),
dbc.Input(type="text", placeholder="name")
]
),
html.Br(),
dbc.Row(
[
dbc.Label("Enter Email:", id="fav-email"),
dbc.Input(type="email", placeholder="email")
],
),
html.Br(),
dbc.Row(
[
dbc.Label("Enter Comment:", id="fav-comment"),
dbc.Input(type="text", placeholder="comment")
],
),
html.Br(),
dbc.Button("Submit", id='fav-submit', color="primary"),
],
)
),
dbc.ModalFooter(
dbc.Button("Close", color="secondary", id="close-add-faculty", className="ml-auto")
),
],
id="fav-modal",
is_open=False,
And my basic callback function that just opens and closes the modal. Essentially I want to save the input fields when a submit button is clicked and write to a databse.
# prompt modal
#app.callback(
Output("fav-modal", "is_open"),
[
Input("add-faculty-button", "n_clicks"),
Input("close-add-faculty", "n_clicks")
],
[State("fav-modal", "is_open")]
)
def toggle_modal(n1, n2, is_open):
if n1 or n2:
return not is_open
return is_open
# write to DB
#app.callback(
Output("written", "children"),
[
State("fav-name", "value"),
State("fav-email", "value"),
State("fav-comment", "value")
],
[
Input("fav-submit", "n_clicks")
]
)
def write_to_db(n_clicks, name, email, comment):
if n_clicks is not None:
....
An image of what it looks like:
The fact that these inputs are inside a modal doesn't matter in this case. You can still set up callbacks using those component IDs to get the value of each input, and then use that to pass along to your database.
You may want to consider adding a "submit" button or something like that, which would serve as the trigger for your callback, and take these inputs as State. Otherwise, your callback would fire every time the input updates, and you would likely send lots of unwanted values to your database.
Edit:
You need to add IDs to all the dbc.Input components and the submit button in order to hook them up to the callback. Here's an example.
# layout code
dbc.Input(type="text", placeholder="name", id='name-input')
# end of layout
#app.callback(
Output("some-notification-contaienr", "children"),
[
Input("submit-button", "n_clicks"),
],
[
State("name-input", "value"),
State("email-input", "value"),
State("comment-input", "value"),
]
)
def toggle_modal(submit_clicks, name, email, comment):
if not submit_clicks:
raise PreventUpdate
# some code here to send your stuff to the database
# ...
return 'Successfully submitted.'

Define Dependent Dictionaries in Python

I am working on an NLP project analyzing the words spoken by characters in The Office. Part of this project involves making a network diagram of which characters talk to each other for a given episode.
This will be shown in a Dash app by allowing a user to select dropdowns for 4 parameters: season, episode, character1, and character2.
Here is a relevant snippet of my code so far:
#Import libraries
import pandas as pd
import numpy as np
import dash
import dash_core_components as dcc
import dash_html_components as html
import dash_bootstrap_components as dbc
from dash.dependencies import Input, Output, State
#Load data
sheet_url = 'https://docs.google.com/spreadsheets/d/18wS5AAwOh8QO95RwHLS95POmSNKA2jjzdt0phrxeAE0/edit#gid=747974534'
url = sheet_url.replace('/edit#gid=', '/export?format=csv&gid=')
df = pd.read_csv(url)
#Set parameters
choose_season = df['season'].unique()
choose_episode = df['episode'].unique()
choose_character = ['Andy','Angela', 'Darryl', 'Dwight', 'Jan', 'Jim','Kelly','Kevin','Meredith','Michael','Oscar','Pam','Phyllis','Roy','Ryan','Stanley','Toby']
#Define app layout
app = dash.Dash()
server = app.server
app.layout = html.Div([
dbc.Row([
dbc.Col(
dcc.Dropdown(
id='dropdown1',
options=[{'label': i, 'value': i} for i in choose_season],
value=choose_season[0]
), width=3
),
dbc.Col(
dcc.Dropdown(
id='dropdown2',
options=[{'label': i, 'value': i} for i in choose_episode],
value=choose_episode[0]
), width=3
),
dbc.Col(
dcc.Dropdown(
id='dropdown3',
options=[{'label': i, 'value': i} for i in choose_character],
value=choose_character[0]
), width=3
),
dbc.Col(
dcc.Dropdown(
id='dropdown4',
options=[{'label': i, 'value': i} for i in choose_character],
value=choose_character[1]
), width=3
)
])
])
if __name__=='__main__':
app.run_server()
In order to have this work efficiently, I would like to have the following dependencies in the dropdown menus:
1.) The selection of the first dropdown menu updates the dropdown menu
ie: Season updates possible episodes
2.) The selection of the first two dropdown menus updates the 3rd and 4th dropdown menus
ie: Season, Episode updates possible characters (if a character was not in that episode, they will not appear)
3.) The selection of the third dropdown menu updates the fourth dropdown menu
ie: If a character is selected in the third dropdown menu, they can not be selected in the fourth (can't select the same character twice)
I understand one way to do this is to make a massive season to episode dictionary and then an even larger season to episode to character dictionary.
I've already made the code to process the season to episode dictionary:
#app.callback(
Output('dropdown2', 'options'), #--> filter episodes
Output('dropdown2', 'value'),
Input('dropdown1', 'value') #--> choose season
)
def set_episode_options(selected_season):
return [{'label': i, 'value': i} for i in season_episode_dict[selected_season]], season_episode_dict[selected_season][0]
I can definitely build these dictionaries, but this seems like a really inefficient use of time. Does anyone know of a way to build these dictionaries with just a few lines of code? Not sure how to approach building these in the easiest way possible. Also, if you have an idea for a better way to approach this problem, please let me know that too.
Any help would be appreciated! Thank you!
I think I see what you're asking about now. Something like this should get you a basic dictionary, which you could then modify for the options param for the dropdowns.
df = pd.read_csv(url)
season_episode_character_dictionary = {}
for season in df['season'].unique.tolist():
df_season = df[df['season'].eq(season)]
season_episode_character_dictionary[season] = {}
for episode in df_season['episode'].unique.tolist():
df_episode = df_season[df_season['episode'].eq(episode)]
characters = df_episode['characters'].unique.tolist()
season_episode_character_dictionary[season][episode] = characters

Plotly: Dash button with live graph not working

i developed python dash based application for monitoring. as a part of the project i want to display live graph and change the live graph value based on user input. i am stuck in this part. the live graph getting update when i start typing in input box (eg:temp) the graph keep on updating for each letter like t,te,tem,temp. so i created a button to submit the input value. still the graph updating for each letter.
the code for the same:
app = dash.Dash(__name__)
app.layout = html.Div(
[
dcc.Input(id='input-value', value='example', type='text'),
html.Button('Submit', id="submit-button"),
dcc.Graph(id='live-graph', animate=False),
dcc.Interval(
id='graph-update',
interval=1*1000
),
]
)
call back function is like below
#app.callback(Output('live-graph', 'figure'),
[Input('submit-button','n_clicks')],
state=[State(component_id='sentiment_term', component_property='value')],
events=[Event('graph-update', 'interval')])
def update_graph_scatter(n_clicks, input_value):
conn = sqlite3.connect('database.db')
c = conn.cursor()
df = pd.read_sql("SELECT * FROM table WHERE colume LIKE ? ORDER BY unix DESC LIMIT 1000", conn ,params=('%' + input_value+ '%',))
df.sort_values('unix', inplace=True)
df['date'] = pd.to_datetime(df['unix'],unit='ms')
df.set_index('date', inplace=True)
X = df.index
Y = df.column
data = plotly.graph_objs.Scatter(
x=X,
y=Y,
name='Scatter',
mode= 'lines+markers'
)
return {'data': [data],'layout' : go.Layout(xaxis=dict(range= [min(X),max(X)]),
yaxis=dict(range=[min(Y),max(Y)])}
Note: If i removed the interval the button start working.but i want to be live update as well as button
You could use an intermediate component like a div to save the input from the text field and update that div only on button click.
So you would have
app = dash.Dash(__name__)
app.layout = html.Div([
dcc.Input(id='input-value', value='example', type='text'),
html.Div(['example'], id='input-div', style={'display': 'none'}),
html.Button('Submit', id="submit-button"),
dcc.Graph(id='live-graph', animate=False),
dcc.Interval(
id='graph-update',
interval=1*1000
),
])
Now you update the div only on button click:
#app.callback(Output('input-div', 'children'),
[Input('submit-button', 'n_clicks')],
state=[State(component_id='input-value', component_property='value')])
def update_div(n_clicks, input_value):
return input_value
And the Graph always uses the the div content to query your database (either when the interval triggers or the div changes):
#app.callback(Output('live-graph', 'figure'),
[Input('graph-update', 'interval'),
Input('input-div', 'children')])
def update_graph_scatter(n, input_value):
...
Are you sure the updates on each letter are because of the input? Sounds like the interval updates the graph with the not finished input you are typing.
P.S.: I guess you have a name discrepancy between you Input and State id.

passing value from 1 page to another in dash plotly

I am trying to pass input value to another with Dash Plot.
The main page has a list of strategies :
First_page = html.Div([
html.Div([dcc.Dropdown(id='strategy', options=[{'label': i, 'value': i} for i in [strat1, start2]], multi=True)], value = [strat1]),
html.Div([dcc.Input(id='date_start', type='text', value='1990-01-01')]),
html.Button('Start Calculation', id='button'),
])
And my pages 1 and 2 are the following :
for i in range(2) :
layout = html.Div([
html.Div([dcc.Dropdown(id='strategy'+str(i), options=[{'label': i, 'value': i} for i in [strat1, start2]]])], value = strat+str(i)),
html.Div([dcc.Input(id='date_start'+str(i), type='text', value='1990-01-01')]),
html.Button('Start Calculation', id='button'+str(i)),
+ other options not listed in main page
])
The goal is the following : when the user is on the main page he can choose the list of strategies he want to see, as well as the starting date.
Then when he press the button, it should open pages for each strategy selected (In the example we have 2 strategies so I should have 2 pages : localhost:port/page-strat1 and localhost:port/page-strat2) where the graph shown on each page starts with the selected date.
I succeed to open the pages localhost:port/strat1 and/or the page localhost:port/strat2 depending on the list of selected strategies.
list_event = [Event('button', 'click')]
list_state = [State('strategy', 'value')]
#app.callback(Output('hidden-div', 'children'),events=list_event, state=list_state)
def create_callback_share(datestart, list_strategies_used):
for _strat in list_strategies_used:
time.sleep(1)
webbrowser.open('http://localhost:port/page-'+_strat)
But I do not succeed to send the selected date. I try the following callback :
for i in range(2) :
#app.callback(Output('date_start'+str(i), 'value'),inputs=[Input('date_start', 'value')])
def set_display_children(selected_element):
return "{}".format(selected_element)
But the page localhost:port/strat1 and localhost:port/strat2 allow open with the default value which is '1990-01-01' even if I enter another value in the main page.
Is it possible to send value from one page to the other ?
Use dcc.Store() as in the answer here:
Plotly Dash Share Callback Input in another page with dcc.Store
And here is the Dash documentation:
https://dash.plotly.com/dash-core-components/store

Categories

Resources