Dynamically changing content of html div using callbacks - python

I am trying to create a drop down menu with 2 options : Village Amenities and Town Amenities. If the user selects one of these options, I want the selected option to appear in the html div I have created below.
For this, I tried to use callbacks, but it doesn't seem to work.
My code is as follows :
app.layout = html.Div(
html.Div([
html.Div(children=[
dcc.Dropdown(
id='input',
options=[{'label': label, 'value': value} for label, value in zip(
['Town Amenities', 'Village Amenties'], ['Town', 'Village'])],
value="Town Amenties"
),
html.Div(id='ouput'),
], style={'width': '20%', 'display': 'inline-block'}),
.......
And the callback code is as follows :
#app.callback(
Output(component_id='output', component_property='children'),
[Input(component_id='input', component_property='value')]
)
def update_value(input_data):
return input_data
Any help or suggestions would be really appreciated. Thanks a lot !!

The output div in the layout has no set children prop. Dash struggles with unset default values, so you can fix this by setting it explicitly like this:
html.Div(id='ouput', children=[]),
The callback should work after that.

Related

Dash and Plotly - using CSS to style elements of the same class name

I would like to use a css stylesheet to keep most of the styling for my dash app in a separate file, but the problem is that a lot of my components use the name classname: className='col-2' or className='col-3'. My understanding is that using className='col-2' specifies the number of columns and helps with layout, but I want to create a boarder next to some divs and it's impossible using classnames. Is there another way to do this?
Below are two columns from my code, they both use className='col-3'. Problem is that when I create a stylesheet with background color and border, the same style is applied to my header which also has a component with col-3.
/////STYLESHEET///////
.col-3{
border: 2px solid darkgray;
background-color:#FFFFFF
}
//////DASH-APP/////// fragment of code
row1_col1 = html.Div(children=[
#DIV 6 - row
html.Div(children=[
html.Br(),
#DIV 2
html.Div(children=[
html.H4(children="Select By University",
style={'textAlign':'center','height': '25px','fontSize': 18}),
# DIV 3 selection
html.Div(children=dd1_select_univ,
style={'padding-left':'3%'})
],
#className='row',
style={'color':font_color, 'fontSize': 12},className='col-6'),
#DIV 3
html.Div(children=[
html.H4(children="Search by Publication",
style={'textAlign':'center','height': '25px','fontSize': 18}),
# DIV 5- selection
html.Div(children=inp1_public_title,
style={'padding-left':'1%'}),
],
style={ 'color':font_color, 'fontSize': 12},className='col-6')],
className='row'),
html.Div(children=[
html.Br(),
html.Div(children=[html.H4("Year Range",
style={'textAlign':'center','fontSize': 14,'height':'20px'}),
rs1_year],
className='col-12',
)],
className='row')
# ], className='row',style={'margin-left':12})
],className='col-6',
style={"background":background_color,'width':'45%','height':'40%','borderWidth':'3px','borderColor':'gray','margin-left':'1%'} )
row1_col2 = html.Div(children=[
html.Div(children=[
html.H4("The Most Popular Keyword in the Academic World",
style={'textAlign':'center','padding-top':'3%','fontSize': 20}),
html.Div(rb1_popular_keyword,
style={'height':'20%'}),
html.H5(id='val-popular-keyword', style={'padding-top':'1%',"background":'yellow'}),
]
)
] , className='col-3',style={"background":background_color,'width':'27%','margin-left':'0.5%'})
row1_col3 = html.Div(children=[
html.Div(children=[
html.H4("Number of Publications Referencing the Most Popular Keyword",
style={'textAlign':'center','padding-top':'3%','fontSize': 20,}),
html.Div(
style={"background":'red'}),
html.H5(id='val-pub_cnt', style={'padding-top':'2%','fontSize': 24})
]
)
] , className='col-3',style={"background":background_color,'width':'25%','color':font_color,'margin-left':'0.5%'})
So you want to have some styles associated with one class name, and different styles associated with another? You can do this. Define the styles for each class name in your CSS file, and then give class names to your components as needed. Example:
html.Div(
id='whatever',
children='Some text',
className='col-3 another-class-name yet-a-third-class-name'
)

Route in Dash without creating inefficient, hidden items

I am inheriting a plotly Dash app which currently has an app.layout as follows:
def serve_layout():
return html.Div(
className="app",
children=[
dcc.Location(id="url", refresh=False),
build_navbar(),
html.Div(
id="app-container",
className="main-content",
children=[
dcc.Store(id="usecase-store", storage_type="session", data=LiveGraph.use_case_library),
dcc.Store(id="gen-config-store", storage_type="session", data=LiveGraph.gen_config),
dcc.Store(id="control-config-store", storage_type="session", data=LiveGraph.control_config),
dcc.Store(id="data-config-store", storage_type="session", data=LiveGraph.data_config),
dcc.Store(id="data-store", storage_type="session"),
dcc.Store(id="liveplot-store", storage_type="session"),
build_settings_tab(),
build_simulation_tab(),
build_network_tab(),
dcc.Interval(id='graph-update', interval=1000, max_intervals=1000, n_intervals=0,
disabled=True)
],
),
],
)
where there are 3 separate routes for each of the build_* functions; with a callback setting {display: none} on the divs returned by the non-selected functions. I propose the following code:
def serve_layout():
return html.Div(
className="app",
children=[
dcc.Location(id="url", refresh=False),
build_navbar(),
html.Div(
id="app-container",
className="main-content",
children=[
dcc.Store(id="usecase-store", storage_type="session", data=LiveGraph.use_case_library),
dcc.Store(id="gen-config-store", storage_type="session", data=LiveGraph.gen_config),
dcc.Store(id="control-config-store", storage_type="session", data=LiveGraph.control_config),
dcc.Store(id="data-config-store", storage_type="session", data=LiveGraph.data_config),
dcc.Store(id="data-store", storage_type="session"),
dcc.Store(id="liveplot-store", storage_type="session"),
dcc.Interval(id='graph-update', interval=1000, max_intervals=1000, n_intervals=0,
disabled=True),
html.Div(id="main-content")
],
),
],
)
#app.callback(Output("main-content", "children"), [Input("url", "pathname")])
def route(path):
if path == "/":
return build_settings_tab()
# ...only return the individual function necessary for the path
Note that I have removed the three function calls from serve_layout() and added a div#main-content. The issue I'm running into is that I have callbacks set up to watch various inputs across the three pages, so I get an Error loading dependencies if I try to use the second code block. Is there a way around this, or do I really have to put everything in app.layout that I want to access later on? This would be really inefficient, as one of my pages creates charts and I don't want to render the charts and then set {display: none}, seems backwards and certainly sets off a lot of warning bells in my head... Are there any other solutions?

Additional elements inline with Dash's dcc.Tabs?

For my Dash app, I want to create a navigation bar that has links to the different pages but also additional stuff, e.g. the currently logged in user, and logos and stuff.
However, I unfortunately cannot use pages (Like in the "Navbar" example in dbc) since the WebApp has to be hosted as a single-url app inside another tool. My only option is to got with dcc.Tabs.
However, it looks to me like dcc.Tabs forces a newline behind the Tabs. I tried different things to prevent that, but nothing seems to be working. The best I got so far is the example below. How do I make it so that the text is in the same row as the Tabs element?
tabs_styles = {
'height': '44px', "width": "49%", "display":"inline-block"
}
app.layout = html.Div(children=[
html.Div(children=[
dcc.Tabs(id="tabs-styled-with-inline", value='tab-1', children=[
dcc.Tab(label='Page1', value='tab-1', style=tab_style, selected_style=tab_selected_style),
dcc.Tab(label='Page2', value='tab-2', style=tab_style, selected_style=tab_selected_style),
], style=tabs_styles)]),
html.Span(children=[
" Logged in as ",
html.Strong(id="username")
], style = tabs_styles),
html.Div(children=[
# Distance to header:
html.Hr(),
html.Div(id='tabs-content-inline')
])
])
Your inline style should be applied to the parent div of your Tabs component. I made some small modifications here (look at tabs_container_styles):
tabs_styles = {"height": "44px"}
tabs_container_styles = {"width": "49%", "display": "inline-block"}
app.layout = html.Div(
children=[
html.Div(
children=[
dcc.Tabs(
id="tabs-styled-with-inline",
value="tab-1",
children=[
dcc.Tab(label="Page1", value="tab-1"),
dcc.Tab(label="Page2", value="tab-2"),
],
style=tabs_styles,
)
],
style=tabs_container_styles,
),
html.Span(
children=[" Logged in as ", html.Strong(id="username")], style=tabs_styles
),
html.Div(
children=[
# Distance to header:
html.Hr(),
html.Div(id="tabs-content-inline"),
]
),
]
)
Set the parent_style property of your Tabs component instead of the style property and move your Span component to be a child of the div containing your Tabs component.
parent_style (dict; optional): Appends (inline) styles to the top-level parent container holding both the Tabs container and the content container.
style (dict; optional): Appends (inline) styles to the Tabs container holding the individual Tab components.
https://dash.plotly.com/dash-core-components/tabs
MRE
from dash import Dash, html, dcc
tabs_styles = {"height": "44px", "width": "49%", "display": "inline-block"}
app = Dash()
app.layout = html.Div(
children=[
html.Div(
children=[
dcc.Tabs(
id="tabs-styled-with-inline",
value="tab-1",
children=[
dcc.Tab(
label="Page1", value="tab-1", style={}, selected_style={}
),
dcc.Tab(
label="Page2", value="tab-2", style={}, selected_style={}
),
],
parent_style=tabs_styles,
),
html.Span(
children=[" Logged in as ", html.Strong(id="username")], style=tabs_styles
),
]
),
html.Div(
children=[
# Distance to header:
html.Hr(),
html.Div(id="tabs-content-inline"),
]
),
]
)
if __name__ == "__main__":
app.run_server()

Aligning 2 html.Divs of text next to each other in Plotly Dash

I am trying to place 2 Div elements side by side, without luck. I have been following
https://community.plotly.com/t/horizontally-stack-components/10806 and
https://medium.com/analytics-vidhya/dash-for-beginners-dash-plotly-python-fcd1fe02b749 guides on using width<50% and 'display': 'inline-block' but without luck.
If you look at attached pictures, it looks like the block that is supposed to go to the right, actually does, until i update the page by choosing a song in the list to show the lyrics on the left, then it pushes the recommendations to the bottom. Any ideas?
My code looks like this:
html.Div([
html.Div(id="lyrics",style={'width': '50%', 'display': 'inline-block'}),
html.Div([
html.H6(id="recommendations",children="Recommendations:"),
html.H3(id="songsbysameartist",children='Songs by the same artist:',
),
html.H6(id="sameartistrecommendations",
),
html.H3(id="songsfromotherartists",children='Music from other artists that you might also like:',
),
html.H6(id="otherartistrecommendations",
)
],
style = {'width': '30%', 'display': 'inline-block'})
])
edited code would look like this:
html.Div([
html.Div(id="lyrics",style={'width': '50%', 'display': 'inline-block'}),
html.Div([
html.H6(id="recommendations",children="Recommendations:"),
html.H3(id="songsbysameartist",children='Songs by the same artist:',
),
html.H6(id="sameartistrecommendations",
),
html.H3(id="songsfromotherartists",children='Music from other artists that you might also like:',
),
html.H6(id="otherartistrecommendations",
)
],
style = {'width': '30%', 'display': 'flex'})
])
since the bottom style is connected to the parent Div above recommendations as far as I can tell.
I found the solution:
I believe the error is in the structure. Parent Div needs to have "display":"flex" while the 2 children Div elements have "display":"inline-block".
Correct code ended up like this:
html.Div([
html.Div([
html.Div(id="songandartist",style={'display': 'flex',"fontSize" : 40,"marginBottom":50}),
html.Div(id="lyrics",style={'display': 'flex'}
)
],style={'width': '49%', 'display': 'inline-block'}),
html.Div([
html.Div(id="recommendations",children="Recommendations:",style={"fontSize" : 40,"marginBottom":50,'display': 'flex'}),
html.Div(id="songsbysameartist",style={"fontSize" : 27,'display':'flex'},children='Songs by the same artist:',
),
html.Div(id="sameartistrecommendations",style={"marginBottom":50,'whiteSpace': 'pre-line','display': 'flex'}
),
html.Div(id="songsfromotherartists",style={"fontSize" : 27,'display': 'flex'},children='Music from other artists that you might also like:',
),
html.Div(id="otherartistrecommendations",style={'whiteSpace': 'pre-line','display': 'flex'}
)
],
style = {'width': '49%', 'display': 'inline-block'})
],style={"display": "flex"})

Dash: Add dynamically empty plotly graph and choose axis from dropdown values using Pattern-Matching callbacks

I succeed in adding dynamically container/bloc in my dash app when clicking on a button.
Each bloc contains one graph and two dropdown (one for X axis and the other for Y axis)
Each time I update a dropdown input (X or Y) the graph axis are updated and datas are correctly plotted
It works, but...
Before I choose dropdown value, some values are inially plotted on the graph zone. And I don't want this. I would like an empty graph
enter image description here
Here is my app code:
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State, MATCH, ALL
import plotly.express as px
import plotly.graph_objects as go
import pandas as pd
app = dash.Dash(name="OUATT")
DATA = pd.read_csv('C:/Users/joris/Desktop/donnees.txt', sep=';')
print(DATA)
#graphe_test= px.scatter(DATA,x=DATA.x,y=DATA.y)
def create_figure(column_x, column_y):
return px.scatter(DATA,x=column_x,y=column_y)
app.layout = html.Div([
html.Button(" + Graphe", id="ajout-graphe", n_clicks=0),
html.Div(),
html.Div(id='bloc_graphe', children=[])
])
#app.callback( Output('bloc_graphe', 'children'),
[Input('ajout-graphe', 'n_clicks')],
[State('bloc_graphe', 'children')])
def ajouter_graphe(n_clicks, children):
nouvelle_zone_graphe = html.Div(
style={'width': '23%', 'display': 'inline-block', 'outline': 'thin lightgrey solid', 'padding': 10},
children=[
dcc.Graph(
id ={'type': 'Graphique',
'index': n_clicks}
),
dcc.Dropdown(
id={
'type':'Selection_variable_X',
'index': n_clicks
},
options=[{'label':i, 'value':i} for i in DATA.columns],
value = None
),
dcc.Dropdown(
id={
'type':'Selection_variable_Y',
'index': n_clicks
},
options=[{'label':i, 'value':i} for i in DATA.columns],
value = None
),
])
children.append(nouvelle_zone_graphe)
return children
#app.callback( Output({'type':'Graphique', 'index':MATCH},'figure'),
[Input({'type':'Selection_variable_X', 'index':MATCH}, 'value'),
Input({'type':'Selection_variable_Y', 'index':MATCH}, 'value')]
)
def display_output(column_x,column_y):
return create_figure(column_x, column_y)
if __name__ == '__main__':
app.run_server(debug=True)
My datas are basic and located in a text file:
enter image description here
I use Pattern-Matching callbacks. I'm sure I miss something in this part of my code:
#app.callback( Output({'type':'Graphique', 'index':MATCH},'figure'),
[Input({'type':'Selection_variable_X', 'index':MATCH}, 'value'),
Input({'type':'Selection_variable_Y', 'index':MATCH}, 'value')]
)
def display_output(column_x,column_y):
return create_figure(column_x, column_y)
If someone can tell me why I have not empty graph when adding a new bloc ?
Thanks a lot in advance for your support
Joe
I always suggest explicitly setting the prop you plan to update, figure in this case, to some default you want, such as {}.
It's possible the callback is running when you add the dropdowns. You can stop that by doing something like this:
#app.callback( Output({'type':'Graphique', 'index':MATCH},'figure'),
[Input({'type':'Selection_variable_X', 'index':MATCH}, 'value'),
Input({'type':'Selection_variable_Y', 'index':MATCH}, 'value')]
)
def display_output(column_x,column_y):
if column_x is None and column_y is None:
raise dash.exceptions.PreventUpdate
return create_figure(column_x, column_y)
Hopefully that keeps your figure empty until the user selects from the dropdowns.

Categories

Resources