Dynamically adding HTML inputs in Dash framework - python

I am new to Dash and I am in need of some assistance. I need to create a dynamic number of inputs, but I have no idea how to bind a dynamic number of callback for each slider component. I am only able to create a dynamic number of sliders, but as you can see in the code, I need to hard code the number of callbacks. Is there a better way to to this so that it is more dynamic?
#app.callback(
Output('slider-container', 'children'),
[Input('button', 'n_clicks')])
def add_sliders(n_clicks):
return \
html.Div([
html.Div([
html.Div(dcc.Slider(id='slider-{}'.format(i))),
dcc.Input(
id='input-{}'.format(i),
placeholder='Graph Name',
type='text',
value=''
),
html.Div(id='output-{}'.format(i), style={'marginTop': 30})
]) for i in range(n_clicks)]
)
# up to 10 sliders
for i in range(10):
#app.callback(
Output('slider-{}'.format(i), 'children'),
[Input('slider-{}'.format(i), 'value'),
Input('input-{}'.format(i), 'value')])
def update_output(slider_i_value, input_i_value):
return str(slider_i_value) + str(input_i_value)

As of Dash 1.11.0 you have Pattern Matching Callbacks that do just this. In your case you'd use the one with the MATCH.

Related

Callback with navbar plotly dash python

I have this code below. I’ve created 3 navigation links: Inputs, Calculations and About.
Now on the Inputs sheet, I would like to add several dropdowns and input fields for the user. How can I include those input fields and give them ids as I want to use them for calculations later (i.e I create a dropdown and I give it an id of “my-dropdown” to later use for the calculations I want to make in Calculations).
So far this is what I have in the callbacks but if I want to continue my function with an elif pathname == “/inputs” and then I start including my input fields, I will not be able to store their ids to call them later once I want to do the calculations. Can you please advise?
navbar = dbc.NavbarSimple(
children=[
dbc.NavItem(dbc.NavLink("INPUTS", href="/inputs", external_link=True, active="exact")),
dbc.NavItem(dbc.NavLink("CALCULATIONS", href="/calculations", external_link=True, active="exact")),
dbc.NavItem(dbc.NavLink("ABOUT", href="/about", external_link=True, active="exact")),
dbc.DropdownMenu(
children=[
dbc.DropdownMenuItem("Comments", href="/comments", external_link=True),
dbc.DropdownMenuItem("Version Updates", href="/updates", external_link=True),
],
nav=True,
in_navbar=True,
label="More",
),
],
brand="Main Page Title",
color="#015151",
dark=True
)
content = html.Div(id="page-content", children=[])
app.layout = html.Div([
dcc.Location(id="url"),
navbar,
content
])
# ---------------------------------------------------------------------------
## APP CALLBACKS ##
# Add info in about section
#app.callback(
Output("page-content", "children"),
[Input("url", "pathname")]
)
def render_page_content(pathname):
if pathname == "/about":
return [
html.Br(),
html.Br(),
html.P('About', style={'fontWeight': 'bold', 'font-size': 32, 'margin-left': '20px'}),
I suggest you have some global hidden temporary divs or use the dcc.Store component that can hold the inputs that are inputted on the inputs sheet, then you'll just have to refer to these hidden divs that hold these inputs in the calculations sheet, rather than referring to the inputs sheet components.

How to update a layout into a Python Dash application

i'm new with Python & Dash and i don't know how to force my application to refresh the shown layout without use the callback; this is my code:
AppMain.py
app.layout = html.Div(
children=[
dcc.Location(
id=ID_URL,
refresh=False
),
html.Div(
[
dcc.Link(
'Test',
href=HREF_PAGE_TEST,
),
dcc.Link(
'Single',
href=HREF_PAGE_SINGLE,
),
dcc.Link(
'Common',
href=HREF_PAGE_COMMON,
),
]
),
html.Div(
id=ID_PAGE_CONTAINER,
),
),
#app.callback(
Output(ID_PAGE_CONTAINER, 'children'),
[Input(ID_URL, 'pathname')],
)
def clickOnPageToShow(pathname):
return findPageToShow(pathname)
def findPageToShow(pathname):
if pathname == HREF_PAGE_TEST:
PageTest.data_frame = shared_data_frame
return PageTest.layout
elif pathname == HREF_PAGE_SINGLE:
PageSingle.data_frame = shared_data_frame
return PageSingle.layout
elif pathname == HREF_PAGE_COMMON:
PageCommon.data_frame = shared_data_frame
return PageCommon.layout
return ""
Every page builds own layout and user choose which one shown clicking on the related dcc.Link; this procedure works fine and user can change page avery time he wants.
What i want now is to build and shown the PageSingle in programmatically way, so i tried to add:
AppMain.showPageSingle()
and
def showPageSingle():
page = findPageToShow(HREF_PAGE_SINGLE)
app.layout[ID_PAGE_CONTAINER].children = [page]
#app.layout[ID_PAGE_CONTAINER].children = page
But nothing happens.
What i do wrong?
Thanks
EDIT
How suggested i use a callback raised when use click on a cell of table created into page Common
PageCommon.py
#app.callback(
Output(ID_PAGE_CONTAINER, 'children'),
[
Input(ID_TABLE_COMMON, 'active_cell'),
Input(ID_TABLE_COMMON, 'data')
]
)
def clickOnCell(active_cell, data_table):
if active_cell is not None:
row_index = active_cell['row']
col_index = active_cell['column']
import AppMain
return AppMain.buildPageSingle()
return []
BUT in this case, application doesn't respond when i choose a page, maybe because the callback is not valid.
You can add an "interval" component in this way (in your layout):
dcc.Interval(
id='interval-component',
interval=60 * 1000, # in milliseconds , 20 seconds every refresh
n_intervals=0
And then, you can refer to that component.
#app.callback(Output('live-update-time', 'children'),
[Input('interval-component', 'n_intervals')])
def update_time(n):
current_time = datetime.datetime.now()
return html.P(f"Last Update at {current_time} ")
In the above example, i refresh my page every 20 second.

