I'm currently developing simple dashboard for my department using Dash Plotly and having a lot of issues understanding toggling of modals depending on multiple inputs.
Here is my script:
layout = dbc.Container([
dbc.Row([
dbc.Col([
html.P('Select value:'),
dcc.Dropdown(['Value1','Value2','Value3'],
id='dropdown',
style={'height': '30px', 'width': '200px','align-items': 'center', 'justify-content': 'center'},
multi=True),
html.Br(),
html.P('Select observed period:'),
dcc.DatePickerRange(
id='date-picker-range',
min_date_allowed= date(2022, 1, 1),
initial_visible_month= date(2023, 1, 1),
end_date= datetime.today().date(),
display_format='DD.MM.YYYY'),
html.Br(), html.Br(),
dbc.Button("Execute process", id='button'),
html.Br()
],
width=3,
style={"background-color": "#f8f9fa",'align-items': 'center'},
),
dbc.Col(children=[
html.Div(id='output'),
]),
])
])
#app.callback(
Output("modal", "is_open"),
[Input("close", "n_clicks")],
[State("modal", "is_open")]
)
def toggle_modal(n1,is_open):
if n1:
return not is_open
return is_open
#app.callback(
Output('output','children'),
[Input('button', 'n_clicks'),
Input('dropdown','value'),
Input('date-picker-range', 'start_date'),
Input('date-picker-range', 'end_date')]
)
def update_output(n_clicks,
values,
start_date,
end_date):
ctx = dash.callback_context
trigger_id = ctx.triggered[0]["prop_id"].split(".")[0]
if trigger_id == "button":
if start_date is not None:
if values is not None:
return 'Works!'
else: # no value is selected
return dbc.Modal(
[
dbc.ModalHeader("Warning"),
dbc.ModalBody("No value selected for values."),
dbc.ModalFooter(
dbc.Button(
"Close",
id="close"
)
),
],
id="modal",
is_open=True
)
else:#no start date is selected
return dbc.Modal(
[
dbc.ModalHeader("Warning"),
dbc.ModalBody("No value selected for date."),
dbc.ModalFooter(
dbc.Button(
"Close",
id="close",
)
),
],
id="modal", is_open=True
)
else:
raise PreventUpdate
What I want to achieve/understand is to set up callback that will work in such way:
if Value is None and click button - raise modal
if Value is not None but start date is not selected and click button - raise modal
if value is not None and click button execute code !
if i change value in value dropdown or change date and click button - execute code!
if i remove all values from values dropdown and click button - raise modal again (here my code stops working and I don't know how to handle it)
It only work until i choose values for input but when I remove them to None again, modals do not raise.
I hope I've described my problem sufficiently so you can help me out.
Thanks a lot!
Related
I'm creating a dashboard with Dash on which I want a variable number of graphs with associated dropdowns underneath each other. The dropdowns control an aspect of the graph (how it's sorted, but this is unimportant). Here is the code:
from dash import html, dcc
from dash.dependencies import Output, Input, State, MATCH
import dash_bootstrap_components as dbc
from app.plots import get_product_breakdown_bar_chart
from .selectors import get_product_selection_checklist, get_impact_parameter_selection_checklist, get_product_to_sort_on_dropdown, DEFAULT_PRODUCT_CHECKLIST_ID, DEFAULT_IMPACT_PARAMETER_CHECKLIST_ID, DEFAULT_PRODUCTS, DEFAULT_IMPACT_PARAMETERS
def get_selectors_pane():
selectors_row = dbc.Row([
dbc.Col(
[get_product_selection_checklist()],
width = 6
),
dbc.Col(
[get_impact_parameter_selection_checklist()],
width = 6
)
])
labels_row = dbc.Row([
dbc.Col(
[dbc.Label("Products:", html_for = DEFAULT_PRODUCT_CHECKLIST_ID)],
width = 6
),
dbc.Col(
[dbc.Label("Impact parameters: ", html_for = DEFAULT_IMPACT_PARAMETER_CHECKLIST_ID)],
width = 6
)
])
return html.Div([
labels_row,
selectors_row
])
saved_sorted_on_states = {}
selected_products = DEFAULT_PRODUCTS
def render_graph_rows(selected_products, selected_impact_parameters):
def sov(impact_parameter):
if impact_parameter in saved_sorted_on_states:
if saved_sorted_on_states[impact_parameter] in selected_products:
return saved_sorted_on_states[impact_parameter]
else:
saved_sorted_on_states.pop(impact_parameter)
return selected_products[0]
else:
return selected_products[0]
rows = []
for s_ip in selected_impact_parameters:
sort_on_dropdown_id = {"type": "sort-on-dropdown", "index": s_ip}
ip_graph_id = {"type": "impact-parameter-graph", "index": s_ip}
rows.append(
html.Div([
dbc.Row([
dbc.Col([dbc.Label("Sort on:", html_for = sort_on_dropdown_id)], width = 2),
dbc.Col([get_product_to_sort_on_dropdown(sort_on_dropdown_id, sov(s_ip))], width = 10)
]),
dbc.Row([
dbc.Col([
dcc.Graph(
id = ip_graph_id,
figure = get_product_breakdown_bar_chart(s_ip, selected_products, sov(s_ip))
)
], width = 12)
])
])
)
return rows
content_layout = html.Div(
id = "content",
children = [
get_selectors_pane(),
html.Div(
id = "graph-grid",
children = render_graph_rows(DEFAULT_PRODUCTS, DEFAULT_IMPACT_PARAMETERS)
)
],
style = {
"margin-left": "14rem",
"margin-right": "2rem",
"padding": "2rem 1rem",
}
)
def register_callback(app):
def sort_graph_callback(value, index):
global saved_sorted_on_states
saved_sorted_on_states[index] = value
return (get_product_breakdown_bar_chart(index, selected_products, value), )
app.callback(
[Output({"type": "impact-parameter-graph", "index": MATCH}, "figure")],
[Input({"type": "sort-on-dropdown", "index": MATCH}, "value")],
[State({"type": "sort-on-dropdown", "index": MATCH}, "id")]
)(sort_graph_callback)
def new_master_selection_callback(s_ps, s_ips):
global selected_products
selected_products = s_ps
return (render_graph_rows(s_ps, s_ips), )
app.callback(
[Output("graph-grid", "children")],
[Input(DEFAULT_PRODUCT_CHECKLIST_ID, "value"), Input(DEFAULT_IMPACT_PARAMETER_CHECKLIST_ID, "value")]
)(new_master_selection_callback)
The problem is that the sort_graph_callback defined on line 86 never gets called. This callback is supposed to connect dynamically added graphs with dynamically added dropdowns associated to them. But when I select a different option in such a dropdown nothing happens to the associated graph and the callback doesn't get called at all. I know this from setting breakpoints in them. I have verified that the correct id's are assigned to the rendered graph and dropdown components.
(Please note that I'm registering the callbacks in a peculiar way due to code organization reasons. I have verified that this is not the cause of the issue)
I have no clue anymore how to debug this issue. In my development environment pattern matching callback examples from the official documentation work just fine. Is there anything I'm missing?
Thank you so much in advance,
Joshua
I have a Mysql table from where I am retrieving the data from the table and updating the datatable with those records. Now after listing them when I click on add row button it should add a row and let the user edit the row.
app.layout = dbc.Container([
dcc.Store(id='store'),
# dashboard header on row - 0
dbc.Row([dbc.Col(html.H1('Requirements', className='text-center mb-4'),
width=12)]),
dbc.Row([
html.Label('1. Select Table name:'),
dbc.Col([
dcc.Dropdown(id='table_dropdown', multi=False, value='',
options=[{'label': x, 'value': x} for x in tables_list], )
], xs=12, sm=12, md=12, lg=3, xl=3),
html.Br(),
html.Div(id='datatable-div', children=None),
dbc.Row([
# Query table from DB
html.Label('List of query in database:'),
dash_table.DataTable(
id='query-table',
# data=[],
# columns=[],
style_cell={'textAlign': 'left'},
# style_table={'overflowX': 'scroll'},
# style_as_list_view=True,
sort_action="native",
sort_mode="multi",
row_selectable="multi",
selected_rows=[],
style_header={'backgroundColor': 'white', 'fontWeight': 'bold'},
editable=True),
], justify='start'),
html.Br(),
dbc.Row([
html.Button('Update', id='update-db', n_clicks=0, style=cons.style_dic),
], justify='end'),
html.Div(
id='update-status',
children=None),
dbc.Row([html.Button('Add', id='add-rows-button', n_clicks=0, style=cons.style_dic),
], justify='end'),
html.Div(
id='add-status',
children=None)
], fluid=True)
# call back to update query table and add the rows if new row is added
#app.callback(
[Output('query-table', 'data'), Output('query-table', 'columns')],
[Input('table_dropdown', 'value')
Input('add-rows-button', 'n_clicks')],
[State('query-table', 'data'),
State('query-table', 'columns')]
)
def update_query_table(table_name, n_clicks, rows, columns):
button_id = dash.ctx.triggered_id if not None else 'No clicks yet'
print('button_id', button_id)
if button_id == 'table_dropdown':
if not table_name:
raise dash.exceptions.PreventUpdate
if not i_name:
raise dash.exceptions.PreventUpdate
df = execute_sql_query(table) # step will return the records
print('First value:', df.to_dict('records'))
print('n_clicks:', n_clicks)
rows = df.to_dict('records')
if button_id == 'add-rows-button' and n_clicks > 0:
rows.append({c['id']: '' for c in columns})
print(rows)
return rows, columns
I tried having the code for new row in the next call back but then It is giving me an error stating duplicate output callbacks.
#app.callback(
Output('query-table', 'data'),
Input('add-rows-button', 'n_clicks'),
State('query-table', 'data'),
State('query-table', 'columns'))
def add_row(n_clicks, rows, columns):
if n_clicks > 0:
rows.append({c['id']: '' for c in columns})
return rows, columns
Please help me in adding the rows dynamically and updating the DB back with this new rows in DASH python.
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)
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)
I am working on a Dash app and want to include a button/hyperlink to a local html file, however I need to be able to change the html file depending on what search term is input. Below is what I currently have, but I keep getting an error saying the dbc.Button doesn't support 'n-clicks'. I also had an issue with the link immediately being opened when the script was ran. I've never used the Dash Bootstrap components before so I'm not really sure what I need to do to fix this issue.
This is just a snippet of the code so that it wasn't too long
app.layout = html.Div([
html.H1('Gene-NE'),
html.H5('Created by Lauren Kirsch and Dr. Chiquito Crasto'),
html.Label('Search Box'),
dcc.Input(id="search_gene",
type="text",
value='',
placeholder="Type a human gene name",
debounce=True,
minLength=0, maxLength=50,
autoComplete='on',
size='40'),
html.Div([
dcc.Graph(id='mygraph')]),
dcc.RadioItems(
id="vertical_display_toggle",
options=[
{'label': 'Show vertical date bars', 'value': 'show'},
{'label': 'Hide vertical bars', 'value': 'hide'}],
value='hide', # first loading value selected
labelStyle={'display': 'inline-block'}, inputStyle={"margin-left": "8px", "margin-right": "5px"}),
dcc.RadioItems(
id="synonym_display_toggle",
options=[
{'label': 'Show synonyms', 'value': 'show'},
{'label': 'Hide synonyms', 'value': 'hide'}],
value='hide', # first loading value selected
labelStyle={'display': 'inline-block'}, inputStyle={"margin-left": "8px", "margin-right": "5px"}),
html.Div([
dbc.Button("Click Here", id="id-button", className="mr-2"),
html.A(dbc.Nav(dbc.NavItem(dbc.NavLink('Click for PubMedIDs', id='outlink', href='/', target="_blank",
className="nav-link"))))
]),
html.Br(),
html.H6('Texas Tech University Center for Biotechnology and Genomics')])
df = pd.read_csv('list_out.txt', sep='\t', dtype=str)
df = df.transpose().reset_index().rename(columns={'index': 'Date'})
new_header = df.iloc[0]
df = df[1:]
df.columns = new_header
df = df.iloc[0:600]
df = df.set_index('Date')
df = df.iloc[:, ~df.columns.duplicated()]
lookup_df = pd.read_csv('Gene_Lookup.csv', dtype=str)
link = lookup_df.set_index('Approved_Symbol').Linked_Genes.str.split('|').to_dict()
link_date = lookup_df.set_index('Approved_Symbol').Date_Name_Changed.to_dict()
#app.callback(
[Output('mygraph', 'figure'),
Output('outlink', 'children')],
[Input('search_gene', 'value'),
Input('vertical_display_toggle', 'value'),
Input('synonym_display_toggle', 'value'),
Input('id-button', 'n-clicks')])
def update_output(search_gene, vertical_display_user_slct, synonym_display_user_slct, clicks):
if search_gene:
search_gene = search_gene.upper()
syns = link[search_gene]
trace1 = go.Scatter(x=df.index, y=df[search_gene], line_shape='linear', line=dict(color='white'), name=search_gene)
fig = go.Figure()
fig.add_trace(trace1)
if clicks != 0:
return 'f"/assets/{search_gene}.html"'
The main problem is that you've specified n-clicks as input and it needs to be n_clicks instead.
So for clarity, the callback should look more like this:
#app.callback(
[Output("mygraph", "figure"), Output("outlink", "children")],
[
Input("search_gene", "value"),
Input("vertical_display_toggle", "value"),
Input("synonym_display_toggle", "value"),
Input("id-button", "n_clicks"),
],
)
def update_output(
search_gene, vertical_display_user_slct, synonym_display_user_slct, clicks
):
# ...
As far as the link problem goes, I'm not able to reproduce this with what you've shared, but in your callback you have this check:
if clicks != 0:
return 'f"/assets/{search_gene}.html"'
clicks can also be None so make sure this gets handled correctly. Instead you could do something this:
if clicks:
return 'f"/assets/{search_gene}.html"'
This will handle None as well.