Dash Bootstrap components not changing linked url - python

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.

Related

'float' object not iterable, dcc.Upload in Dash

This is a strange error, and one that has been asked before here but unfortunately went unanswered.
I have taken the code from the Dash official documentation here, which allows the user to upload a csv or xls file and view it as a datatable in the dash web app. I've copied and pasted the code below:
import base64
import datetime
import io
import dash
from dash.dependencies import Input, Output, State
from dash import dcc, html, dash_table
import pandas as pd
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
app.layout = html.Div([
dcc.Upload(
id='upload-data',
children=html.Div([
'Drag and Drop or ',
html.A('Select Files')
]),
style={
'width': '100%',
'height': '60px',
'lineHeight': '60px',
'borderWidth': '1px',
'borderStyle': 'dashed',
'borderRadius': '5px',
'textAlign': 'center',
'margin': '10px'
},
# Allow multiple files to be uploaded
multiple=True
),
html.Div(id='output-data-upload'),
])
def parse_contents(contents, filename, date):
content_type, content_string = contents.split(',')
decoded = base64.b64decode(content_string)
try:
if 'csv' in filename:
# Assume that the user uploaded a CSV file
df = pd.read_csv(
io.StringIO(decoded.decode('utf-8')))
elif 'xls' in filename:
# Assume that the user uploaded an excel file
df = pd.read_excel(io.BytesIO(decoded))
except Exception as e:
print(e)
return html.Div([
'There was an error processing this file.'
])
return html.Div([
html.H5(filename),
html.H6(datetime.datetime.fromtimestamp(date)),
dash_table.DataTable(
df.to_dict('records'),
[{'name': i, 'id': i} for i in df.columns]
),
html.Hr(), # horizontal line
# For debugging, display the raw contents provided by the web browser
html.Div('Raw Content'),
html.Pre(contents[0:200] + '...', style={
'whiteSpace': 'pre-wrap',
'wordBreak': 'break-all'
})
])
#app.callback(Output('output-data-upload', 'children'),
Input('upload-data', 'contents'),
State('upload-data', 'filename'),
State('upload-data', 'last_modified'))
def update_output(list_of_contents, list_of_names, list_of_dates):
if list_of_contents is not None:
children = [
parse_contents(c, n, d) for c, n, d in
zip(list_of_contents, list_of_names, list_of_dates)]
return children
if __name__ == '__main__':
app.run_server(debug=True)
The above code works flawlessly, as one would expect from code taken from the official documentation. Below is my code, which works perfectly except for the feature taken from the above documentation. I didn't change the code at all.
import pandas as pd
import numpy as np
import plotly.express as px
import dash
from dash import html, Dash, dcc, dash_table
from dash.dependencies import Input, Output, State
import dash_bootstrap_components as dbc
import dash_split_pane
import base64
import io
import datetime
from pandas.tseries.offsets import BDay
import plotly.graph_objects as go
external_stylesheets = ["https://codepen.io/chriddyp/pen/bWLwgP.css"]
app = Dash(__name__, external_stylesheets=external_stylesheets)
columnNames = ["Blood Lactate", "Velocity (km/h)", "Stage Finish Time (MM:SS)"]
df = pd.DataFrame(columns=columnNames)
df.rename_axis("Stage", inplace=True, axis=0)
columnIds = ["bloodLactate", "velocity", "stageFinishTime"]
# ------------------------------------------------------------------------
input_types = ['number', 'number', 'text']
row1 = html.Div(
[
dbc.Row([
dbc.Col([
html.Div([
html.P("Blood Lactate:", style={"margin-left":20}),
dcc.Input(
id="bloodLactateId",
type="number",
placeholder="insert Blood Lactate",
minLength=0, maxLength=50,
autoComplete='on',
disabled=False,
readOnly=False,
required=False,
size=20,
style={"margin-right":20}
)
], style=
{
"display":"flex",
"justify-content":"space-between",
"align-items":"baseline",
"margin-top":20
}
)
])
])
]
)
row2 = html.Div(
[
dbc.Row([
dbc.Col([
html.Div([
html.P("Velocity (km/h):", style={"margin-left":20}),
dcc.Input(
id="velocityId",
type="number",
placeholder="insert Velocity",
minLength=0, maxLength=50,
autoComplete='on',
disabled=False,
readOnly=False,
required=False,
size="20",
style={"margin-right":20}
)
], style={
"display":"flex",
"justify-content":"space-between",
"align-items":"baseline"})
]),
])
]
)
row3 = html.Div(
[
dbc.Row([
dbc.Col([
html.Div([
html.P("Stage Finish Time (MM:SS):",
style={"margin-left":20}),
dcc.Input(
id="stageFinishTimeId",
type="text",
placeholder="insert (MM:SS)",
minLength=0, maxLength=50,
autoComplete='on',
disabled=False,
readOnly=False,
required=False,
size="20",
style={"margin-right":20}
)
], style={"display":"flex",
"justify-content":"space-between",
"align-items":"baseline"})
]),
])
]
)
row4 = html.Div(
dbc.Row(
html.Button('Add Row',
id='add_row',n_clicks=0),
style={"text-align":"center"}
)
)
row5 = html.Div([
dcc.Upload(
id="upload-data", children=html.Div([
'Drag and Drop COSMED file or ', html.A('Select Files')
] ),
style={
'width':'80%',
"lineHeight":"60px",
'borderWidth':'1px',
'borderStyle':'dashed',
'borderRadius':'5px',
'text-align':'center',
'margin-left':'auto',
'margin-right':'auto',
'margin-top':40,
}
)
], style={"align-content":'center'})
table = html.Div([
dbc.Row([
dbc.Col([html.H5('Results',
className='text-center',
style={"margin-left":20}),
dash_table.DataTable(
id='table-container_3',
data=[],
columns=[{"name":i_3,"id":i_3,'type':'numeric'} for i_3 in df.columns],
style_table={'overflow':'scroll','height':600},
style_cell={'textAlign':'center'},
row_deletable=True,
editable=True),
],width={'size':12,"offset":0,'order':1})
]), html.Div(id='output-data-upload')
])
pane1 = html.Div([
row1,
html.Br(),
row2,
html.Br(),
row3,
html.Br(),
row4,
html.Br(),
row5
])
pane2 = html.Div(
table
)
app.layout = dash_split_pane.DashSplitPane(
children=[pane1, pane2],
id="splitter",
split="vertical",
size=500
)
#copy pasted code from docs ======================================================
def parse_contents(contents, filename, date):
content_type, content_string = contents.split(',')
decoded = base64.b64decode(content_string)
try:
if 'csv' in filename:
# Assume that the user uploaded a CSV file
df = pd.read_csv(
io.StringIO(decoded.decode('utf-8')))
elif 'xls' in filename:
# Assume that the user uploaded an excel file
df = pd.read_excel(io.BytesIO(decoded))
except Exception as e:
print(e)
return html.Div([
'There was an error processing this file.'
])
return html.Div([
html.H5(filename),
html.H6(datetime.datetime.fromtimestamp(date)),
dash_table.DataTable(
df.to_dict('records'),
[{'name': i, 'id': i} for i in df.columns]
),
html.Hr(), # horizontal line
# For debugging, display the raw contents provided by the web browser
html.Div('Raw Content'),
html.Pre(contents[0:200] + '...', style={
'whiteSpace': 'pre-wrap',
'wordBreak': 'break-all'
})
])
#==================================================================================
#app.callback(
Output('table-container_3', 'data'),
Output('bloodLactateId', 'value'),
Output('velocityId', 'value'),
Output('stageFinishTimeId', 'value'),
Input('add_row', 'n_clicks'),
State('table-container_3', 'data'),
State('table-container_3', 'columns'),
State('bloodLactateId', 'value'),
State('velocityId', 'value'),
State('stageFinishTimeId', 'value'))
def add_row(n_clicks, rows, columns, selectedBloodLactate, selectedVelocity,
selectedStageFinishTime):
if n_clicks > 0:
rows.append({c['id']: r for c,r in zip(columns,
[selectedBloodLactate,
selectedVelocity,
selectedStageFinishTime])})
return rows, '', '', ''
#copy pasted code ===============================================================
#app.callback(Output('output-data-upload', 'children'),
Input('upload-data', 'contents'),
State('upload-data', 'filename'),
State('upload-data', 'last_modified'))
def update_output(list_of_contents, list_of_names, list_of_dates):
if list_of_contents is not None:
children = [
parse_contents(c, n, d) for c, n, d in
zip(list_of_contents, list_of_names, list_of_dates)]
return children
#=================================================================================
if __name__ == '__main__':
app.run_server(debug=False)
Here is a photo of what the app looks like, to get an idea.
When I load the app it runs as expected, I can add rows, but when I upload a csv file I get the following error:
File "C:\Users\harry\OneDrive\Documents\coding\fypWebApp\dashApp\app.py", line 255, in update_output
zip(list_of_contents, list_of_names, list_of_dates)]
TypeError: 'float' object is not iterable
It seems the problem lies in one of the lists in the update_output function. Also, I get the following notice in VSCode.
This notice does not appear when I have just the code taken from the documentation in a standalone file (as seen in the image below), only when I enter the code into my code.
I don't know where to start to fix this problem, and it seems like it requires a good understanding of Dash to solve. Any help is appreciated.
After long debugging, the problem is that list_of_dates is a float number which represents the last modified for the uploaded file. There is a list comprehension in the callback which iterates through this float number, which does not make sense to iterate through a number, and it eventually throws an error. To solve this problem, all you should do is to replace the callback function with the following:
def update_output(list_of_contents, list_of_names, list_of_dates):
if list_of_contents is not None:
children = [parse_contents(list_of_contents, list_of_names, list_of_dates)]
return children
Inside the function parse_contents, I added the delimiter=";" to read the CSV file properly.
if 'csv' in filename:
# Assume that the user uploaded a CSV file
df = pd.read_csv(
io.StringIO(decoded.decode('utf-8')), delimiter=";") #<-- this line.
Finally, I reduced the size of the upper table to show the table of the CSV file.
Output

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)

