Dynamic list of python dash core components - python

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)

Related

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.

Python Plotly Dash dropdown Adding a "select all" for scatterplot

I would like to add a select all for my dropdown, and make it the default when the app opens up, with the ability to then one by one remove capsule and also to unselect the select all button and select a group of capsule. I managed to find several ways that other people have use the select all option but none seems to work for my situation. Thank you!
Data looks this this.
Dash look like this.
import pandas as pd
import plotly.express as px # (version 4.7.0)
import plotly.graph_objects as go
import dash # (version 1.12.0) pip install dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
from dash.exceptions import PreventUpdate
app = dash.Dash(__name__)
server = app.server
df = pd.read_csv("tcd vs rh.csv")
print(df)
capsuleID = df['Capsule_ID'].unique()
print(capsuleID)
capsuleID_names = list(capsuleID)
print(capsuleID_names)
capsuleID_names_1 = [{'label': k, 'value': k } for k in sorted(capsuleID)]
capsuleID_names_2 = [{'label': '(Select All)', 'value': 'All'}]
capsuleID_names_all = capsuleID_names_1 + capsuleID_names_2
app.layout = html.Div([
html.H1("Web Application Dashboards with Dash", style={'text-align': 'center'}),
dcc.Dropdown(id="capsule_select",
options=capsuleID_names_all,
optionHeight=25,
multi=True,
searchable=True,
placeholder='Please select...',
clearable=True,
value=[''],
style={'width': "40%"}
),
html.Div([
dcc.Graph(id="the_graph")
]),
])
# -----------------------------------------------------------
#app.callback(
Output('the_graph', 'figure'),
[Input('capsule_select', 'value')]
)
def update_graph(capsule_chosen):
if capsule_chosen == 'all_values':
dff = df['Capsule_ID']
else:
dff = df[df['Capsule_ID'].isin(capsule_chosen)] # filter all rows where capsule ID is the capsule ID selected
scatterplot = px.scatter(
data_frame=dff,
x="tcd",
y="humidity",
)
scatterplot.update_traces(textposition='top center')
return scatterplot
# ------------------------------------------------------------------------------
if __name__ == '__main__':
app.run_server(debug=True)
If by default you want to select everything from df['Capsule_ID'] you can simply pass it to the value of your dropdown.
Then you can change your callback to something like this for the 'select all' functionality:
#app.callback(
Output("the_graph", "figure"),
Output("capsule_select", "value"),
Input("capsule_select", "value"),
)
def update_graph(capsules_chosen):
dropdown_values = capsules_chosen
if "All" in capsules_chosen:
dropdown_values = df["Capsule_ID"]
dff = df
else:
dff = df[df["Capsule_ID"].isin(capsules_chosen)]
scatterplot = px.scatter(
data_frame=dff,
x="tcd",
y="humidity",
)
scatterplot.update_traces(textposition="top center")
return scatterplot, dropdown_values
In addition to your check not working with the default dropdown value, you were doing this:
dff = df['Capsule_ID']
which means you were setting dff to a single column. This is not what want since 'tcd' and 'humidity' columns don't exist on df['Capsule_ID'].

Connecting graphs with dropdown (Plotly Dash)

I am building the app in which I want to plot two separate graphs from two data frames. I would like to use the dropdown to display only one graph per page based on each data frame (df,df1).
I followed the Plotly reference ​and couldn't reproduce it on my example.
This is my code so far that works (it displays both graphs on the same page, without dropdown):
import pandas as pd
import dash
import plotly.express as px
import dash_core_components as dcc
import dash_html_components as html
app = dash.Dash(__name__)
data = [['Blue',20],['Red ',12],['Green',33]]
df = pd.DataFrame(data,columns=['Color','Number'])
data1 = [['A',10,88],['B ',50,45],['C',25,120]]
df1 = pd.DataFrame(data1,columns=['Letter','Column1','Column2'])
fig = px.bar(df, x=df['Color'], y=df['Number'])
fig1 = px.line(x=df1['Letter'], y=df1['Column1'], color=px.Constant('Column1'),
labels=dict(x='Letter', y='Column1', color='Letter'))
fig1.add_bar(x=df1['Letter'], y=df1['Column2'], name='Letter')
app.layout = html.Div(children=[
html.H1(children='Colors and Letters', style={'text-align': 'center'}),
html.Div(children='Color', style={'text-align': 'center'}),
dcc.Graph(
id='example-graph',
figure=fig
),
html.Div(children='Letter', style={'text-align': 'center'}),
dcc.Graph(
id='example-graph1',
figure=fig1
)
])
if __name__ == '__main__':
app.run_server(debug=True)
This is what I would like to get:
What would be the best approach to do this?
Thanks in advance.
I don't have much experience with Dash, but here's a great answer and your code throughout. The point is to introduce a dropdown so that the initial value is displayed as fig. Depending on the return value of the callback function, I use the if function to switch the graph.
import pandas as pd
import plotly.express as px
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
app = dash.Dash(__name__)
data = [['Blue',20],['Red ',12],['Green',33]]
df = pd.DataFrame(data,columns=['Color','Number'])
data1 = [['A',10,88],['B ',50,45],['C',25,120]]
df1 = pd.DataFrame(data1,columns=['Letter','Column1','Column2'])
app.layout = html.Div(children=[
html.H1(children='Colors and Letters', style={'text-align': 'center'}),
html.Div(children='Color', style={'text-align': 'center'}),
html.Div([
html.Label(['Choose a graph:'],style={'font-weight': 'bold'}),
dcc.Dropdown(
id='dropdown',
options=[
{'label': 'graph1', 'value': 'graph1'},
{'label': 'graph2', 'value': 'graph2'},
],
value='graph1',
style={"width": "60%"}),
html.Div(dcc.Graph(id='graph')),
]),
])
#app.callback(
Output('graph', 'figure'),
[Input(component_id='dropdown', component_property='value')]
)
def select_graph(value):
if value == 'graph1':
fig = px.bar(df, x=df['Color'], y=df['Number'])
return fig
else:
fig1 = px.line(x=df1['Letter'], y=df1['Column1'], color=px.Constant('Column1'),
labels=dict(x='Letter', y='Column1', color='Letter'))
fig1.add_bar(x=df1['Letter'], y=df1['Column2'], name='Letter')
return fig1
if __name__ == '__main__':
app.run_server(debug=True)

