Use a button to set values in drop down using dash - python

I was wondering is it possible to set multiple values to a drop down value using a button. What I want to be able to do is filter the drop down and then if the user presses the submit button, all options that contain the search_value are added as the drop down value. For example in my code, if you type in 'a' into the drop down search bar then 'Montreal' and 'San Francisco' appear. If the user were to then press the submit button both these options should be added as values to the drop down at once. At the moment I am able to save the most recent search value to user put in but I am having trouble dynamically altering the drop down value. Any help would be greatly appreciated!! Here is my code: FYI: I am running this in a Colab Notebook
**Correct answer
from jupyter_dash import JupyterDash # pip install dash
import dash_html_components as html
import dash_core_components as dcc
from dash.dependencies import Output, Input, State
import pandas as pd # pip install pandas
import plotly.express as px
import dash_table
import math
from dash import no_update
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = JupyterDash(__name__, external_stylesheets=external_stylesheets)
options = [
{"label": "New York City", "value": "NYC"},
{"label": "Montreal", "value": "MTL"},
{"label": "San Francisco", "value": "SF"},
]
app.layout = html.Div([
html.Div(id='output_data'),
html.Div(id="hidden-search-value", style={"visibility":"hidden"}),
html.Div(id="value-test", style={'color':'white'}),
html.Div(html.Button('Submit', id = 'button', n_clicks=0), style={'width':'49%', 'display':'inline-block'}),
html.Div(
dcc.Dropdown(id="my-multi-dynamic-dropdown", multi=True, clearable = True, options = options, style={'width':'49%', 'display':'inline-block'})),
])
#app.callback(
[Output("hidden-search-value", "children"),
Output("value-test", "children")],
Input(component_id="my-multi-dynamic-dropdown", component_property="search_value"),
)
# save previous search value in hidden variable
def update_hidden_value(value):
if value == "":
raise PreventUpdate
return value, "Select all values containing '{}'".format(value)
#app.callback(
Output("my-multi-dynamic-dropdown", "value"),
Input(component_id="button", component_property="n_clicks"),
[
State(component_id="output_data", component_property="children"),
State(component_id="hidden-search-value", component_property="children")
]
)
# submit button logic: use saved search value to update drop_down value
def update_multi_options(n_clicks, children, search_value):
# submit button pressed
if int(n_clicks) > 0:
values = []
for o in options:
# all values containing search value
if search_value.lower() in o["label"].lower():
values.append(o["value"])
else:
continue
return values
else:
return no_update
app.run_server(mode='inline', port=8030)

I believe you have the wrong ordering of your input arguments to your callback update_multi_options. It should be:
#app.callback(
Output("my-multi-dynamic-dropdown", "value"),
Input(component_id="button", component_property="n_clicks"),
State(component_id="output_data", component_property="children")
)
def update_multi_options(n_clicks, children):
Now, regarding the dynamic updating using the search feature, it turns out that whenever you click out of the search bar, the search_value is cleared. So I suggest perhaps tracking the search_value in a temporary hidden variable, if this is a low risk option for your application. An approach to do this would be the following:
app.layout = html.Div([
html.Div(id='output_data'),
html.Div(html.Button('Submit', id = 'button', n_clicks=0)),
html.Div(id="hidden-search-value", style={"visibility":"hidden"}),
html.Div([
"Multi dynamic Dropdown",
dcc.Dropdown(id="my-multi-dynamic-dropdown", multi=True, clearable = True, options = options),
]),
])
.
.
.
#app.callback(
Output("my-multi-dynamic-dropdown", "value"),
[
Input(component_id="button", component_property="n_clicks"),
],
[
State(component_id="output_data", component_property="children"),
State(component_id="hidden-search-value", component_property="children")
]
)
def update_multi_options(n_clicks, children, search_value):
# Make sure that the set values are in the option list, else they will disappear
# from the shown select list, but still part of the `value`.
if int(n_clicks) > 0:
values = []
for o in options:
if search_value in o["label"]:
values.append(o)
else:
continue
return values
else:
return no_update
#app.callback(
Output("hidden-search-value", "children"),
Input(component_id="my-multi-dynamic-dropdown", component_property="search_value"),
)
def update_hidden_value(value):
print(value, type(value))
if value == "":
raise PreventUpdate
return value
The only limitation with this approach would be that when you type something into the search and click submit, then you manually erase the search value and re-click on submit, it'll be stuck on the last typed in value. But, I don't see this being an issue, since you can overwrite this with another callback if this is undesirable.
In summary, you'll need to track the value you are typing in (eg. dcc.Dropdown.search_value) then trigger the filtering.