Returning a completely new layout to same page in Dash

I am trying to set up a dash app which has 2 layers:
1st page has a couple of input forms, and based on these inputs - app_layout
2nd page - which has a different set of intputs (layout_more_inputs) which pop up depending on what 1st page input is.
I am trying to return layout_more_inputs from a callback but it doesn't work.
app.layout = html.Div([
html.H3('welcome to app'),html.Br(),
dcc.Input(id='input-00-state', type='text', value='QQQ'),
dcc.Input(id='input-01-state', type='text', value='MOVE'),
html.Button(id='submit-button-state', n_clicks=0, children='Go!'),
html.Div(id='output-state'),
dcc.Graph(id='graph-with-slider'),
])
layout_more_inputs = html.Div([
dcc.Input(id='input-10-state', type='number', value='0.11'),
dcc.Input(id='input-11-state', type='number', value=0.12),
html.Button(id='submit-button-state2', n_clicks=0, children='Go Go!'),
])
#front page - 0
#app.callback(
Output('container', 'children'),
Input('submit-button-state', 'n_clicks'),
State('input-00-state', 'value'),
State('input-01-state', 'value'),
)
def ask_for_more_inputs(n_clicks,asset_str,event_str):
print("input summary:")
print(n_clicks,asset_str,event_str)
return layout_more_inputs #<<--DOES NOT WORK
#front page - 1
#app.callback(
Output('graph-with-slider', 'figure'),
Output('output-state', 'children'),
Input('submit-button-state2', 'n_clicks'),
State('input-10-state', 'value'),
State('input-11-state', 'value'),
)
def more_inputs(n_clicks,input0,input1):
d = {'x': [input0, input1], 'y': [input0, input1]}
df = pd.DataFrame(data=d)
filtered_df = df
fig = px.scatter(filtered_df, x="x", y="y")
fig.update_layout(transition_duration=500)
return fig, u'''Button pressed {} times, 1 is "{}", and 2 is "{}"'''.format(n_clicks,state1,state2)
if __name__ == '__main__':
app.run_server(debug=True)
I'm not 100% on what exactly is is you're attempting to deliver, but one options would be to use Dash's dcc.Tabs property.
I've started & laid the groundwork, sort of, for you:
import sys
import dash
from dash import html
from dash import dcc
from dash import no_update
from dash.dependencies import Input, Output, State
# app = dash.Dash(__name__)
app = JupyterDash()
layout = html.Div([
html.H3('welcome to app'),
html.Br(),
dcc.Input(id='input-00-state', type='text', value='QQQ'),
dcc.Input(id='input-01-state', type='text', value='MOVE'),
html.Button(id='submit-button-state', n_clicks=0, children='Go!'),
html.Div(id='output-state'),
dcc.Graph(id='graph-with-slider'),
])
layout_more_inputs = html.Div([
dcc.Input(id='input-10-state', type='number', value=0.11),
dcc.Input(id='input-11-state', type='number', value=0.12),
html.Button(id='submit-button-state2', n_clicks=0, children='Go Go!'),
])
tabs = [
dcc.Tab(label="Front Page - 0", children=layout),
dcc.Tab(label="Front Page - 1", children=layout_more_inputs)
]
multitab_layout = [dcc.Tabs(id="tabs", children=tabs)]
app.layout = html.Div(
[html.Div(id="multitab_layout", children=multitab_layout)])
# front page - 0
#app.callback(
Output('container', 'children'),
Input('submit-button-state', 'n_clicks'),
[State('input-00-state', 'value'),
State('input-01-state', 'value')]
)
def ask_for_more_inputs(n_clicks, asset_str, event_str):
print("input summary:")
print(n_clicks, asset_str, event_str)
return layout_more_inputs # <<--DOES NOT WORK
# front page - 1
#app.callback([
Output('graph-with-slider', 'figure'),
Output('output-state', 'children')
], Input('submit-button-state', 'n_clicks'),
Input('submit-button-state2', 'n_clicks'), [
State('input-00-state', 'value'),
State('input-01-state', 'value'),
State('input-10-state', 'value'),
State('input-11-state', 'value')
])
def more_inputs(n_clicks, n_cliks2, input0, input1, state0, state1, state10,
state11):
d = {'x': [input0, input1], 'y': [input0, input1]}
df = pd.DataFrame(data=d)
filtered_df = df
fig = px.scatter(filtered_df, x="x", y="y")
fig.update_layout(transition_duration=500)
return fig, u'''Button pressed {} times, 1 is "{}", and 2 is "{}"'''.format(
n_clicks, state1, state2)
if __name__ == '__main__':
app.run_server(debug=True)
using your provided code; but not entirely sure where exactly you'd want it to go from here. In terms of UI/EX behavior etc. Perhaps this may be enough of a clue to get you going as you need to...

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)

