Does someone know how I would dynamically render tabs based on a given input?
For example, if I put a number(like 4) in a text box and pressed submit, I'd want to have 4 tabs show up underneath with content(let's the tab number underneath). If put 2 I'd want 2 tabs. I think I use a for loop somewhere but not sure how to implement.
I've only been able to get to show a static number of tabs once submit is clicked to start off.
import dash
import dash_html_components as html
import dash_core_components as dcc
import dash_bootstrap_components as dbc
from dash.dependencies import Input, Output, State
app = dash.Dash()
input_group_Row = dbc.Row([ dbc.Col([
dbc.CardBody(
[
dbc.InputGroup(
[
dbc.InputGroupAddon("Enter number", addon_type="prepend"),
dbc.Input(id='integer',placeholder="Enter int"),
],className="mb-3",),
]),
]),
dbc.Col([
dbc.Button('Enter', color='primary',id='load', block=True,n_clicks=0),
])
])
app.layout = html.Div([input_group_Row, html.Div(id='output-content')], style = CONTENT_STYLE)
#app.callback(Output('output-content', 'children'),
[Input('load', 'n_clicks')],
[State('integer','value')],
)
def render_tabs(click1, integ):
output =""
ctx = dash.callback_context
action = ctx.triggered[0]['prop_id'].split('.')[0]
if action == 'load':
print(type(int(integ)))
output = integ
return dcc.Tabs(id='tab', value='tab1',children=[
dcc.Tab(label='Tab 1', value='tab1', children=[html.Div(output,style={'color': 'blue', 'fontSize': 140})]),
dcc.Tab(label='Tab 2', value='tab2', children=[html.Div(int(output)+3,style={'color': 'blue', 'fontSize': 140})])
])
You could use a list comprehension.
List comprehensions provide a concise way to create lists. Common applications are to make new lists where each element is the result of some operations applied to each member of another sequence or iterable, or to create a subsequence of those elements that satisfy a certain condition.
https://docs.python.org/3/tutorial/datastructures.html
Based on your example you could do something like this:
#app.callback(
Output("output-content", "children"),
[Input("load", "n_clicks")],
[State("integer", "value")],
)
def render_tabs(click1, integ):
output = ""
ctx = dash.callback_context
action = ctx.triggered[0]["prop_id"].split(".")[0]
if action == "load":
output = int(integ)
return dcc.Tabs(
id="tab",
value="tab1",
children=[
dcc.Tab(
label=f"Tab {num + 1}",
value=f"tab{num + 1}",
children=[html.Div(num)],
)
for num in range(output)
],
)
The same solution with a for loop could look like this:
#app.callback(
Output("output-content", "children"),
[Input("load", "n_clicks")],
[State("integer", "value")],
)
def render_tabs(click1, integ):
output = ""
ctx = dash.callback_context
action = ctx.triggered[0]["prop_id"].split(".")[0]
if action == "load":
output = int(integ)
tabs = []
for num in range(output):
tabs.append(
dcc.Tab(
label=f"Tab {num + 1}",
value=f"tab{num + 1}",
children=[html.Div(num)],
)
)
return dcc.Tabs(
id="tab",
value="tab1",
children=tabs,
)
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
Want to create a dash board, with two or more inputs, a submit button at the end of inputs that outputs a table. I am having trouble creating properly spaced inputs, placing the button and formatting the output table.
import pandas as pd
import dash
import dash_core_components as dcc
import dash_bootstrap_components as dbc
import dash_html_components as html
from dash.dependencies import Input, Output, State
df1 = pd.DataFrame({{'col1': [1, 2], 'col2': [3, 4]}})
df1 = df1 + 100
df3 = df1 -100
df4 = df1/2
app = dash.Dash()
app.layout = html.Div([
html.Div([
html.Div([
dcc.Markdown(children= ''' Drop Down''')
]),
dcc.Dropdown(id = 'dd',
options = [{'label' : 'NY', 'value' : 'NY'},
{'label' : 'SF', 'value' : 'SF'}],
value = 'NY'
)
],
style = {'width':'48%', 'display':'inline-block'}
),
html.Div([
html.Div([
dcc.Markdown(children= ''' Input''')
]),
dcc.Input(id = 'x',
placeholder='Enter a value...',
value = '',
type = 'number',
max = 1.0,
min = 0.0
),
],
style = {'width':'48%', 'display':'inline-block'}
),
html.Button(id = 'submit',
# n_clicks = 0,
children = 'Submit here',
style = {'fontsize':24}
),
html.Div(id = 'table')
])
#app.callback(Output(component_id = 'table',
component_property = 'children'),
[Input(component_id ='dd',
component_property = 'value'),
Input(component_id ='x',
component_property = 'value')
],
[State('submit', 'value')]
)
def risk(dd, d):
if ((dd == 'NY') and (d <0.5)):
table = df1
elif ((dd == 'NY') and (d >= 0.5)):
table = df2
elif ((dd == 'SF') and (d <0.5)):
table = df3
else :
table = df4
return dbc.Table.from_dataframe(table,
striped=True,
bordered=True,
hover=True)
if __name__ == '__main__':
app.run_server()
I ran the above after commenting out the html.Button and the State part. It runs.
How can I properly include the button such that the table is produced only when I input all inputs and click submit?
How can I better format the table with borders?
Here is how my html looks like, when I run after removing Button and State.
This is ugly. Help me format better.
PS - I want the puts(Drop Down and Input take equal space, and are on the same line. Submit button should ideally be in the middle, right below the inputs.
I am new to dash and I am also requiring the guidance in the same matter, I have a code with the cascaded radio buttons, I want to add the Start button and after clicking the Start button the dash should start displaying the data, but currently its just opening and displaying the data.
I also need help , as I have auto-update in my dash, will there be any contradiction if I need to add Start button and then dash should display the data and auto update the values.
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'].
I am trying to make a live updating dash app but the live update only shows when I manually press the refresh button on the web page until then the graph doesn't update , I am trying to generate a progressively updating heat map from some data derived from sensor reading to show how it would look like if the data is read in real time.
Data Source is available at : Data source used in code below 1MB in size only
import dash
from dash.dependencies import Output, Input
import dash_core_components as dcc
import dash_html_components as html
import plotly
import pandas as pd
import random
import plotly.graph_objs as go
# The csv file for this dataset used here can be downloaded from:
# https://easyupload.io/0ni29p (Hyperlinked in post as well)
cols = [ 'shot' , 'chan' , 'peak_amplitude', 'onset_time_(ms)', 'bubble_period_(ms)']
cols_bubble_period = [ 'shot' , 'chan' , 'bubble_period_(ms)']
source_df = pd.read_csv('seq-4bubbleQC.txt').drop(['sequence', 'file'], axis=1)
source_df.columns = cols
source_df.loc[source_df['shot'] % 2 == 0, 'chan'] += 18
all_results = pd.DataFrame(columns = cols)
i=0
def df_to_plotly(df):
return {'z': df.values.tolist(),
'x': df.columns.tolist(),
'y': df.index.tolist()}
app = dash.Dash(__name__)
app.layout = html.Div(
[
dcc.Graph(id = 'live-graph', animate = True),
html.Button('START_PLOT', id='plot_start',n_clicks=0),
html.H2(id='hidden-div', style= {'display': 'none'} ),
dcc.Interval(
id = 'graph-update',
interval = 5*1000,
n_intervals = 0
),
]
)
#app.callback(
Output('hidden-div', 'children'),
[ Input('graph-update', 'n_intervals') ],
[Input('plot_start', 'n_clicks')]
)
def update_frame(n_clicks, n_interval):
global source_df
global i
global all_results
if n_clicks == 0:
raise dash.exceptions.PreventUpdate()
all_results = all_results.append((source_df[i: i+36]))
i+=36
#print(i)
#app.callback(
Output('live-graph', 'figure'),
[ Input('graph-update', 'n_intervals') ],
[Input('plot_start', 'n_clicks')]
)
def update_graph_heat(n_clicks, n_interval):
global all_results
all_results_1 = all_results.apply(pd.to_numeric)
#all_results_1 = all_results_1.drop_duplicates()
#all_results_1.to_csv('all_results.csv')
df_s1 = all_results_1.loc[all_results_1['chan'] <=18]
df_s1_bp = df_s1[cols_bubble_period]
#print(df_s1_bp)
#df_s1_bp.to_csv('test_data.csv')
df_s1_pivoted = df_s1_bp.pivot( 'chan', 'shot', 'bubble_period_(ms)')
data = go.Heatmap(df_to_plotly(df_s1_pivoted) , colorscale='rainbow', zmin=30.0, zmax=210.0)
return {'data': [data],
'layout' : go.Layout(xaxis_nticks=20, yaxis_nticks=20)}
if __name__ == '__main__':
app.run_server()
The graph only updates when a manual page refresh is done , how can I make the updated graph appear automatically?
Your callbacks are constructed incorrectly. All inputs should be within the same list. Here is what your first one should look like:
#app.callback(
Output('hidden-div', 'children'),
[Input('graph-update', 'n_intervals'),
Input('plot_start', 'n_clicks')]
)
def update_frame(n_clicks, n_interval):
global source_df...
# etc.
I need to write a call back function which will have a dynamic input component and a static input component. But putting them together is throwing error.
This is what I have done so far -
#app.callback(
[Output("new_list", "children")],
[
#Static Input Component
Input("clear", "n_clicks"),
#Dynamic Input Component
Input(str(i), "value") for i in item_list[['item3','item2','item1']].stack().groupby(level=0).agg(' '.join)
],
)
###########################################################
## Updated sample working code
import dash
import dash_html_components as html
import dash_bootstrap_components as dbc
from dash import callback_context
from dash.dependencies import Input, Output, State
import numpy as np
import pandas as pd
import dash_table as dt
item1 = ['A','A','B','B']
item2 = ["W","X","Y","Z"]
item3 = ["L",np.nan,'M','L']
item_list = pd.DataFrame(list(zip(item1,item2,item3)),columns=["item1","item2","item3"])
cat_list = item_list.item1.unique()
global itemlist
itemlist = pd.DataFrame( columns = ['item','qty'])
for item in item_list[['item3','item2','item1']].stack().groupby(level=0).agg(' '.join):
itemlist.loc[len(itemlist)] = (item,0)
def Add_item(data,data2):
print("BILLING: Add_item()")
item_list = dbc.Card(children = [
generate_item(data,data2),
dbc.Row([
dbc.Col([
dbc.Button(
html.H5("Cancel"), id="cancel", className="ml-auto", color = "danger"
)
]),
dbc.Col([
dbc.Button(
html.H5("Clear"), id="clear", className="ml-auto", color = "info"
)
]),
],style = {"width": "71rem"})
],
id="modal-body-newitem"
)
return item_list
def generate_item(item_list,itemlist):
buttons = html.Div(children =[
dbc.Row([
dbc.Button(
html.H5(str('+ ' + a)),
id= str(a),
className="mb-3",
color="primary",
block=True
),
dbc.Collapse([
dbc.Card([
dbc.Row([
dbc.Col([dbc.Button(html.H5(str('+ ' + b)),className="mb-3",color="info",block=True, disabled = True)],width = 10),
dbc.Col([dbc.Input(type="number", min=0, max=20, step=1,value = itemlist.loc[itemlist.item == b,'qty'],id = b )],width = 2)
]) for b in item_list.loc[item_list.item1== a, ('item3','item2','item1')].stack().groupby(level=0).agg(' '.join).sort_values()
#,dbc.Card(dbc.CardBody(str("This content is for " + a)))
],body = True, style = {"width": "71rem", "justify": "centre", "align": "centre"})
],id= str("collapse" + a))
]) for a in item_list.item1.unique()
])
return buttons
app = dash.Dash(__name__,external_stylesheets=[dbc.themes.CYBORG])
app.config.suppress_callback_exceptions = True
server = app.server
app.title="Test"
app.layout = html.Div([
dbc.Card(
dbc.CardBody(
[
html.H5("New Item", className="card-title"),
Add_item(item_list,itemlist),
html.Div(id = 'new_list')
]
)
)
])
#Item Ctegory List
#app.callback(
[Output(str("collapse" + i), "is_open") for i in cat_list],
[Input(str(i), "n_clicks") for i in cat_list]+
[Input("cancel", "n_clicks")])
def toggle_collapse_category_box(*args):
trigger = callback_context.triggered[0]
print("MYBIZZAPP:toggle_collapse_category_box: Call - "+str(callback_context.triggered))
if not callback_context.triggered or trigger["prop_id"].split(".")[0] == 'cancel':
print('MYBIZZAPP:toggle_collapse_category_box: Not Triggered/Cancel')
global isopn
isopn = [False] * len(cat_list)
else:
print('MYBIZZAPP:toggle_collapse_category_box: Triggered')
for i in range(len(cat_list)):
if cat_list[i] == trigger["prop_id"].split(".")[0]:
isopn[i] = not isopn[i]
return isopn
### Clear item list
#app.callback(
[Output(str(i), "value") for i in item_list[['item3','item2','item1']].stack().groupby(level=0).agg(' '.join)],
[Input("cancel", "n_clicks"),
Input("clear", "n_clicks")])
def clear_item_box(n2,n3):
trigger = callback_context.triggered[0]
print("MYBIZZAPP:clear_item_box: Call - "+str(callback_context.triggered))
if trigger["prop_id"].split(".")[0] == 'cancel':
print("MYBIZZAPP:clear_item_box: cancel order")
itemlist = pd.DataFrame( columns = ['item','qty'])
for item in item_list[['item3','item2','item1']].stack().groupby(level=0).agg(' '.join):
itemlist.loc[len(itemlist)] = (item,0)
elif trigger["prop_id"].split(".")[0] == 'clear':
print("MYBIZZAPP:clear_item_box: clear order")
return [0] * len(item_list)
#Menu item list
#app.callback(
[Output("new_list", "children")],
[Input(str(i), "value") for i in item_list[['item3','item2','item1']].stack().groupby(level=0).agg(' '.join)]+
[Input("clear", "n_clicks"),
Input("cancel", "n_clicks")])
def menu_item_list(*args):
trigger = callback_context.triggered[0]
print("MYBIZZAPP:menu_item_list: Call - "+str(callback_context.triggered))
print(trigger["prop_id"].split(".")[0])
if not callback_context.triggered :
print('MYBIZZAPP:menu_item_list: Not Triggered')
global itemlist
itemlist = pd.DataFrame( columns = ['item','qty'])
for item in item_list[['item3','item2','item1']].stack().groupby(level=0).agg(' '.join):
itemlist.loc[len(itemlist)] = (item,0)
else:
print('MYBIZZAPP:menu_item_list: Triggered')
itemlist.loc[itemlist.item == trigger["prop_id"].split(".")[0],'qty'] = trigger["value"]
if trigger["prop_id"].split(".")[0] == 'clear' or trigger["prop_id"].split(".")[0] == 'clear':
print('MYBIZZAPP:menu_item_list: Clear|Cancel')
itemlist['qty'] = 0
print('MYBIZZAPP:menu_item_list: Item List')
print(itemlist)
return ['Total items selected - ' + str(sum(itemlist.qty))]
if __name__ == '__main__':
app.run_server(debug=True)
If I do not mention the static input component, code works fine. But I need to have the static component as well.
Overall problem that I am trying to work on is - I have dynamic input text boxes, if and when a value is entered in any of these boxes, that value needs to be accounted for and returned as a table. Also, there is a 'clear' button. If clicked, all values should be re-set to default pre-defined value.
Thanks for your help in advance.
Thank you for updating with the error. This issue is coming from your list comprehension. When you add the first input, it creates invalid syntax for the comprehension. This should work, though:
#app.callback(
[Output("new_list", "children")],
[
#Static Input Component
Input("clear", "n_clicks"),
] + [
#Dynamic Input Component
Input(str(i), "value") for i in item_list[['item3','item2','item1']].stack().groupby(level=0).agg(' '.join)
],
)