Related

input text in plotly dash based on a RadioItem picker

I have a code where i use plotly dash radio item to pick from different tests ('weekness', 'std', etc.) The main code is shown below.
def plots(self,):
df_lists = self.df_lists
plots_names = ['weakness', 'std', 'std_average', 'std_weak', 'p_mean', 'p_mean_average', 'p_median', 'p_median_average','range', 'p_range_average']
colors = {'background': '#111111', 'text': '#7FDBFF'}
from dash import Dash, dcc, html, Input, Output
names = self.names
app = Dash()
app.layout = html.Div( children=[
html.H4('Dieharder Tests Plots'),
dcc.Graph(id="bar-plot", style={'width':'80%', 'height': '70vh','display':'inline-block'}),
html.Div([ # this Div contains our output graph
dcc.Graph(id='side-plot', )], style={'width':'20%', 'height':'70vh','display':'inline-block'}),
html.P('Test Description'),
dcc.Markdown(id='test-explain', link_target="_blank", ),
html.P('Chose Plot Type'),
dcc.RadioItems(plots_names, plots_names[0], id="plot-picker", ),
html.P("Filter by test:"),
dcc.Dropdown(names, names[0], id="test-picker", multi = True),
])
Now, i want to display an explanation to the tests whenever one is picked using the radio item button. I am using the following #app.callback code: (note i am just displaying one text for the 'weakness' test)
#app.callback(
Output('est-explain', 'children'), [Input("test-picker", "value")])
def update_chart_info(picker_test):
if picker_test == 'weakness':
text = "This test counts how many weak results are there for each test for all rounds and return the number divided by the number of rounds"
return [text]
however, i am not getting anything on the dashboard when i run the code. I mean, the code works for other callbacks (that are not shown here) however it is not working for the callback for displaying the text. I tried different dcc components, such as html and markdown, but still can't see the explanation displayed.
any suggestions?
Thanks
There's a few issues that are probably preventing your callback from working as intended:
you have a typo in your callback arguments and Output('est-explain', 'children') should be Output('test-explain', 'children')
you mentioned you want the Dash app to update based on the selection from the Radioitem component, but Input("test-picker", "value") refers to the id for your Dropdown component, so you probably want to change it to Input("plot-picker", "value")
you'll also need a condition in your callback for when the Radioitem selection isn't "weakness" – otherwise you'll get something like UnboundLocalError: local variable 'text' referenced before assignment whenever someone selects another radioitem because update_chart_info won't know what text is supposed to be (for simplicity, I am returning an empty string whenever anything option besides "weakness" is selected, but you can modify this as needed)
The following should work:
#app.callback(
Output('test-explain', 'children'),
Input('plot-picker', 'value')
)
def update_chart_info(radio_selection):
if radio_selection == 'weakness':
text = "This test counts how many weak results are there for each test for all rounds and return the number divided by the number of rounds"
else:
text = ""
return [text]
And here is a small working example:
import plotly.graph_objects as go
from dash import Dash, dcc, html, Input, Output
plots_names = ['weakness', 'std', 'std_average', 'std_weak', 'p_mean', 'p_mean_average', 'p_median', 'p_median_average','range', 'p_range_average']
colors = {'background': '#111111', 'text': '#7FDBFF'}
## sample names
names = ['weakness','other']
app = Dash(__name__)
app.layout = html.Div(
[
html.H4('Dieharder Tests Plots'),
dcc.Graph(id="bar-plot", style={'width':'80%', 'height': '70vh','display':'inline-block'}),
html.Div([
# this Div contains our output graph
dcc.Graph(id='side-plot', )
], style={'width':'20%', 'height':'70vh','display':'inline-block'}),
html.P('Test Description'),
dcc.Markdown(id='test-explain', link_target="_blank", ),
html.P('Chose Plot Type'),
dcc.RadioItems(plots_names, plots_names[0], id="plot-picker", ),
html.P("Filter by test:"),
dcc.Dropdown(names, names[0], id="test-picker", multi = True),
]
)
#app.callback(
Output('test-explain', 'children'),
Input('plot-picker', 'value')
)
def update_chart_info(radio_selection):
if radio_selection == 'weakness':
text = "This test counts how many weak results are there for each test for all rounds and return the number divided by the number of rounds"
else:
text = ""
return [text]
app.run_server(debug=True)

