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
Related
I have been working with plotly dash and especially django-dash for a while, and I am now facing an issue that I am not able to resolve. I am confusedbecause I have successfully used the same structure in the past. Hopefully a pair of fresh eyes could help me see what I am messing up.
Here is what I have:
=> first callback acquires data from a django session and that is used to create a dropdown menu that contains some dataframe extracted values:
#app.expanded_callback(
Output('output_two', 'children'),
[Input('dummy', 'value')]
)
def clean_data(file_path,**kwargs):
file_path = file_path
path = kwargs['request']
path = path.session.get('path')
print("path", path)
ifc_file = ifcopenshell.open(path)
work_plans = ifc_file.by_type("IfcWorKPlan")
work_plans_df = pd.DataFrame({
'id': [schedule.id() for schedule in work_plans],
'StartTime': [schedule.StartTime for schedule in work_plans],
'FinishTime': [schedule.FinishTime for schedule in work_plans],
})
work_plans_df['StartTime'] = pd.to_datetime(work_plans_df['StartTime']).dt.date
work_plans_df['Status'] = "En cours"
work_plan_id = work_plans_df['id'].unique()
return html.Div(children=[html.Div(
className='five columns',
children=[
dcc.Dropdown(
id="dropdown",
options=list({'label': Id, 'value': Id} for Id in
work_plan_id
),
value='',
),
],
),
],
)
Now, the second call should be using the submited dropdown value and use it to output something (I won't put the details of the calculations)
#app.callback(
Output('dd-output-container', 'children'),
Input('submit-val', 'n_clicks')
,State('dropdown','value')
)
def process_workplans(path, n_clicks,value,*args,**kwargs):
if value is not None:
...#do calcs
return dt.DataTable(...)
and finally here is the layout that I am using:
app.layout = html.Div([
html.Div(id="dummy"),
html.Div(id="bed_file_path", children=[], style={'display': 'none'}),
dcc.Store(id='output_one'),
# wrapper dashboard
html.Div([ # main-area
html.Div([ # row that includes everything on the same "plane"
html.Div([
html.H4("Carnet d'entretien de mon bien"), html.Hr()]),
html.Div([ # col sm-3, the entire length of the menu
html.Div([ # control the frame in the whitch the menu is displayed
html.Br(),
html.Div([
html.Div([ # this make sure the content takes the 12 spaces within the 3 sm column
html.Div(
className='twelve columns',
children=html.P(
"Sélectionner un chantier",
),
),
html.Div(id="output_two"),
], className='col-sm-12',
style={"border-left": "white solid 0.4rem", "border-right": "white solid 0.4rem"}),
html.Br(),
html.Br(),
html.A(html.Button('Afficher les chantiers', id='submit-val', n_clicks=0,
style={'border-radius': '4px', 'border': '1px solid #ff5402',
'background-color': 'white'}),
style={'margin-right': '3px', 'margin-bottom': '20px', 'margin-left': '30px'}),
html.A(html.Button('Refresh', style={'border-radius': '4px', 'border': '1px solid #ff5402',
'background-color': 'white', 'margin-bottom': '20px'}),
href='/eoq_modeling'),
html.Br(),
], className='col-sm-12',
style={"border-left": "white solid 0.4rem", "border-right": "white solid 0.4rem"}),
html.Br(),
], className='row-for-params')
], className="col-sm-3"),
html.Div([
html.Div(id='dd-output-container'),
], className="col-sm-9")
], className='row')
], className="main-area"),
], className="wrapper-dashboard")
The error that I am getting tells that dropdown cannot be found.
I can see that when the page is initialized, dropdown is not there but it gets there and the second callback is not able to update
I believe that a delay in the triggering of callback 2 could be an option but I can't find how to do that in django-dash documentation and dash app.config[‘suppress_callback_exceptions’] = True does not work with django-dash. Anyone got an idea on how to solve that problem?
Seems to me that:
You could create a dummy div & dropdown (i.e. the same structure that clean_data() will output) in your initial app.layout, then just overwrite it with new controls with your callback. i.e. that way you always have a control with the correct ID in the layout in order to avoid initialising on an incomplete layout.
Potentially suppressing the initial call to the second callback could avoid the issue?
I have a function that takes two inputs and a button to generate a table. It was working well until I placed the inputs and the button in a tab. Now the function does not recognize the button Id.
I get an error while loading the app. Function "displayDVFiltre" is not able to find component ids "VF_button" and "result". Most likely because they are generated during the "render_content" callback.
informations = html.Div([dcc.Tabs(id="tabs-styled-with-inline", value='VF', children[dcc.Tab(label='Ventes historiques', value='VF', disabled=False)], style=tabs_styles), html.Div(id='tabs-content inline')])
#app.callback(Output('tabs-content-inline', 'children'),
Input('tabs-styled-with-inline', 'value'))
def render_content(tab):
if tab == 'VF':
return VF_tab
VF_tab = dbc.Card(
dbc.CardBody(
[
html.H5("Historique des ventes immobilières"),
dbc.Input(id="cp", placeholder="Code postal", type="text"),
dbc.Input(id="cd", placeholder="Code cadastral", type="text"),
dbc.Button("Voir les ventes", id="VF_button", color="dark"),
html.Div(id="result"),
]
),
className="mt-3",
style=tab_style_inside
)
#app.callback(
Output("result", 'children'),
Input("VF_button","n_clicks"),
State("cp","value"),
State("cd","value")
)
def displayDVFiltre(click, codePostal, codeCadastral):
if click:
df = vf.valeurFonciereParCPetCodeCadastal(codePostal, codeCadastral)
print(df.columns)
return dash_table.DataTable(df.to_dict('records'), [{"name": i, "id": i} for i in df.columns])
else:
return {}
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.
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.
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']