Plotly-Dash: Hide and Unhide Dropdown - python

I would like to launch my Dash with my dropdown hide and after selecting something on another dropdown unhide my first dropdown. Idk if you have any idea, maybe it's a mistake on my little script.
This is a little example :
import dash
from dash.dependencies import Input, Output
import dash_core_components as dcc
import dash_html_components as html
app = dash.Dash('example')
app.layout = html.Div([
dcc.Dropdown(
id = 'dropdown-to-show_or_hide-element',
options=[
{'label': 'Show element', 'value': 'on'},
{'label': 'Hide element', 'value': 'off'}
],
value = 'off'
),
# Create Div to place a conditionally visible element inside
html.Div([
# Create element to hide/show, in this case an 'Input Component'
dcc.Input(
id = 'element-to-hide',
placeholder = 'something',
value = 'Can you see me?',
)
], style= {'display': 'none'} # <-- This is the line that will be changed by the dropdown callback
)
])
#app.callback(
Output(component_id='element-to-hide', component_property='style'),
[Input(component_id='dropdown-to-show_or_hide-element', component_property='value')])
def show_hide_element(visibility_state):
if visibility_state == 'on':
return {'display': 'block'}
if visibility_state == 'off':
return {'display': 'none'}
if __name__ == '__main__':
app.server.run(debug=False, threaded=True)

By slightly changing your code (I changed the parameter that your function takes and using that same parameter in the if statement, this code seems to work). I also change the original style to block, which allows the element to be there, but only shown when on the show dropdown.
import dash
from dash.dependencies import Input, Output
import dash_core_components as dcc
import dash_html_components as html
app = dash.Dash('example')
app.layout = html.Div([
dcc.Dropdown(
id = 'dropdown-to-show_or_hide-element',
options=[
{'label': 'Show element', 'value': 'on'},
{'label': 'Hide element', 'value': 'off'}
],
value = 'off'
),
# Create Div to place a conditionally visible element inside
html.Div([
# Create element to hide/show, in this case an 'Input Component'
dcc.Input(
id = 'element-to-hide',
placeholder = 'something',
value = 'Can you see me?',
)
], style= {'display': 'block'} # <-- This is the line that will be changed by the dropdown callback
)
])
#app.callback(
Output(component_id='element-to-hide', component_property='style'),
[Input(component_id='dropdown-to-show_or_hide-element', component_property='value')])
def show_hide_element(value):
if value == 'on':
return {'display': 'block'}
if value == 'off':
return {'display': 'none'}
if __name__ == '__main__':
app.server.run(debug=False)

Related

Plotly dash creating columns based on user selection

I am working on a selection menu where the user can select the number of experiments they want to compare/see. Depending on this number I would like to adapt the number of columns that appear, being one for each experiment. In the image below an example of the menu with one experiment can be seen. However, I would like these dropdowns to duplicate if the user selects two experiments having them in side-to-side columns.
Here is an example of one column and one experiment:
The code I have so far is the following:
# Load Data
df = px.data.tips()
external_stylesheets = ["https://codepen.io/chriddyp/pen/bWLwgP.css"]
# Build App
app = JupyterDash(__name__, external_stylesheets=external_stylesheets)
app.layout = html.Div([
# first row - selecting the number of experiments
html.Div([
html.I("Select the number of experiments you want to compare (min - 2 and max - 10):"),
html.Br(),
# input box for the number of experiments
dcc.Input(id="num_exp" , type='number', min = 2, max = 10, placeholder="No of experiments"),
html.Div(id="output")
], style = {'width': '100%'}
),
# second row - where the experiments are organized in columns
html.Div([
html.Label([
html.I("X axis "),
dcc.Dropdown(
id = 'dd_axis',
clearable = False,
placeholder = 'Select property x-axis',
options=[
{'label': i, 'value': i}
for i in properties
],
style={
'width': '100%'
})
]),
html.Label([
html.I("Y Axis "),
dcc.Dropdown(
id = 'dd_yaxis',
clearable = False,
placeholder = 'Select property y-axis',
options=[ ],
disabled = False)
])
], style = {'display': 'inline-block', 'vertical-align': 'top', 'margin-left': '3vw', 'margin-top': '3vw', 'width': '50%'})
])
#app.callback(
Output("output", "children"),
# number of experiments input
Input("num_exp", "value"),
)
def update_output(test_type):
in_testtype = test_type
return u'{} experiments'.format(test_type)
def cb_render(*vals):
return " | ".join((str(val) for val in vals if val))
Can you help me adding more dropdown menus to the side and dividing the second row in columns based in the user selection?
Thank you!
You can do this by using dbc.Col and dbc.Row.
In your layout, have a Div and within it an empty dbc.Row that will be used to populate its children attribute with columns.
Then, in the callback that is triggered by the input box value, have a loop that returns the number of columns you want, with the relevant code for the dbc.Col and what you'd like to display in it.
Here's a worked-up example of your original code:
import dash
from dash import dcc, html
import dash_bootstrap_components as dbc
from dash.dependencies import Input, Output
app = dash.Dash(external_stylesheets=[dbc.themes.BOOTSTRAP])
properties = ["a", "b", "c"]
app.layout = html.Div([
# first row - selecting the number of experiments
html.Div([
html.I(
"Select the number of experiments you want to compare (min - 2 and max - 10):"),
html.Br(),
# input box for the number of experiments
dcc.Input(id="num_exp", value=2, type='number', min=2,
max=10, placeholder="No of experiments"),
html.Div([
dbc.Row([], id="output")
])
], style={'width': '100%'}
),
])
#app.callback(
Output("output", "children"),
# number of experiments input
Input("num_exp", "value"),
)
def update_output(test_type):
columns_list = []
letters = "xyzabcdefg"
for i in range(test_type):
columns_list.append(
dbc.Col([
html.Label([
html.I(f"{letters[i].upper()} axis "),
dcc.Dropdown(
id=f'{letters[i]}_axis',
clearable=False,
placeholder=f'Select property {letters[i]}-axis',
options=[
{'label': i, 'value': i}
for i in properties
],
style={
'width': '100%'
})
])
])
)
return columns_list
if __name__ == "__main__":
app.run_server(debug=True)