To select all other checkboxes when checked the option (all) is clicked in dash

I need a logic which selects all checkboxes when the option All is checked in the checklist And the graph would change accordingly.
Or if we can disable the other checkboxes(x,y,x and x1, x2,x3) if the option All is Checked.
The code which I have used is as follows
import pandas as pd
import plotly.graph_objs as go
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
df = pd.DataFrame({'category' : ['A','A','A','B','B','B'],
'subcategory' : ['l', 'y', 'z', 'x1','y1','z1'],
'x_coord' : [1, 2,3,2,2,2],
'y_coord' : [1,3,2,1,3,2]})
# lists of categories
options1 = sorted(df["category"].unique().tolist())
# dictionary of category - subcategories
all_options = df.groupby("category")["subcategory"].unique()\
.apply(list).to_dict()
# we add as first subcategory for each category `all`
for k, v in all_options.items():
all_options[k].insert(0, 'all')
app = dash.Dash()
app.layout = html.Div([
dcc.Dropdown(
id='first-dropdown',
options=[{'label': k, 'value': k} for k in all_options.keys()],
value=options1[0]
),
html.Hr(),
dcc.Dropdown(id='second-dropdown'),
html.Hr(),
dcc.Graph(id='display-selected-values')
])
# the following two callbacks generate a dynamic 2 option
#app.callback(
dash.dependencies.Output('second-dropdown', 'options'),
[dash.dependencies.Input('first-dropdown', 'value')])
def set_2_options(first_option):
return [{'label': i, 'value': i} for i in all_options[first_option]]
#app.callback(
dash.dependencies.Output('second-dropdown', 'value'),
[dash.dependencies.Input('second-dropdown', 'options')])
def set_2_value(available_options):
return available_options[0]['value']
#app.callback(
dash.dependencies.Output('display-selected-values', 'figure'),
[dash.dependencies.Input('first-dropdown', 'value'),
dash.dependencies.Input('second-dropdown', 'value')])
def update_graph(selected_first, selected_second):
if selected_second == 'all':
ddf = df[df["category"]==selected_first]
else:
ddf = df[(df["category"]==selected_first) &
(df["subcategory"]==selected_second)]
fig = go.Figure()
fig.add_trace(
go.Scatter(x=ddf["x_coord"],
y=ddf["y_coord"],
marker = dict(size=15, color='green'),
mode='markers'))
return fig
if __name__ == '__main__':
app.run_server(debug=False)
If the All option is checked from the checkbox for category A then the subcategories can either be all selected(i.e checked) or the can be disbled and the graph must be shown for all the subcategories.
What calls and function is necessary to achieve these.
Thanks in Advance!!!

Construct and pass dataframes between callbacks after clicking a button in Dash

