Hey guys,
I use Dash Bootstrap Components and was wondering why the same Graph does display on Tab 1 but not on Tab 2? I think it has to do with the displaying, since downloading the PNG of the Graph on Tab 2 shows the full Graph.
import dash
import dash_bootstrap_components as dbc
import dash_core_components as dcc
import dash_html_components as html
body = dbc.Container(dcc.Graph(figure={"data": [{"x": [1, 2, 3], "y": [1, 4, 9]}]}))
body2 = dbc.Container(dcc.Graph(figure={"data": [{"x": [1, 2, 3], "y": [1, 4, 9]}]}))
tabs = dbc.Tabs(
[
dbc.Tab(body, label="Tab 1"),
dbc.Tab(body2, label="Tab 2")
]
)
app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
app.layout = html.Div([tabs])
if __name__ == "__main__":
app.run_server()
I think that’s related to this known issue that if the dcc.Graph
is unable to determine the height and width of its parent when created, it falls back on some default and doesn’t rerender when the size of the parent changes. The reason this is a problem for dbc.Tabs
is that that dbc.Tabs
works by setting display: none
on all but the currently visible Tab
, so you’re precisely in the situation that the dcc.Graph
can’t tell the size of the parent.
You can solve the problem by using a callback to render the content of the tabs for you. You can probably also manually set the height and width of the graph, but it will be harder to get the size right on different screens.
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
body = dbc.Container(
dcc.Graph(figure={"data": [{"x": [1, 2, 3], "y": [1, 4, 9]}]})
)
body2 = dbc.Container(
dcc.Graph(figure={"data": [{"x": [1, 2, 3], "y": [1, 4, 9]}]})
)
tabs = dbc.Tabs(
[
dbc.Tab(label="Tab 1", tab_id="tab-1"),
dbc.Tab(label="Tab 2", tab_id="tab-2"),
],
id="tabs",
)
app = dash.Dash(external_stylesheets=[dbc.themes.BOOTSTRAP])
app.layout = html.Div([tabs, html.Div(id="content")])
@app.callback(Output("content", "children"), [Input("tabs", "active_tab")])
def display_tab_content(active_tab):
if active_tab == "tab-1":
return body
elif active_tab == "tab-2":
return body2
return "Something went wrong..."
if __name__ == "__main__":
app.run_server(debug=True)
Thanks for the answer. I managed to play around with manually setting the size through fig.update_layout, but as you said, it’s a bit of a nightmare. I’m currently trying to set autosize=True but that doesn’t seem to work.
Regarding your workaround, unfortunately, the different Tabs in my app need to share information with each other and once I switch to the next Tab, this information gets lost. I have an expensive calculation on Tab1 that tries to write the results to a DataTable in Tab2. Since I supress Callback_Exceptions, the function from Tab1 just waits until I switch to Tab2 and then fails because it cannot reach Tab1 anymore.
Do you have a suggestion how to circumvent that?
Check out this example which uses a dcc.Store
to save the results of an expensive calculation and share it between the two tabs. It’s very likely you’d be able to adapt that to your needs.
This was exactly what I needed. Thanks a lot!
However, it seems the speed/responsiveness of the app is noticably slower now. Apparently it doesn’t matter if I define the Tabs beforehand or if I return the Tabs as a result of the “rendering tabs” callback. I’ll have to see how to improve that aspect.