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.
Related
I have a code where i use plotly dash radio item to pick from different tests ('weekness', 'std', etc.) The main code is shown below.
def plots(self,):
df_lists = self.df_lists
plots_names = ['weakness', 'std', 'std_average', 'std_weak', 'p_mean', 'p_mean_average', 'p_median', 'p_median_average','range', 'p_range_average']
colors = {'background': '#111111', 'text': '#7FDBFF'}
from dash import Dash, dcc, html, Input, Output
names = self.names
app = Dash()
app.layout = html.Div( children=[
html.H4('Dieharder Tests Plots'),
dcc.Graph(id="bar-plot", style={'width':'80%', 'height': '70vh','display':'inline-block'}),
html.Div([ # this Div contains our output graph
dcc.Graph(id='side-plot', )], style={'width':'20%', 'height':'70vh','display':'inline-block'}),
html.P('Test Description'),
dcc.Markdown(id='test-explain', link_target="_blank", ),
html.P('Chose Plot Type'),
dcc.RadioItems(plots_names, plots_names[0], id="plot-picker", ),
html.P("Filter by test:"),
dcc.Dropdown(names, names[0], id="test-picker", multi = True),
])
Now, i want to display an explanation to the tests whenever one is picked using the radio item button. I am using the following #app.callback code: (note i am just displaying one text for the 'weakness' test)
#app.callback(
Output('est-explain', 'children'), [Input("test-picker", "value")])
def update_chart_info(picker_test):
if picker_test == 'weakness':
text = "This test counts how many weak results are there for each test for all rounds and return the number divided by the number of rounds"
return [text]
however, i am not getting anything on the dashboard when i run the code. I mean, the code works for other callbacks (that are not shown here) however it is not working for the callback for displaying the text. I tried different dcc components, such as html and markdown, but still can't see the explanation displayed.
any suggestions?
Thanks
There's a few issues that are probably preventing your callback from working as intended:
you have a typo in your callback arguments and Output('est-explain', 'children') should be Output('test-explain', 'children')
you mentioned you want the Dash app to update based on the selection from the Radioitem component, but Input("test-picker", "value") refers to the id for your Dropdown component, so you probably want to change it to Input("plot-picker", "value")
you'll also need a condition in your callback for when the Radioitem selection isn't "weakness" – otherwise you'll get something like UnboundLocalError: local variable 'text' referenced before assignment whenever someone selects another radioitem because update_chart_info won't know what text is supposed to be (for simplicity, I am returning an empty string whenever anything option besides "weakness" is selected, but you can modify this as needed)
The following should work:
#app.callback(
Output('test-explain', 'children'),
Input('plot-picker', 'value')
)
def update_chart_info(radio_selection):
if radio_selection == 'weakness':
text = "This test counts how many weak results are there for each test for all rounds and return the number divided by the number of rounds"
else:
text = ""
return [text]
And here is a small working example:
import plotly.graph_objects as go
from dash import Dash, dcc, html, Input, Output
plots_names = ['weakness', 'std', 'std_average', 'std_weak', 'p_mean', 'p_mean_average', 'p_median', 'p_median_average','range', 'p_range_average']
colors = {'background': '#111111', 'text': '#7FDBFF'}
## sample names
names = ['weakness','other']
app = Dash(__name__)
app.layout = html.Div(
[
html.H4('Dieharder Tests Plots'),
dcc.Graph(id="bar-plot", style={'width':'80%', 'height': '70vh','display':'inline-block'}),
html.Div([
# this Div contains our output graph
dcc.Graph(id='side-plot', )
], style={'width':'20%', 'height':'70vh','display':'inline-block'}),
html.P('Test Description'),
dcc.Markdown(id='test-explain', link_target="_blank", ),
html.P('Chose Plot Type'),
dcc.RadioItems(plots_names, plots_names[0], id="plot-picker", ),
html.P("Filter by test:"),
dcc.Dropdown(names, names[0], id="test-picker", multi = True),
]
)
#app.callback(
Output('test-explain', 'children'),
Input('plot-picker', 'value')
)
def update_chart_info(radio_selection):
if radio_selection == 'weakness':
text = "This test counts how many weak results are there for each test for all rounds and return the number divided by the number of rounds"
else:
text = ""
return [text]
app.run_server(debug=True)
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'm having trouble getting multiple inputs and outputs to work in Dash-leaflet. Essentially, I want to do the following: on a mouse click place a marker on the map, and when hovering over a country highlight an area of the map with a polygon and display some information about the country being hovered over in a different pane. I implemented each of those functions in a separate app.callback, as in the below code.
app = dash.Dash(__name__)
app.layout = html.Div(children=[
dl.Map(children=[dl.TileLayer(),
dl.LayerGroup(id="layer"),
dl.GeoJSON(url='https://pkgstore.datahub.io/core/geo-countries/countries/archive/23f420f929e0e09c39d916b8aaa166fb/countries.geojson', id="countries",
options=dict(style=dict(opacity=0,fillOpacity=0)),
hoverStyle=arrow_function(dict(weight=2, color='red', dashArray='', opacity=1)))
],
style={'width': '1000px', 'height': '500px'}, zoom=3, id='map'),
html.Div(id='country'),
html.Div(id='Country info pane')
])
#app.callback(Output("layer", "children"), [Input("countries", "click_feature")])
def map_click(feature):
return [dl.Marker(position=[np.random.randint(-90,90),np.random.randint(-185,185)])]
#app.callback(Output('Country info pane', 'children'),
Input('countries', 'hover_feature'))
def country_hover(feature):
if feature is not None:
country_id = feature['properties']['ISO_A3']
return country_id
#app.callback(Output('layer', 'children'),
Input('countries', 'hover_feature'))
def country_hover(feature):
if feature is not None:
return(dl.Polygon(positions=[[30,40],[50,60]], color='#ff002d', opacity=0.2))
if __name__ == '__main__':
app.run_server(debug=False)
While each of those callbacks works in isolation (i.e. if I comment out the other callbacks), none of them work if I run the code with two or more of them active.
If I combine the two hover_feature functions like the below, they seem to work together.
#app.callback(Output('Country info pane', 'children'),
Output('layer', 'children'),
Input('countries', 'hover_feature'))
def country_hover(feature):
if feature is not None:
country_id = feature['properties']['ISO_A3']
return country_id, dl.Polygon(positions=[[30,40],[50,60]], color='#ff002d', opacity=0.2)
However, the "map_click" function still does not work along with the "country_hover" function. How can I get all three callbacks to work?
I found out how to do this, I had to add another dl.LayerGroup to the dl.Map object with a different id, then assigned each function to output to a different layer.
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
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']