Dash -Callback with multiple inputs (input and dropdown) in Datatable

I'm new to Dash and Python. I have an app with a dropdown and a search input however I cannot get the callback to get both inputs to work. Currently either only the dropdown will work or just the input. I would like to first select the dropdown and then be able to search for text within the Datatable.
Below is my code.
import pandas as pd
import dash
import dash_table
import dash_core_components as dcc
import dash_bootstrap_components as dbc
import dash_html_components as html
import pathlib
from dash.dependencies import Input, Output
df = pd.read_csv('data.csv',encoding='cp1252')
env_list = df["Environment"].unique()
PAGE_SIZE = 20
app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP]
)
def description_card():
"""
:return: A Div containing logo.
"""
return html.Div(
id="description-card",
children=[
html.Img(id="logo", src=app.get_asset_url("logo.png"), height=80)
],style={'textAlign': 'center'}
)
def generate_control_card():
"""
:return: Descriptions
"""
return html.Div(
id="env-card",
children=[
html.H3("Welcome to the Package Catalog"),
]
)
app.layout = html.Div(
id="app-container",
children=[
# Banner
html.Div(
id="banner",
className="banner",
children=[description_card(),generate_control_card()
],
),
html.Div([
html.P([
html.Label("Select Environment", style={"background": "#F5F5F5"}),
dcc.Dropdown(
id='env-select',
options=[{'label': i, 'value': i} for i in env_list],
value=env_list[0],
)]),
html.Div([
html.P([
html.Label("Search for a package and description in the search box.", style={'display':'inline-block', 'background': '#F5F5F5'}),
dcc.Input(value='', id='filter-input', placeholder='Filter', debounce=True)
]),
dash_table.DataTable(css=[{'selector': '.row', 'rule': 'margin: 0'}],
id='datatable-paging',
columns=[
{"name": i, "id": i} for i in df.columns # sorted(df.columns)
],
style_header={
'backgroundColor': 'F5F5F5',
'fontWeight': 'bold'
},
page_current=0,
page_size=PAGE_SIZE,
page_action='custom',
sort_action='custom',
sort_mode='single',
sort_by=[]
)
]),
]),
])
#app.callback(
Output('datatable-paging', 'data'),
[
Input('datatable-paging', 'page_current'),
Input('datatable-paging', 'page_size'),
Input('datatable-paging', 'sort_by'),
Input('env-select', 'value'),
Input('filter-input', 'value')
])
def update_table(page_current, page_size, sort_by, environment_input, filter_string,
):
# Filter
dff = df[df.Environment==environment_input]
dff = df[df.apply(lambda row: row.str.contains(filter_string, regex=False).any(), axis=1)]
# Sort
if len(sort_by):
dff = dff.sort_values(
sort_by[0]['column_id'],
ascending=sort_by[0]['direction'] == 'asc',
inplace=False
)
return dff.iloc[
page_current * page_size:(page_current + 1) * page_size
].to_dict('records')
Change this line
dff = df[df.apply(lambda row: row.str.contains(filter_string, regex=False).any(), axis=1)]
to this
dff = dff[df.apply(lambda row: row.str.contains(filter_string, regex=False).any(), axis=1)]
In the line before the one shown above you apply your dropdown filter and store the filtered result as dff. So by using df instead of dff you're essentially discarding the dropdown filter result.