Is it possible to dynamically load dcc.Dropdown option from values in Plotly Dash, dcc.store?

The application is built to store values from dcc.Input and display it on a dash_table. For the purpose of storing the input values dcc.store is being used.
Now I need to dynamically load the values of the first column i.e. "Student Name" in the dcc.Dropdown as options. Is there a possible way to share the data of the first column in dcc.store as an input to the options in dcc.Dropdown?
import dash
import dash_core_components as dcc
import dash_html_components as html
import dash_table
from dash.dependencies import Output, Input, State
user_key = 'Student Name'
# Setup table.
columns = ['Student Name',
'Age',
'Place',
'Grade']
table = dash_table.DataTable(columns=[{"name": column, "id": column} for column in columns],
data=[], id="table")
# Create app.
app = dash.Dash(prevent_initial_callbacks=True)
app.layout = html.Div([
html.Div([dcc.Input(id=column, value=column) for column in columns] +
[html.Button("Save", id="save"), dcc.Store(id="cache", data=[]), table]),
html.Div([
dcc.Dropdown(
id='demo-dropdown',
options=[
{'label': 'Mark', 'value': 'mrc'},
],
value='mrc'
)
])
])
#app.callback(Output("table", "data"),
[Input("save", "n_clicks")],
[State("table", "data")] +
[State(column, "value") for column in columns])
def update_table(n_clicks, data, *args):
record = {columns[i]: arg for i, arg in enumerate(list(args))}
try:
record_index = [record[user_key] for record in data].index(record[user_key])
data[record_index] = record
except ValueError:
data.append({columns[i]: arg for i, arg in enumerate(list(args))})
return data
if __name__ == '__main__':
app.run_server()
Yes you can load from dcc.Store() that has been populated with some dataframe. You will need a callback which takes in the Store's modified_timestamp property as the Input() and its data as a State() explained in Dash's official documentation). So something like this callback will do the trick:
#app.callback(
[Output("demo-dropdown", "options"), Output("demo-dropdown", "value")],
[Input("cache", "modified_timestamp")],
State("cache", "data"),
)
def update_dropdown_from_store(time_stamp, data):
return (
[
[{"label": i, "value": i} for i in list(df["Student Name"])],
df["Student Name"].iloc[0],
]
if data != []
else [[{"label": "Mark", "value": "mrc"}], "mrc"]
)

How to add value of a dropdown while button clicked in python dash?