How can we create data columns in Dash Table dynamically using callback with a function providing the dataframe

I am trying to create dash table on Web using Inputs. However the issue is that the data is created from database from the callback and a priori,
I do not know the names of the columns unless the pandas dataframeis created using the callback function.
I have checked that I getting correct data. However not able to display it. I have used multiple output options (using Dash 0.41)
My code looks as follows: ( I have not provided the details of the function which generates the pandas dataframe in the callback someFunc,
as that was not important for the purpose of this Dash code TroubleShooting.
import dash_table as dt
def someFunc(ID, pattern_desc, file_path):
## do something
return df # pandas dataframe
#
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
server = app.server
app = dash.Dash(__name__)
app.config.suppress_callback_exceptions = True
app.css.config.serve_locally = True
app.scripts.config.serve_locally = True
app.layout = html.Div(
children = [
html.Div(
id = 'title',
children = appTitle,
className = 'titleDiv'
),
html.Div(
children = [
html.Div(
children = "Enter ID:",
className = 'textDiv'
),
dcc.Input(
id = 'ID',
type = 'text',
value = 'ABCER1',
size = 8),
html.Div(
children = "Enter Test Pattern",
className = 'textDiv'
),
dcc.Input(
id = 'pattern_desc',
type = 'text',
value = 'Sample',
size = 20),
html.Div(
children = "Enter File OutPut Path:",
className = 'textDiv'
),
dcc.Input(
id = 'file_path',
type = 'text',
value = '',
size = 30),
html.Button(
id = 'submit',
n_clicks = 0,
children = 'Search'
)
]
),
html.Div(
id = 'tableDiv',
children = dash_table.DataTable(
id = 'table',
style_table={'overflowX': 'scroll'},
style_as_list_view=True,
style_header={'backgroundColor': 'white','fontWeight':
'bold'},
),
className = 'tableDiv'
)
]
)
# callback to update the table
#app.callback([Output('table', 'data'),Output('table', 'columns')]
[Input('submit', 'n_clicks')],
[State('ID', 'value'), State('pattern_desc', 'value'),
State('file_path', 'value')])
def update_table(n_clicks, ID, pattern_desc, file_path):
df = someFunc(ID, pattern_desc, file_path)
mycolumns = [{'name': i, 'id': i} for i in df.columns]
return html.Div([
dt.DataTable(
id='table',
columns=mycolumns,
data=df.to_dict("rows")
)
])
So in this case the function someFunc which takes the 3 input arguments returns a pandas dataframe which can have different columns based on the inputs. Thus the app layout should display
those columns as given by the output of the callback function dynamically based on the inputs.
I should be getting the webpage populated with table and columns, But instead getting an error. When I run this, I am getting the data generated through the function to the file, but dash is not able to
generated the table on webpage. I get the following error:
dash.exceptions.InvalidCallbackReturnValue: The callback ..table.data...table.columns.. is a multi-output.
Expected the output type to be a list or tuple but got Div([DataTable(columns=[{'name': 'pattern_desc', 'id': 'pattern_desc'}, ......
Not Sure How I can achieve that. Any help will be appreciated.
In your Dash callback you are supposed to be returning 2 separate values to the 2 separate outputs:
[Output('table', 'data'),Output('table', 'columns')]
You are returning:
return html.Div([
dt.DataTable(
id='table',
columns=mycolumns,
data=df.to_dict("rows")
)
])
which is only 1 output.
Dash expects 2 return values in either a list, or a tuple like so:
return("output1" , outputVariable2)
or
return[ Html.Div("text") , "output Text 2"]
in order to fix the problem, either return 2 values in a tuple or list, or edit your output requirements so only one value is necessary.
From the looks of it you are trying to return a Div with a Datatable in it, so you could just make the following changes:
html.Div(
id = 'tableDiv',
className = 'tableDiv'
)
...
#app.callback([Output('tableDiv', 'children')]
[Input('submit', 'n_clicks')],
[State('ID', 'value'), State('pattern_desc', 'value'),
State('file_path', 'value')])
def update_table(n_clicks, ID, pattern_desc, file_path):
df = someFunc(ID, pattern_desc, file_path)
mycolumns = [{'name': i, 'id': i} for i in df.columns]
return html.Div([
dt.DataTable(
id='table',
columns=mycolumns,
data=df.to_dict("rows")
)
])
If I've understood you correctly, then you can simply create another callback which outputs the updated value for the columns prop. You could also use a multi-output callback to update both at the same time.
#app.callback(Output('table', 'columns'),
[Input('submit', 'n_clicks')],
[State('ID', 'value'), State('pattern_desc', 'value'),
State('file_path', 'value')])
def update_table(n_clicks, ID, pattern_desc, file_path):
mydata = someFunc(ID, pattern_desc, file_path)
# here you would use the dataframe columns to create the new column values
return new_column_values

Categories