Callback error (Key error) updating scatter.children in Dash while selecting a column for plotting

I am getting message "Callback error updating scatter.children" while running Dash app (my first one). Debugging shows: KeyError 'Hours'. 'Hours is my column in dataframe. Column name is OK, and data are plotted correctly, but error message is always there. I do not understand why I am getting it. Any suggestions are very welcome.
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
import pandas as pd
import numpy as np
app = dash.Dash()
df_percent = pd.read_csv('data/neo_percent.csv')
app.layout = html.Div([
html.H1('Graph picker dashboard'),
html.H3('select a patient'),
dcc.Dropdown(
id='patient-selector',
options=[
{'label': 'R1', 'value': 'R1'},
{'label': 'R2', 'value': 'R2'},
{'label': 'R3', 'value': 'R3'}
],
value='none',
placeholder='Patient',
style=dict(
width='40%',
horizontalAlign='right',
color='blue'
)
),
html.Div(id='output-patient-selector'),
html.Div(id='scatter')
],
)
#app.callback(
Output('output-patient-selector', 'children'),
[Input('patient-selector', 'value')])
def update_header(value):
return 'You have selected "{}"'.format(value)
# Adding first graph
#app.callback(
Output('scatter', 'children'),
[Input('patient-selector', 'value')])
def update_output(value):
patient = pd.DataFrame()
if value == 'R1':
patient = df_percent.iloc[0:6]
elif value == 'R2':
patient = df_percent.iloc[6:12]
elif value == 'R3':
patient = df_percent.iloc[12:18]
return dcc.Graph(id='scatter1',
figure={
'data': [
{
'x': patient['Hours'],
'y': patient['pct_Lymphs'],
'type': 'line',
'text':patient['Name'],
'hoverinfo': 'text + y + x'
}
],
'layout': go.Layout(title='Patient Data',
xaxis={'title': 'time'},
yaxis={'title': 'percent'},
hovermode='closest')
}
)
if __name__ == '__main__':
app.run_server(debug=True)
The problem is the initial callback firing. Your dropdown value begins as 'none'. There is no condition for that in the callback, so your initial value for patient is still:
patient = pd.DataFrame()
There is no 'Hours' in that dataframe, so you get the error. Once you update the dropdown, it can hit one of your conditions to update the dataframe, and it works. Here is one way to solve that:
if value == 'R1':
patient = df_percent.iloc[0:6]
elif value == 'R2':
patient = df_percent.iloc[6:12]
elif value == 'R3':
patient = df_percent.iloc[12:18]
else:
return dcc.Graph(id='scatter1', figure={})

Changing the label in Dash bootstrap dcc.tab based on callback children

