I am new to Plotly Dash and trying to develop a multi-page App. How can I use both Sidebar and Navbar simultaneously?
I have below an example code where the Sidebar is coming above Navbar. I have tried searching similar examples but couldn't find any.
Any help would be awesome. Thanks.
import dash
import dash_bootstrap_components as dbc
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
# link fontawesome to get the chevron icons
FA = "https://use.fontawesome.com/releases/v5.8.1/css/all.css"
app = dash.Dash(external_stylesheets=[dbc.themes.BOOTSTRAP, FA])
# the style arguments for the sidebar. We use position:fixed and a fixed width
SIDEBAR_STYLE = {
"position": "fixed",
"top": 0,
"left": 0,
"bottom": 0,
"width": "16rem",
"padding": "2rem 1rem",
"background-color": "#f8f9fa",
}
# the styles for the main content position it to the right of the sidebar and
# add some padding.
CONTENT_STYLE = {
"margin-left": "18rem",
"margin-right": "2rem",
"padding": "2rem 1rem",
}
submenu_1 = [
html.Li(
# use Row and Col components to position the chevrons
dbc.Row(
[
dbc.Col("Menu 1"),
dbc.Col(
html.I(className="fas fa-chevron-right mr-3"), width="auto"
),
],
className="my-1",
),
id="submenu-1",
),
# we use the Collapse component to hide and reveal the navigation links
dbc.Collapse(
[
dbc.NavLink("Page 1.1", href="/page-1/1"),
dbc.NavLink("Page 1.2", href="/page-1/2"),
],
id="submenu-1-collapse",
),
]
submenu_2 = [
html.Li(
dbc.Row(
[
dbc.Col("Menu 2"),
dbc.Col(
html.I(className="fas fa-chevron-right mr-3"), width="auto"
),
],
className="my-1",
),
id="submenu-2",
),
dbc.Collapse(
[
dbc.NavLink("Page 2.1", href="/page-2/1"),
dbc.NavLink("Page 2.2", href="/page-2/2"),
],
id="submenu-2-collapse",
),
]
sidebar = html.Div(
[
html.H2("Sidebar", className="display-5"),
html.Hr(),
html.P(
"A sidebar with collapsible navigation links", className="lead"
),
dbc.Nav(submenu_1 + submenu_2, vertical=True),
],
style=SIDEBAR_STYLE,
id="sidebar",
)
PLOTLY_LOGO = "https://images.plot.ly/logo/new-branding/plotly-logomark.png"
search_bar = dbc.Row(
[
dbc.Col(dbc.Input(type="search", placeholder="Search")),
dbc.Col(
dbc.Button("Search", color="primary", className="ml-2"),
width="auto",
),
],
no_gutters=True,
className="ml-auto flex-nowrap mt-3 mt-md-0",
align="center",
)
navbar = dbc.Navbar(
[
html.A(
# Use row and col to control vertical alignment of logo / brand
dbc.Row(
[
dbc.Col(html.Img(src=PLOTLY_LOGO, height="30px")),
dbc.Col(dbc.NavbarBrand("Navbar", className="ml-2")),
],
align="center",
no_gutters=True,
),
href="https://plot.ly",
),
dbc.NavbarToggler(id="navbar-toggler"),
dbc.Collapse(search_bar, id="navbar-collapse", navbar=True),
],
color="dark",
dark=True,
)
content = html.Div(id="page-content", style=CONTENT_STYLE)
app.layout = html.Div([dcc.Location(id="url"), navbar,sidebar, content])
# this function is used to toggle the is_open property of each Collapse
def toggle_collapse(n, is_open):
if n:
return not is_open
return is_open
# this function applies the "open" class to rotate the chevron
def set_navitem_class(is_open):
if is_open:
return "open"
return ""
for i in [1, 2]:
app.callback(
Output(f"submenu-{i}-collapse", "is_open"),
[Input(f"submenu-{i}", "n_clicks")],
[State(f"submenu-{i}-collapse", "is_open")],
)(toggle_collapse)
app.callback(
Output(f"submenu-{i}", "className"),
[Input(f"submenu-{i}-collapse", "is_open")],
)(set_navitem_class)
#app.callback(Output("page-content", "children"), [Input("url", "pathname")])
def render_page_content(pathname):
if pathname in ["/", "/page-1/1"]:
return html.P("This is the content of page 1.1!")
elif pathname == "/page-1/2":
return html.P("This is the content of page 1.2. Yay!")
elif pathname == "/page-2/1":
return html.P("Oh cool, this is page 2.1!")
elif pathname == "/page-2/2":
return html.P("No way! This is page 2.2!")
# If the user tries to reach a different page, return a 404 message
return dbc.Jumbotron(
[
html.H1("404: Not found", className="text-danger"),
html.Hr(),
html.P(f"The pathname {pathname} was not recognised..."),
]
)
# add callback for toggling the collapse on small screens
#app.callback(
Output("navbar-collapse", "is_open"),
[Input("navbar-toggler", "n_clicks")],
[State("navbar-collapse", "is_open")],
)
def toggle_navbar_collapse(n, is_open):
if n:
return not is_open
return is_open
if __name__ == "__main__":
app.run_server(port=8888, debug=True)
This same issue was reported in the dash-bootstrap-components GitHub repo: https://github.com/facultyai/dash-bootstrap-components/issues/321. The recommendation was to adjust the sidebar padding:
SIDEBAR_STYLE = {
...,
"padding": "4rem 1rem 2rem",
...,
}
Related
I want to trigger an event if the checkbox of a dash-table is clicked. When I start the webapp, I see below output
* Serving Flask app 'app'
* Debug mode: on
aaaa
[]
aaaa
[]
where "aaaa" indicating the callback is triggered. But when I click the checkbox after the initial load, nothing happens. I would expect that if I click the checkbox of a table, the callback-function dummy was triggered. Why is it not triggered?
import dash
import dash_bootstrap_components as dbc
from dash import html
from dash import dcc, dash_table
import plotly.express as px
from dash.dependencies import Input, Output
import pandas as pd
# data source: https://www.kaggle.com/chubak/iranian-students-from-1968-to-2017
# data owner: Chubak Bidpaa
df = pd.read_csv('https://raw.githubusercontent.com/Coding-with-Adam/Dash-by-Plotly/master/Bootstrap/Side-Bar/iranian_students.csv')
app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
# styling the sidebar
SIDEBAR_STYLE = {
"position": "fixed",
"top": 0,
"left": 0,
"bottom": 0,
"width": "16rem",
"padding": "2rem 1rem",
"background-color": "#f8f9fa",
}
# padding for the page content
CONTENT_STYLE = {
"margin-left": "18rem",
"margin-right": "2rem",
"padding": "2rem 1rem",
}
sidebar = html.Div(
[
html.H2("", className="display-4"),
html.Hr(),
html.H2("Sidebar", className="display-4"),
html.Hr(),
html.P(
"Number of students per education level", className="lead"
),
dbc.Nav(
[
dbc.NavLink("Home", href="/", active="exact"),
dbc.NavLink("Page 1", href="/page-1", active="exact"),
dbc.NavLink("Page 2", href="/page-2", active="exact"),
],
vertical=True,
pills=True,
),
],
style=SIDEBAR_STYLE,
)
navbar = dbc.NavbarSimple(
children=[
dbc.NavItem(dbc.NavLink("Page 1", href="#")),
dbc.DropdownMenu(
children=[
dbc.DropdownMenuItem("More pages", header=True),
dbc.DropdownMenuItem("Page 2", href="#"),
dbc.DropdownMenuItem("Page 3", href="#"),
],
nav=True,
in_navbar=True,
label="More",
),
],
brand="NavbarSimple",
brand_href="#",
color="primary",
dark=True,
)
fig1 = px.bar(df, barmode='group', x='Years',
y=['Girls Kindergarten', 'Boys Kindergarten'])
fig1.update_layout(plot_bgcolor='rgb(10,10,10)')
children1 = [html.H1('Kindergarten in Iran',
style={'textAlign':'center'}),
dcc.Graph(id='bargraph',
figure=fig1),
dash_table.DataTable(
id='datatable-interactivity',
columns=[
{"name": i, "id": i, "deletable": True, "selectable": True} for i in df.columns
],
data=df.to_dict('records'),
editable=True,
filter_action="native",
sort_action="native",
sort_mode="multi",
column_selectable="single",
row_selectable="multi",
row_deletable=True,
selected_columns=[],
selected_rows=[],
page_action="native",
page_current= 0,
page_size= 10,
)]
content = html.Div(id="page-content", children=children1, style=CONTENT_STYLE)
app.layout = html.Div([
dcc.Location(id="url"),
sidebar,
navbar,
content
])
#fig1.update_layout(plot_bgcolor='rgb(10,10,10)')
"""
def update_styles(selected_columns):
return [{
'if': { 'column_id': i },
'background_color': '#D2F3FF'
} for i in selected_columns]
"""
#app.callback(
Output('datatable-interactivity', 'style_data_conditional'),
Input('datatable-interactivity', 'selected_columns')
)
def dummy(selected_columns):
print("aaaa")
print(selected_columns)
#app.callback(
Output("page-content", "children"),
[Input("url", "pathname")]
)
def render_page_content(pathname):
if pathname == "/":
return children1
elif pathname == "/page-1":
return children1
elif pathname == "/page-2":
return children1
# If the user tries to reach a different page, return a 404 message
return dbc.Jumbotron(
[
html.H1("404: Not found", className="text-danger"),
html.Hr(),
html.P(f"The pathname {pathname} was not recognised..."),
]
)
if __name__=='__main__':
app.run_server(debug=True)
The dummy function accepts a wrong input parameter. I have not worked with selected_columns, but selected_rows so far. A function input needed to be "chosen_rows" then for the selected_rows.
I am new to dash and am having trouble with writing a more complex callback function. The steps to my app are:
press a button to open a modal popup.
select options from a drop-down on the popup, which changes input options.
choose the desired input options
click a "submit" button to print (ultimately write) the input data.
I suspect I need either pattern matching callbacks, or chained callbacks because the errors I get say:
"Attempting to connect a callback Input item to component:
"id"
but no components with that id exist in the layout."
This is because the inputs don't appear until the modal popup and drop-down are activated.
Below is a minimal example with all the important layout features and should run on any machine. I specifically need help getting the last callback in the proper format to print the selected data from the modal drop-down.
Thanks!
import time
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
import dash_bootstrap_components as dbc
from dash_bootstrap_templates import load_figure_template
################################################################################################
app = dash.Dash(external_stylesheets=[dbc.themes.CERULEAN])
report_list = [
{"label": "drop down 1", "value": 1},
{"label": "drop down 2", "value": 2},
]
body1 = html.Div([
html.H6(
"Enter value (required)"
),
dcc.Input(
id = 'report-1',
type = 'text',
placeholder = "value",
debounce=True,
required=True,
),
html.Div(html.Br(),),
html.H6(
"Choose a new reach type (required)"
),
dcc.RadioItems(
id = 'type_radio',
options=[
{'label':'radio1','value':1},
{'label':'radio2', 'value':2},],
className='btn-group-vertical p-2'),
])
body2 = html.Div([
html.H6(
"Enter value (required)",
),
dcc.Input(
id = 'report-2',
type = 'text',
placeholder = "value",
debounce=True,
required=True,
),
html.Div(html.Br(),),
html.H6(
"Input 1 (required)",
),
dcc.Input(
id = 'input1',
type = 'text',
placeholder = "input 1",
debounce=True,
required=True,
),
html.Div(html.Br(),),
html.H6(
"Input 2 (required)"),
dcc.Input(
id = 'input2',
type = 'text',
placeholder = "input 2",
debounce=True,
required=True,
),
])
################################################################################################
report_overlay = dbc.Modal(
[
dbc.ModalBody(
html.Div(children=[
html.Div([
'Description...'],
id="report-md"),
html.Div([
html.H5([
'Choose an option:'
]),
]),
html.Div([
dcc.Dropdown(
id='Report_DropBox',
options=report_list,
style={
# "textAlign":"center",
"width":"60%",
}
),
]),
html.Div([html.Br()]),
html.Div(id='report-options'), #different options for reporting a reach.
html.Div([html.Br()]),
html.Div([
dbc.Button(
"Submit",
id="report-submit",
outline=False,
color="secondary",
size="sm",
style={
"textTransform": "none",
"width":"30%"
},
),
]),
],
),
),
html.Div([html.Br()]),
html.Div(
id='submit_status',
style={"textAlign":"center", "width":"50%"}), #print status of submission.
html.Div([html.Br()]),
dbc.ModalFooter(
dbc.Button(
"Close",
id="report-close",
className="howto-bn"
)),
],
id="report-modal",
size="lg",
)
################################################################################################
app.layout = html.Div([
html.Br(),
dbc.Button(
"Report Reach",
id="report-open",
outline=False,
color="red",
style={
# "textTransform": "none",
"margin-right": "5px",
"color":"white",
"background-color":"#C42828",
"textAlign":"center",
},
),
report_overlay,
],style={"textAlign":"center"},
)
################################################################################################
##### callbacks
# Callback for modal popup
#app.callback(
Output("report-modal", "is_open"),
[Input("report-open", "n_clicks"), Input("report-close", "n_clicks")],
[State("report-modal", "is_open")],
)
def toggle_modal(n3, n4, is_open):
if n3 or n4:
return not is_open
return is_open
# Callback for different layouts based on drop down.
#app.callback(Output('report-options', 'children'),
Input('Report_DropBox', 'value'))
def render_content(report):
if report == 1:
return body1
if report == 2:
return body2
#attempted callback to print data based on modal input.
#app.callback(
Output("submit_status", "children"),
[
Input("Report_DropBox", "value"),
Input("report-1", "value"),
Input("report-2", "value"),
Input("type_radio", "value"),
Input("input1", "value"),
Input("input2", "value"),
Input("report-submit","n_clicks")
],
)
def print_report(dropbox, r1, r2, type, ip1, ip2, submit):
if submit:
if dropbox == 1:
r = r1
data = type
date_stamp = time.strftime("%d-%b-%Y %H:%M:%S", time.gmtime())
List = [r,data,date_stamp]
statement = html.Div([List])
elif dropbox == 2:
r = r2
data1 = ip1
data2 = ip2
date_stamp = time.strftime("%d-%b-%Y %H:%M:%S", time.gmtime())
List = [r,data1,data2,date_stamp]
statement = html.Div([List])
return statement
if __name__ == '__main__':
app.run_server(debug=True)
I am trying to program a sidebar with some filters but the callback update_output of this dropdown is not working (not throwing an error, just doing nothing).
I think it is because this dropdown is not in the main layout, because my layout is composed of the sidebar and a content. My dropdowns in this sidebar will be filters that will be applied to the dataframe that feeds the graphs of the dashboard and dashboard2 layouts.
My question is how or where should I program those callbacks to give functionality to my sidebar dropdowns?
sidebar = html.Div(
[
html.H2("Sidebar", className="display-4"),
html.Hr(),
html.P(
"Sidebar", className="lead"
),
dbc.Nav(
[
dbc.NavLink("Home", href="/", active="exact"),
dbc.NavLink("Page 1", href="/page-1", active="exact"),
dbc.NavLink("Page 2", href="/page-2", active="exact"),
],
vertical=True,
pills=True,
),
dcc.Dropdown(id='dropdown',value="City"),
html.Br(),
dcc.Dropdown(id='dropdown2',options=[])
],
style=SIDEBAR_STYLE,
)
content = html.Div(id="page-content", children=[], style=CONTENT_STYLE)
app.layout = html.Div([
dcc.Location(id="url"),
sidebar,
content
])
#app.callback(
Output('dropdown2', 'options'),
Input('dropdown', 'value')
)
def update_output(value):
return df[df["cities"].isin(value)]
#app.callback(
Output("page-content", "children"),
[Input("url", "pathname")]
)
def render_page_content(pathname):
if pathname == "/":
return dashboard.layout
elif pathname == "/page-1":
return dashboard.layout
elif pathname == "/page-2":
return dashboard2.layout
# return a 404 message when user tries to reach a different page
return dbc.Jumbotron(
[
html.H1("404: Not found", className="text-danger"),
html.Hr(),
html.P(f"The pathname {pathname} was not recognised..."),
]
)
if __name__=='__main__':
app.run_server(debug=True, port=8000)
I find checklists to be better for this purpose. If you're open to using that instead of dropdown menu to filter your dataframe, then the complete snippet below will produce the following Plotly Dash App.
Complete code:
from jupyter_dash import JupyterDash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State, ClientsideFunction
import dash_bootstrap_components as dbc
import dash_core_components as dcc
import pandas as pd
import plotly.graph_objs as go
import numpy as np
import plotly.express as px
app = JupyterDash(external_stylesheets=[dbc.themes.SLATE])
df = px.data.stocks()
df = df.set_index('date')
controls = dbc.Card(
[
dbc.FormGroup(
[
dbc.Label("Checklist"),
dcc.Checklist(
id="column_select",
options=[{"label":col, "value": col} for col in df.columns],
value=[df.columns[0]],
labelStyle={'display': 'inline-block', 'width': '12em', 'line-height':'0.5em'}
),
],
),
],
body=True,
style = {'font-size': 'large'}
)
app.layout = dbc.Container(
[
html.H1("Dropdowns and checklists"),
html.Hr(),
dbc.Row([
dbc.Col([controls],xs = 4),
dbc.Col([
dbc.Row([
dbc.Col(dcc.Graph(id="graph"), style={'height': '420px'}),
])
]),
]),
],
fluid=True,
)
#app.callback(
Output("graph", "figure"),
[Input("column_select", "value"),],
)
def make_graph(cols):
fig = px.line(df, x = df.index, y = cols, template = 'plotly_dark')
return fig
app.run_server(mode='external', port = 8982)
I am looking to add a scroll bar to a side bar panel for a Dash application - see this app as an example. I can get the scroll bar to show up dynamically when content runs off the page, but the only way I can seem to get the main page div to show up to the right of the sidebar div is to set the CSS style "position": "fixed". However, that setting keeps the content fixed in place even when a scroll bar is available.
Here is the code:
import dash
import dash_bootstrap_components as dbc
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
app = dash.Dash(external_stylesheets=[dbc.themes.BOOTSTRAP])
# the style arguments for the sidebar. We use position:fixed and a fixed width
SIDEBAR_STYLE = {
"position": "fixed",
"top": 0,
"left": 0,
"bottom": 0,
"width": "16rem",
"padding": "2rem 1rem",
"background-color": "#f8f9fa",
}
# the styles for the main content position it to the right of the sidebar and
# add some padding.
CONTENT_STYLE = {
"margin-left": "18rem",
"margin-right": "2rem",
"padding": "2rem 1rem",
"display": "inline-block"
}
sidebar = html.Div(
[
html.H2("Sidebar", className="display-4"),
html.Hr(),
html.P(
"A simple sidebar", className="lead"
),
],
style=SIDEBAR_STYLE,
)
maindiv = html.Div(
id="first-div",
children=[
# first row
html.Div([
html.H2("First Row"),
html.Hr(),
html.P(
"First row stuff", className="lead"
)
]),
# second row
html.Div([
html.H2("Second Row"),
html.Hr(),
html.P(
"Second row stuff", className="lead"
)
])
],
style=CONTENT_STYLE
)
app.layout = html.Div([sidebar, maindiv])
if __name__ == "__main__":
app.run_server(port=8888)
Okay.. it was as simple as adding "overflow": "scroll" in my SIDEBAR_STYLE dict.
I'm trying to 'Pythonify' card generation in a function, but it seems to not be popping up. Perhaps I'm doing something wrong, or perhaps the dashboard I'm building needs to be quite vertical. Basically, the cardify function doesn't generate a card.
Thanks for the help in advance!
import dash
import dash_bootstrap_components as dbc
import dash_html_components as html
app = dash.Dash(external_stylesheets=[dbc.themes.MINTY])
def cardify(metricname, metricvalue, recapstring):
dbc.Card(
[
dbc.CardBody(
[
html.H2(metricname, className="card-title"),
html.H4(metricvalue, className="card-title"),
html.P(
recapstring,
className="card-text",
),
]
),
],
# style={"width": "18rem"},
)
app.layout = \
html.Div(
[
dbc.Row(
[
dbc.Col(
cardify("Card Title", "Metric Value", "Some Quick Example yada yada"),
width={"size": 2, "order": 1, "offset": 1},
),
dbc.Col(
dbc.Card(
[
dbc.CardBody(
[
html.H4("Card title", className="card-title"),
html.P(
"Some quick example text to build on the card title and "
"make up the bulk of the card's content.",
className="card-text",
),
]
),
],
# style={"width": "18rem"},
),
width={"size": 2, "order": 2},
)
]
),
]
)
if __name__ == "__main__":
app.run_server(debug=True)
Your function cardify needs to return dbc.Card.