Plotly Dash Callback error : Trying to hide Div components

I am trying to hide input components Div after link is clicked.
I have given the Div id='input_fields', so that i can hide it's child components but on return app1.layout, {'display': 'none'} I get the following error.
"Callback error updating display-page.children, input_fields.children"
Class
app.layout = html.Div([
dcc.Location(id='url', refresh=False),
html.Br(),
html.H1("Interpretation of Financial Statements", style={'text-align': 'center'}),
html.H1("10 year data analysis", style={'text-align': 'center'}),
html.Div([dcc.Input(id='input-1-state', type='text', placeholder="Enter Company Ticker", value=''),
dcc.Input(id='input-2-state', type='text', placeholder="Enter Company Name", value=''),
dcc.Link(' Get Analytics ', href='/apps/app1')], id='input_fields'),
html.Div(id='display-page'),
], style={"margin-left": "5%", "margin-right": "5%"})
#app.callback(Output('display-page', 'children'),
Output('input_fields', 'children'),
[Input('url', 'pathname')],
Input(component_id='input-1-state', component_property='value'),
Input(component_id='input-2-state', component_property='value'))
def display_page(pathname, ticker, company):
if pathname == '/apps/app1':
# generate links
linkGenerator.generateLinks(ticker, company)
# starting crawler
startingCrawlerClass()
return app1.layout, {'display': 'none'}
else:
return ''
The problem is the return statement of the else clause in your callback. Your callback expects two callback outpus, but you're returning one (i.e. the single empty string).
If you run the app in debug mode Dash will tell you this and also what it wants you to return instead:
dash.exceptions.InvalidCallbackReturnValue: The callback ..display-page.children...input_fields.children.. is a multi-output.
Expected the output type to be a list or tuple but got: ''.
So you could do something like this instead:
#app.callback(
Output("display-page", "children"),
Output("input_fields", "style"),
[Input("url", "pathname")],
Input(component_id="input-1-state", component_property="value"),
Input(component_id="input-2-state", component_property="value"),
)
def display_page(pathname, ticker, company):
if pathname == "/apps/app1":
return app1.layout, {"display": "none"}
else:
return '', {'display': 'block'}
So the above example returns a tuple of two elements. Each element correspons to a callback output. Also the output of the input-field is style

How to make a general callback in python dash app for use with dynamically generated component ids?

I will first describe the goal of my app and then what I have done and the issue I’m having:
Goal:
Create a dash table with an id defined by an option in a drop down menu.
Be able to add arbitrary amount of rows to that table (perhaps with a button).
Be able to create multiple instances of the above (up to the amount in the drop down menu or more or less).
So far I figured that the best way to add arbitrary amount of rows to a table is to initiate it with no rows and then have a button that appends a row to the table via:
#app.callback(
Output('table1', 'data'),
[Input('button1', 'n_clicks')],
[State('table1', 'data'),
State('table1, 'columns')])
def add_row(n_clicks, rows, columns):
if n_clicks > 0:
rows.append({c['id']: '' for c in columns})
return rows
I also managed to generate a table with associated ‘add row’ button via:
#app.callback(
Output('tables_div', 'children'),
[Input('button_add_table', 'n_clicks')],
[State('dropdown_options', 'value')])
def add_table(click, dropdownvalue):
if click != None:
table = add_new_table(str(energy)+material)
B = html.Button(
'Add Row',
id = 'add_row' + str(dropdownvalue),
className='one columns',
style={'marginTop':'25px'}
)
D = html.Div(
children=[
table
],
className='ten columns'
)
return [D,B]
else:
raise PreventUpdate
where the function to create a table is:
def add_new_table(table_id):
table = dash_table.DataTable(
id=table_id,
data=[],
columns=hvl_table_columns,
style_cell = {
'font_family': 'cursive',
'font_size': '26px',
'text_align': 'center'
},
style_header={
'backgroundColor': '#696969',
'color': 'white',
'fontWeight': 'bold'
}
)
return table
My problem is that because a table and a button are dynamically created, the callback for adding rows to a table via that button doesn’t work. Ideally I would like it to be something like:
#app.callback(
Output(str(i), 'data'),
[Input('add_row' + str(i), 'n_clicks')],
[State(str(i), 'data'),
State(str(i), 'columns')]) for i in table_ids #This being the main difference from the callback to add rows above
def add_row(n_clicks, rows, columns):
if n_clicks > 0:
rows.append({c['id']: '' for c in columns})
return rows
where table_ids is just a list of ids I generate when I create the tables and I can append to it if a new table is created in the ‘add_table’ callback.
This approach doesn’t work so I need help. I tried looking into dynamically creating callbacks as explained here but I couldn’t figure out how to implement it into my code. Maybe anyone has other ideas of how this can be done? Is it even possible to generate callbacks while the app is running? If not, then how can I make a generic callback to work for a list of ids?
Thanks