I am using dash core components to create a dash app that includes tabs that return tables based on callbacks. I'm trying to see if it's possible to change the label on the tab itself based on the callback, though it seems like the label will only accept a string, and not a callback with and id and children.
Right now the label of the tab simply says 'Car Type' (this is just a snippet of the code):
dbc.Row([
dbc.Col(dcc.Dropdown(id='car-types', multi=False,,
options=[{'label':x, 'value':x}
for x in sorted(car_list)]),
#width={'size': 5, "offset": 1, 'order': 1}
),
html.Div([
dcc.Tabs([
dcc.Tab(label='Car Type', children=
dbc.Col([
html.Div(id="table1"
)]
)
)
#app.callback(
[Output('table1', 'children'),
Output('car_label', 'children')],
[Input('car-types', 'value')],
[State('car_manuf', 'value')],
def update_table1(a,b):
code for table,
a = code for car_label string
return html.Div([dt.DataTable(),
), a
But what if I wanted it to say "Car Type SUV" or "Car Type Sedan" based on what the output 'car_label' says, how can I change the label of the tab to say that?
I tried something like:
html.Div([
dcc.Tabs([
dcc.Tab(label='Car Type ' + (children=[],id='car_label'), children=
dbc.Col([
html.Div(id="table1"
)]
)
But obviously that won't work. Any suggestions?
Maybe something like this with a dropdown and string formatting.
import dash
import dash_html_components as html
import dash_core_components as dcc
from dash.dependencies import Input, Output
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
app.layout = html.Div([
dcc.Tabs(id='tabs-example', value='tab-1', children=[
dcc.Tab(label='', id='first-tab', value='tab-1'),
dcc.Tab(label='', id='second-tab', value='tab-2'),
]),
dcc.Dropdown(id='car-types', multi=False, value='honda',
options=[{'label':'honda', 'value':'honda'},
{'label':'toyota', 'value':'toyota'}]
),
dcc.Dropdown(id='car-types2', multi=False, value='tesla',
options=[{'label': 'jeep', 'value': 'jeep'},
{'label': 'tesla', 'value': 'tesla'}]
),
html.Div(id='tabs-example-content')
])
#app.callback(Output('tabs-example-content', 'children'),
Input('tabs-example', 'value'))
def render_content(tab):
if tab == 'tab-1':
return html.Div([
html.H3('Tab content 1...')
])
elif tab == 'tab-2':
return html.Div([
html.H3('Tab content 2...')
])
#app.callback(
Output('first-tab', 'label'),
Input('car-types', 'value')
)
def update_label(name):
return f"Car Type: {name}"
#app.callback(
Output('second-tab', 'label'),
Input('car-types2', 'value')
)
def update_label(name2):
return f"Car Type: {name2}"
if __name__ == '__main__':
app.run_server(debug=True)

Dynamic list of python dash core components

I am using python dash and want to create a list of menus/forms that can be extended and reduced dynamically by clicking buttons to do so. Adding new forms/menus should add another identical form to the page (a list of forms/menus).
The following code allows the addition/removal of additional divs enclosing multiple dash core components, however, whenever I choose an option in one of the dropdowns or enter anything into one of the input fields, what I've chosen or entered disappears again.
import dash
import dash_core_components as dcc
import dash_html_components as html
step = html.Div(
children=[
"Menu:",
dcc.Dropdown(options=[{'label': v, 'value': v} for v in ['option1', 'option2', 'option3']]),
dcc.Input(placeholder="Enter a value ...", type='text', value='')
])
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
div_list = [step]
app.layout = html.Div(
children=[
html.H1(children='Hello Dash'),
html.Div(children=div_list, id='step_list'),
html.Button('Add Step', id='add_step_button', n_clicks_timestamp='0'),
html.Button('Remove Step', id='remove_step_button', n_clicks_timestamp='0')])
#app.callback(
dash.dependencies.Output('step_list', 'children'),
[dash.dependencies.Input('add_step_button', 'n_clicks_timestamp'),
dash.dependencies.Input('remove_step_button', 'n_clicks_timestamp')],
[dash.dependencies.State('step_list', 'children')])
def add_step(add_ts, remove_ts, div_list):
add_ts = int(add_ts)
remove_ts = int(remove_ts)
if add_ts > 0 and add_ts > remove_ts:
div_list += [step]
if len(div_list) > 1 and remove_ts > add_ts:
div_list = div_list[:-1]
return div_list
if __name__ == '__main__':
app.run_server(debug=True)
Can anybody explain to me what I’m doing wrong?
Thanks a lot!
Dash components need to have ids specified in order to save values after callbacks.
This example generating step as a function using random ids fixes the problem:
import dash
import dash_core_components as dcc
import dash_html_components as html
import numpy as np
def step():
return html.Div(
children=[
"Menu:",
dcc.Dropdown(options=[{'label': v, 'value': v} for v in ['option1', 'option2', 'option3']],id=str(np.random.randn())),
dcc.Input(placeholder="Enter a value ...",id=str(np.random.randn()))
])
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
app.layout = html.Div(
children=[
html.H1(children='Hello Dash'),
html.Div(children=[step()], id='step_list'),
html.Button('Add Step', id='add_step_button', n_clicks_timestamp=0),
html.Button('Remove Step', id='remove_step_button', n_clicks_timestamp=0)])
#app.callback(
dash.dependencies.Output('step_list', 'children'),
[dash.dependencies.Input('add_step_button', 'n_clicks_timestamp'),
dash.dependencies.Input('remove_step_button', 'n_clicks_timestamp')],
[dash.dependencies.State('step_list', 'children')])
def add_step(add_ts, remove_ts, div_list):
add_ts = int(add_ts)
remove_ts = int(remove_ts)
if add_ts > 0 and add_ts > remove_ts:
div_list += [step()]
if len(div_list) > 1 and remove_ts > add_ts:
div_list = div_list[:-1]
return div_list
if __name__ == '__main__':
app.run_server(debug=True)

Categories