My question is simple but I'm a beginner in Dash and UI development.
I create a Form like the below code:
I have a dropdown with two values ( Node and Edge), a "+" button, and a textarea. A user wants to select a value from the dropdown then click the + button. The selected value must add to the textarea. If the user clicks to + button again, the value must add again. My code update the value in the textarea ad doesn't add the elected values again. What's wrong with my code?
import dash_bootstrap_components as dbc
import dash_core_components as dcc
fLayout = dbc.FormGroup([
dbc.Form([
dcc.Dropdown(options=[{'label':'Node','value':'Node'},{'label':'Edge','value':'Edge'}],id='fcolumns'),
dbc.Button ('+',id='fcol_btn')
],inline=True),
dbc.Form([
dbc.Button(['='],id='fequal'),
dbc.Button (['>'],id='fgt')
],inline=True),
dbc.Form([
dbc.Textarea(id='ftxt'),
dbc.Button (['ok'],id='ftxt_btn')
],inline=True)
])
#app.callback(
Output("ftxt", "value"),
[Input("fcol_btn", "n_clicks"), Input("fequal", "n_clicks"),Input("fgt", "n_clicks")],
[State("fcolumns", "value")],
)
def filter_update(fcol_btn, fequal, fgt,fcolumns):
if fcol_btn>0:
return fcolumns
if fequal >0:
return "="
if fgt >0:
return ">"
Not sure that I fully understand what it is that you are trying to achieve. But the logic concerning the adding, the equal button etc. needs to be defined from you in the if statements according to what you want to get.
Find below a running version.
Generally: You should initiate the values when you set up a component otherwise you need to capture all the nones.
import dash
import dash_bootstrap_components as dbc
from dash import dcc
from dash import html
from dash.dependencies import Input, Output, State
app = dash.Dash(__name__)
app.layout = html.Div([
dcc.Dropdown(options=[{'label':'Node','value':'Node'},
{'label':'Edge','value':'Edge'}],id='fcolumns'),
dbc.Button ('+',id='fcol_btn',n_clicks=0),
dbc.Button(['='],id='fequal',n_clicks=0),
dbc.Button (['>'],id='fgt',n_clicks=0),
dbc.Textarea(id='ftxt',value=" "),
dbc.Button (['ok'],id='ftxt_btn',n_clicks=0),
html.Div(id='final_result',children=[])
])
#app.callback(
Output("ftxt", "value"),
[Input("fcol_btn", "n_clicks"),
Input("fequal", "n_clicks"),
Input("fgt", "n_clicks"),
Input("fcolumns", "value")],
State("ftxt", "value")
)
def do_something(fcol_btn,fequal,fgt,fcolumns,ftxt):
ctx = dash.callback_context
if ctx.triggered:
triggered_item = ctx.triggered[0]['prop_id'].split('.')[0]
else:
triggered_item = None
print('Triggered item was', triggered_item)
print(f'fcol_btn:{fcol_btn}, fequal:{fequal}, fgt:{fgt}, fcolumns:{fcolumns}')
operator = ''
if triggered_item != None:
if triggered_item == "fcol_btn":
operator= "+"
if triggered_item == "fequal":
operator= "="
if triggered_item == "fgt":
operator= ">"
if operator == '':
ftxt = str(ftxt)
else:
ftxt = str(ftxt)+operator+str(fcolumns)
return ftxt
#app.callback( Output("final_result", "children"),
Input("ftxt_btn", "n_clicks"),
State("ftxt", "value"))
def return_all(ftxt_btn,text):
if ftxt_btn>0:
print(text)
return [text]
if __name__ == '__main__':
app.run_server(debug=False, port=8050)

Return ‘href’ value on a click event in Python Dash

I have been trying to return the ‘href’ value from click event in python-Dash application. Below is my code snippet:
import dash
import dash_html_components as html
from dash.dependencies import Input, Output, State
app = dash.Dash(
__name__,
meta_tags=[{"name": "viewport", "content": "width=device-width, initial-scale=1"}],
)
def make_table(df, val):
table = []
for index, row in df.iterrows():
rows = []
html.Td([
html.Div([row["col1"]]),
html.A(id = 'link',href=row["file-link"], children=row["link-name"], target="_blank"),
])
table.append(html.Tr(rows))
return table
app.layout = html.Div([
html.Table(
id="table-element",
className="table__container",
)
],
className="six columns",
),
#app.callback(
Output("link", 'pathname'),
[Input('link', 'n_clicks')],
[State('link', 'href')]
)
def open_link(n_clicks, href):
enable_open_link(href) #enable_open_link function takes in the href string value (a local filesystem link) and opens it up in a new window.
#app.callback(
Output("table-element", 'children'),
[Input("input-1-submit", 'n_submit')],
[State('input-1-submit', 'value')]
)
def update_output(ns1,val):
table = make_table(df,val)
return table
This code works upto some extent i.e. it does returns a href value, but not the one I click. It always returns the last href value stored inside the html table.
Is there a way to fetch the href value when I click on the link?
I know I can use Jquery to fetch the correct href value… but I didn’t find a way to integrate javascript within the call-back function.
I think it's because all the links you'r creating have the same id ='link'.
You'll need to find a way around that. One possibilty would be to generate the id when creating it based on the index of your df, but then you'll also have to create the corresponding callbacks. This thread tells you how it could be done. plotly dash: create multiple callbacks (with loop?)
Taking a clue from thread provided by #Rudertier, I was able to get the solution.
Below is the updated code snippet:
def make_table(df, val):
table = []
for index, row in df.iterrows():
rows = []
html.Td([
html.Div([row["col1"]]),
html.A(id = 'link'+str(index),href=row["file-link"], children=row["link-name"], target="_blank"),
])
table.append(html.Tr(rows))
return table
app.layout = html.Div([
html.Table(
id="table-element",
className="table__container",
)
],
className="six columns",
),
links = ['link1','link2','link3','link4','link5','link6','link7','link8','link9','link10']
for link in links:
#app.callback(
Output('{}'.format(link), 'pathname'),
[Input('{}'.format(link), 'n_clicks')],
[State('{}'.format(link), 'href')]
)
def open_link(n_clicks, href):
enable_open_link(href) #enable_open_link function takes in the href string value (a local filesystem link) and opens it up in a new window.
#app.callback(
Output("table-element", 'children'),
[Input("input-1-submit", 'n_submit')],
[State('input-1-submit', 'value')]
)
def update_output(ns1,val):
table = make_table(df,val)
return table