I am trying to add an item after a button is clicked. After clicking several buttons (A, B, C), the dataframe should have those information passed after the selected buttons are clicked. I want the passed dataframe to include df = pd.DataFrame( combination of A, B, C). However, at this stage, when I press the button, and write the corresponding information into a dataframe, the callback deletes all the information, and keeps only the last pressed button. I bypassed this limitation by writing corresponding information into csv but I wonder if there is a more elegant approach.
#app.callback(Output('intermediate_value', 'children'),
[Input('like_val_0', 'n_clicks_timestamp'),
Input('like_val_1','n_clicks_timestamp'),
Input('like_val_2', 'n_clicks_timestamp')
])
def construct_preferences(like_val_0, like_val_1, like_val_2):
listedTimestamps = [like_val_0, like_val_1, like_val_2]
listedTimestamps = [0 if v is None else v for v in listedTimestamps]
sortedTimestamps = sorted(listedTimestamps)
if like_val_0 == sortedTimestamps[-1]:
pickedButton = "like_val_0"
print (pickedButton)
sample_that_matches_address_0 = data[data['addresses'].str.contains(item_in_df[0].partition('.jpg')[0])]
sample_that_matches_address_0.to_csv('prefs.csv', columns=list(data), mode='a', header=False)
if like_val_1 == sortedTimestamps[-1]:
pickedButton = "like_val_1"
print (pickedButton)
sample_that_matches_address_1 = data[data['addresses'].str.contains(item_in_df[1].partition('.jpg')[0])]
sample_that_matches_address_1.to_csv('prefs.csv', columns=list(data), mode='a', header=False)
if like_val_2 == sortedTimestamps[-1]:
pickedButton = "like_val_2"
print (pickedButton)
sample_that_matches_address_2 = data[data['addresses'].str.contains(item_in_df[2].partition('.jpg')[0])]
sample_that_matches_address_2.to_csv('prefs.csv', columns=list(data), mode='a', header=False)
As you can see I write that item to csv and I wonder if there is any way to prevent callback from updating until I press another button; in my case predict.
#app.callback(Output('container', 'children'),
[Input('intermediate_value', 'children'),
Input('predict_val', 'n_clicks')])
def update_output_images (intermediate_value, n_clicks):
if (n_clicks!=0):
preferences = pd.read_csv('prefs.csv')
preferences.drop_duplicates(keep='first', inplace=True)
EDIT:
As the comment suggests, I update the example with a reproducible one
import json
import os
import pandas as pd
import random
import glob
from os import listdir
import flask
import pickle
import base64
from PIL import Image as PImage
import urllib.request
import numpy as np
import dash
import dash_html_components as html
import dash_core_components as dcc
from dash.dependencies import Input, Output
import dash_bootstrap_components as dbc
app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
server = app.server
data = pd.DataFrame(np.random.randn(100, 4), columns=list('ABCD'))
preferences = pd.DataFrame(columns=list(data))
row_1 = html.Div([(dbc.Row([
dbc.Col([
html.P('Random_val_0 is {}'.format(data['A'].loc[0])),
dbc.Button('Like This One', id='like_val_0', style={'vertical-align': "bottom"}, n_clicks=0),
]),
dbc.Col([
html.P('Random_val_1 is {}'.format(data['A'].loc[1])),
dbc.Button('Like This One', id='like_val_1', style={'vertical-align': "bottom"}, n_clicks=0),
]),
dbc.Col([
html.P('Random_val_2 is {}'.format(data['A'].loc[2])),
dbc.Button('Like This One', id='like_val_2', style={'vertical-align': "bottom"}, n_clicks=0),
])
]))])
predict = html.Div([
html.Button ('Predict', id='predict_val', n_clicks=0),
html.Div(id='images_new')
])
container_out = html.Div([html.Div(id='container')])
placeholder = html.Div([html.Div(id='intermediate_value', style={'display': 'none'})])
app.layout = dbc.Container(children=[row_1, html.Br(), html.Br(), placeholder, html.Br(), container_out, html.Br(), predict])
#app.callback(Output('intermediate_value', 'children'),
[Input('like_val_0', 'n_clicks_timestamp'),
Input('like_val_1','n_clicks_timestamp'),
Input('like_val_2', 'n_clicks_timestamp')
])
def construct_preferences(like_val_0,like_val_1, like_val_2):
listedTimestamps = [like_val_0, like_val_1, like_val_2]
listedTimestamps = [0 if v is None else v for v in listedTimestamps]
sortedTimestamps = sorted(listedTimestamps)
if like_val_0 == sortedTimestamps[-1]:
pickedButton = "like_val_0"
print (pickedButton)
sample_output_0 = data.loc[data['A'] == data['A'].loc[0]]
return sample_output_0.to_json()
#sample_that_matches_address_0.to_csv('prefs.csv', columns=list(data), mode='a', header=False)
if like_val_1 == sortedTimestamps[-1]:
pickedButton = "like_val_1"
print (pickedButton)
sample_output_1 = data.loc[data['A'] == data['A'].loc[1]]
return sample_output_1.to_json()
if like_val_2 == sortedTimestamps[-1]:
pickedButton = "like_val_2"
print (pickedButton)
sample_output_2 = data.loc[data['A'] == data['A'].loc[2]]
return sample_output_2.to_json()
#app.callback(Output('container', 'children'),
[Input('intermediate_value', 'children'),
Input('predict_val', 'n_clicks')])
def update_output_images (intermediate_value, n_clicks):
if (n_clicks!=0):
preferences = pd.read_json(intermediate_value)
print (preferences.shape)
if __name__ == '__main__':
app.run_server(host='0.0.0.0', debug=True)

Categories