Python plotly Dash updating/refreshing layout

I have app that imports layout from second module then renders it in tab as returning app.layout to this tab content. I have problem with thing that layout is imported with default values of inputs and when I have inputs filled with keys and change between tabs then when I click back to the inputs tab the values reset to default 'x'. I managed to solve this problem by declaring layout second time in a callback but it doesn't look good and would require to write layout 2 times for further functionalities. There is my code:
if 'manager' not in vars():
manager = Recognition('xx', 'xx')
print('defining manager')
lcheck = 0
while lcheck == 0:
layout = [
html.Div([
html.Div(
[
html.H3("Primary API key"),
dcc.Input(
id='primary-key',
value=manager.api_key,
placeholder='Enter a value...',
type='text',
style = {"width":"50%"}
),
html.H3("Private API key"),
dcc.Input(
id='private-key',
value=manager.api_secret,
placeholder='Enter a value...',
type='text',
style = {"width":"50%"}
),
],
),
html.Button('Submit', id='button', className='btn btn-primary'),
html.Div(id='output-hidden')
])
]
lcheck=1
#app.callback(
Output('primary-key', 'value'),
[Input('button', 'n_clicks')],
[State('primary-key', 'value'),
State('private-key', 'value')]
)
def update_output(n_clicks, value1, value2):
values = {'value1':value1, 'value2':value2}
global manager
global layout
manager.update(value1, value2)
layout = [
html.Div([
html.Div(
[
html.H3("Primary API key"),
dcc.Input(
id='primary-key',
value=manager.api_key,
placeholder='Enter a value...',
type='text',
style = {"width":"50%"}
),
html.H3("Private API key"),
dcc.Input(
id='private-key',
value=manager.api_secret,
placeholder='Enter a value...',
type='text',
style = {"width":"50%"}
),
],
),
html.Button('Submit', id='button', className='btn btn-primary'),
html.Div(id='output-hidden')
])
]
lcheck=0
return values['value1']
As you can see it saves new layout everytime I send new data with button but this solution looks terrible I would like to find better solution without chaos in code. Whole thing is to update value=manager.api_key, and value=manager.api_secret, in layout variable. I was thinking to get access of the values like from normal list but it is not possible since it is not nested as regular list. Is there any way to access the values for example layout.getid("primary-key").value or something like this? How to clean this code?
I have figured out how to solve this problem. As my app us rendering app.layout everytime I select a tab, so it always render the layout that was assigned during first import. Firstly I wanted to override layout everytime in a callback function but it has no sense to write layout= 2 times. So I found in dash docs a dash core component named dcc.Store (it stores json in hidden div). I used it like this:
html.Div([
dcc.Store(id='storage')
],
id="hiddendata",
style={"display": "none"},
),
This div is placed directly after 'Tabs' div and is there as a 'cache' memory for tabs to remember previous state. And finally I have updated callbacks to change data in dcc.Store everytime I send new keys so when I leave the tab, other tabs know what is there:
#app.callback(
Output('storage', 'data'),
[Input('button', 'n_clicks')],
[State('primary-key', 'value'),
State('private-key', 'value')]
)
def update_output(n_clicks, value1, value2):
values = {'value1':value1, 'value2':value2}
global manager
global layout
manager.update(value1, value2)
lcheck=0
return values
#app.callback(
Output('primary-key', 'value'),
[Input('storage', 'data')],
[State('storage', 'data')])
def update_values(values, value):
if values:
return value['value1']
#app.callback(
Output('private-key', 'value'),
[Input('storage', 'data')],
[State('storage', 'data')])
def update_values(values, value):
if values:
return value['value2']

Categories