Dash App call back append new entry in data frame and replace if matches with any previous entry

I am trying to build a dash app which takes input from users and append each entry in the data frame, but it's replacing the old entry and generating a new one. I am not sure what's wrong in the callback.
also trying to build a logic if the user entry matches with any row of data frame then only it will replace else append.
your help will be much appreaciated:
#app.callback(Output('save-query', 'children'),
[Input('save', 'n_clicks')],
[State('ad_account_id', 'value'),
State('app_id', 'value'),
State('access_token', 'value'),
State('app_secret', 'value'),
State('metrics', 'value'),
State('breakdown', 'value'),
State('start-date', 'date'),
State('end-date', 'date'),
State('save-as', 'value')
],
)
def save_query(clicks, ad_account_id, app_id, access_token, app_secret, metrics, breakdown,
start_date, end_date, save):
if clicks is not None:
my_ad_account = ad_account_id
my_app_id = app_id
my_access_token = access_token
my_app_secret = app_secret
my_metrics = metrics
my_breakdown = breakdown
my_start_date = start_date
my_end_date = end_date
my_save = str.lower(save)
data = [[my_save, my_ad_account, my_app_id, my_access_token, my_app_secret, my_metrics, my_breakdown,
my_start_date,
my_end_date]]
df = pd.DataFrame(data, columns=['report_name', 'ad_account', 'app_id', 'access_token',
'app_secret', 'metrics', 'breakdown',
'start_date', 'end_date'])
dff = df.append(df)
return html.Div([
dash_table.DataTable(
css=[{'selector': '.row',
'rule': 'margin: 0; white-space: inherit; overflow: inherit; text-overflow: inherit;'}],
id='table',
columns=[{"name": i, "id": i} for i in dff.columns],
data=dff.to_dict("rows"), )],
style={'margin-top': 30, 'display': 'inline-block', 'margin-left': 20, 'width': '100%'})
Since mutation of global variables is discouraged in Dash, the standard approach to enable access the to current data would be to add a component that holds the data as a State argument. It could be a separate component (such as a Store component) or simply the data table itself. Here is a small example demonstrating the latter approach,
import dash
import dash_core_components as dcc
import dash_html_components as html
import dash_table
from dash.dependencies import Output, Input, State
user_key = 'access_token'
# Setup table.
columns = ['report_name', 'ad_account', 'app_id', 'access_token']
table = dash_table.DataTable(columns=[{"name": column, "id": column} for column in columns], data=[], id="table")
# Create app.
app = dash.Dash(prevent_initial_callbacks=True)
app.layout = html.Div([dcc.Input(id=column, value=column) for column in columns] +
[html.Button("Save", id="save"), dcc.Store(id="cache", data=[]), table])
#app.callback(Output("table", "data"), [Input("save", "n_clicks")], [State("table", "data")] +
[State(column, "value") for column in columns])
def append(n_clicks, data, *args):
record = {columns[i]: arg for i, arg in enumerate(list(args))}
# If the record (identified by user_key) already exists, update it.
try:
record_index = [record[user_key] for record in data].index(record[user_key])
data[record_index] = record
# Otherwise, append it.
except ValueError:
data.append({columns[i]: arg for i, arg in enumerate(list(args))})
# Return the updated data.
return data
if __name__ == '__main__':
app.run_server()
As a sidenote, the prevent_initial_callbacks keyword is new as per Dash 1.12.0. As indicated by the name, it prevents the initial callback, thereby eliminating the need for the if clicks is not None: check.

Categories