Skip to content
This repository was archived by the owner on Jun 3, 2024. It is now read-only.

Dash objects reset their height to 450px after changing tabs #922

Open
gtg489p opened this issue Jan 26, 2021 · 20 comments
Open

Dash objects reset their height to 450px after changing tabs #922

gtg489p opened this issue Jan 26, 2021 · 20 comments
Labels

Comments

@gtg489p
Copy link

gtg489p commented Jan 26, 2021

This problem started after I upgraded to dash 1.19.0 (possibly a dash-core-components 1.15.0 issue??)

I have an app with multiple tabs, with dash components on them like charts and indicators. I set the component heights using update_layout. When I first load the page, everything looks fine. But when I click on a different tab, and then go back to the previous tab, all the dash charts and indicators change their height to 450px. The indicators are set in my code to be 200px, and when they snap to 450px after changing tabs, the indicators components themselves stretch outside of their containing DIVs.

Can confirm this was not an issue in Dash 1.16 and was recently introduced.

As well as getting this on a backlog for a fix in future release, I'm also interested if anyone has a clever workaround to prevent this right now while using the current dash version.

Thanks,
Nathan

@alexcjohnson
Copy link
Collaborator

Thanks for the report @gtg489p - can you make a self-contained example app that shows the problem? There are a number of ways to set sizes so it's important that we're talking about the same thing.

@gtg489p
Copy link
Author

gtg489p commented Jan 27, 2021

I can try for a full fledged example when time allows, but for the time being - to address your specific concern, here is how I set the height of the figures in my python code

fig.update_layout(height=200)

It is set once at initialization, and no callbacks ever touch it again.

Thanks,
Nathan

@AnnMarieW
Copy link
Contributor

AnnMarieW commented Jan 27, 2021

Here is a small app that shows the problem. It's a new issue in 1.19.0. It works fine in 1.18.1

import dash
import dash_html_components as html
import dash_core_components as dcc
import plotly.express as px
import pandas as pd


external_stylesheets = ["https://codepen.io/chriddyp/pen/bWLwgP.css"]

app = dash.Dash(__name__, external_stylesheets=external_stylesheets)

df = pd.DataFrame(
    {
        "Fruit": ["Apples", "Oranges", "Bananas", "Apples", "Oranges", "Bananas"],
        "Amount": [4, 1, 2, 2, 4, 5],
        "City": ["SF", "SF", "SF", "Montreal", "Montreal", "Montreal"],
    }
)

fig = px.bar(df, x="Fruit", y="Amount", color="City", barmode="group")
fig.update_layout(height=200)


tab1 = html.Div([html.H3("Tab content 1"), dcc.Graph(id="example-graph1", figure=fig)])
tab2 = html.Div([html.H3("Tab content 2"), dcc.Graph(id="example-graph2", figure=fig)])

app.layout = html.Div(
    [
        dcc.Tabs(
            id="tabs",
            value="tab-1",
            children=[
                dcc.Tab(label="Tab one", value="tab-1", children=tab1),
                dcc.Tab(label="Tab two", value="tab-2", children=tab2),
            ],
        ),
    ]
)


if __name__ == "__main__":
    app.run_server(debug=True)

tabs_graph_height_issue

@alexcjohnson alexcjohnson transferred this issue from plotly/dash Jan 27, 2021
@alexcjohnson
Copy link
Collaborator

Thanks @gtg489p and @AnnMarieW - moved to dcc as that's ultimately where the problem is. This is probably a result of #905

@AnnMarieW
Copy link
Contributor

Hey @gtg489p

A workaround is to add the height in the style parameter of dcc.Graph:
This works with 1.19.0

tab1 = html.Div(
    [
        html.H3("Tab content 1"),
        dcc.Graph(id="example-graph1", figure=fig, style={"height": 200})
    ]
)

@gtg489p
Copy link
Author

gtg489p commented Jan 28, 2021

Thanks @AnnMarieW , I'll see to using that in the mean time (changed 200 to "200px")

Off topic, but how did you create that gif illustrating the bug? I know there are gif maker programs out there but just wondering if there's a commonly accepted way. Thanks

@AnnMarieW
Copy link
Contributor

@gtg489p Happy to help :-)

I'm not sure about the commonly accepted way to make a gif, but on Linux I use Peek and on Windows I used ScreenToGif. Both are pretty simple, which makes them easy to use.

@ppfreitas
Copy link

Any other workaround on this? Setting the height in the dcc.Graph does not work for me as I want to have different dcc.Graph and figure heights (and using 'overflowY': 'scroll').

@nhoover
Copy link

nhoover commented Sep 11, 2021

@ann-marie-ward thanks for this workaround. It works for me but is clunky to have to specify the height by style when my dcc.Graph objects (a plotly Table and a line_mapbox) already specify height via their height parameters.

In my case, my two tabs initially render correctly, click to the second one, good. Click back to the first, and the height changes to 450px. This is using 1.21. If I add your style parameter to both dcc.Graph objects, it works correctly.

@Admolly
Copy link

Admolly commented Oct 7, 2021

Experiencing the same problem as ppfreitas. Having another workaround/fix would be nice!

@mcsewhoy
Copy link

Same problem here! The workaround works well though, thanks @AnnMarieW . But a fix would be good since now I am styling both the figure and graph in two places, when one should be enough

@jo47011
Copy link

jo47011 commented Jan 18, 2022

Is this issue solved in Dash 2.x?

@daviddavo
Copy link

No, it's still not solved

@yuenherny
Copy link

yuenherny commented Aug 20, 2022

This issue happened when I changed one tab (showing a heatmap with default aspect ratio) to another tab (showing a bar chart). I tried the style parameter workaround as suggested:

Hey @gtg489p

A workaround is to add the height in the style parameter of dcc.Graph: This works with 1.19.0

tab1 = html.Div(
    [
        html.H3("Tab content 1"),
        dcc.Graph(id="example-graph1", figure=fig, style={"height": 200})
    ]
)

but still to no avail. I noticed that this issue only happens when I changed tabs from the heatmap tab, and the tab with issue is the subsequent tab from heatmap tab (where I am using px.imshow.

So I added the aspect='auto' parameter in px.imshow so that it follows the size of plot area. Now it works fine.

I am using:

  • python=3.10.5
  • dash=2.6.1
  • dash-bootstrap-components=1.2.1

@lnkov
Copy link

lnkov commented Sep 2, 2022

Experiencing same bug. Switching tabs resets height of Dropdown options container, previously set by maxHeight argument.
image

@Lxstr
Copy link

Lxstr commented Sep 6, 2022

I'm still having this issue as my charts are dynamic heights based on internal variables.

@IsraelCea
Copy link

Here's a way to solve it.
I used @AnnMarieW 's code for this example.
Just added a Callback that uses the dcc.Tabs value as input to set the size of the graph everytime you switch tabs.
It uses the current state of both graphs to transform it into a go.Figure (from plotly.graph_objects) and sets the layout's height you need.

It just causes a little blink in the chart and you can see how it changes sizes, but is only just a microsecond, so, if it's not a major problem for you, it works :)

WhatsApp.Video.2022-11-17.at.17.37.49.4.mp4

import dash
import dash_html_components as html
import dash_core_components as dcc
import plotly.express as px
import pandas as pd
from dash import Input, Output, State, ctx
from dash.exceptions import PreventUpdate
import plotly.graph_objects as go

external_stylesheets = ["https://codepen.io/chriddyp/pen/bWLwgP.css"]

app = dash.Dash(__name__, external_stylesheets=external_stylesheets)

df = pd.DataFrame(
    {
        "Fruit": ["Apples", "Oranges", "Bananas", "Apples", "Oranges", "Bananas"],
        "Amount": [4, 1, 2, 2, 4, 5],
        "City": ["SF", "SF", "SF", "Montreal", "Montreal", "Montreal"],
    }
)

fig = px.bar(df, x="Fruit", y="Amount", color="City", barmode="group")
fig.update_layout(height=200)


tab1 = html.Div([html.H3("Tab content 1"), dcc.Graph(id="example-graph1", figure=fig)])
tab2 = html.Div([html.H3("Tab content 2"), dcc.Graph(id="example-graph2", figure=fig)])

app.layout = html.Div(
    [
        dcc.Tabs(
            id="tabs",
            value="tab-1",
            children=[
                dcc.Tab(label="Tab one", value="tab-1", children=tab1),
                dcc.Tab(label="Tab two", value="tab-2", children=tab2),
            ],
        ),
    ]
)

@app.callback(
    [Output('example-graph1','figure'),
    Output('example-graph2','figure')],
    Input('tabs','value'),
    State('example-graph1','figure'),
    State('example-graph2','figure')
)

def controlGraphSizes(tabValue, figure1,figure2):

    if tabValue == 'tab-1':
        print(type(figure1))
        newFigure1 = go.Figure(figure1)
        newFigure1.update_layout(height=200)
        return [newFigure1,dash.no_update]
    
    elif tabValue == 'tab-2':
        print(type(figure2))
        newFigure2 = go.Figure(figure1)
        newFigure2.update_layout(height=200)
        return [dash.no_update,newFigure2]

    else:
        raise PreventUpdate

if __name__ == "__main__":
    app.run_server(debug=True)

@MitchMedeiros
Copy link

MitchMedeiros commented May 27, 2023

Here's an example of a clientside callback for faster updating if your graph takes a while to create and specifying the height in style isn't a solution for you. Note that also specifying the height in the dcc.Graph style will likely overwrite the callback output.

The dcc.Tabs components has id='tabs', the dcc.Graph has id='plot' and the dcc.Graph is inside tab 1 with default value='tab-1'. The updated graph will have a height of 450px.

from dash import clientside_callback, Input, Output, State

clientside_callback(
    """
    function adjust_height(tab_value, plot) {
        if (tab_value === 'tab-1') {
            var plot = Object.assign({}, plot);
            plot.layout.height = 450;
            return plot;
        }
        return plot;
    }
    """,
    Output('plot', 'figure', allow_duplicate=True),
    Input('tabs', 'value'),
    State('plot', 'figure'),
    prevent_initial_call=True
)

Use allow_duplicate=True in the Output if the dcc.Graph is created through another callback. Otherwise you should be okay to remove it.

@chivonblanton
Copy link

The graphs on each of my tabs have a variable height that is calculated based on the len of the df in the callback. The height varies widely depending on the value selected from the dropdown. In this case, setting a fixed height is not an option. I don't think any of the proposed workarounds will work for my case.

Any ideas in this case?

@cscadenas
Copy link

Here's a way to solve it. I used @AnnMarieW 's code for this example. Just added a Callback that uses the dcc.Tabs value as input to set the size of the graph everytime you switch tabs. It uses the current state of both graphs to transform it into a go.Figure (from plotly.graph_objects) and sets the layout's height you need.

It just causes a little blink in the chart and you can see how it changes sizes, but is only just a microsecond, so, if it's not a major problem for you, it works :)

WhatsApp.Video.2022-11-17.at.17.37.49.4.mp4


import dash
import dash_html_components as html
import dash_core_components as dcc
import plotly.express as px
import pandas as pd
from dash import Input, Output, State, ctx
from dash.exceptions import PreventUpdate
import plotly.graph_objects as go

external_stylesheets = ["https://codepen.io/chriddyp/pen/bWLwgP.css"]

app = dash.Dash(__name__, external_stylesheets=external_stylesheets)

df = pd.DataFrame(
    {
        "Fruit": ["Apples", "Oranges", "Bananas", "Apples", "Oranges", "Bananas"],
        "Amount": [4, 1, 2, 2, 4, 5],
        "City": ["SF", "SF", "SF", "Montreal", "Montreal", "Montreal"],
    }
)

fig = px.bar(df, x="Fruit", y="Amount", color="City", barmode="group")
fig.update_layout(height=200)


tab1 = html.Div([html.H3("Tab content 1"), dcc.Graph(id="example-graph1", figure=fig)])
tab2 = html.Div([html.H3("Tab content 2"), dcc.Graph(id="example-graph2", figure=fig)])

app.layout = html.Div(
    [
        dcc.Tabs(
            id="tabs",
            value="tab-1",
            children=[
                dcc.Tab(label="Tab one", value="tab-1", children=tab1),
                dcc.Tab(label="Tab two", value="tab-2", children=tab2),
            ],
        ),
    ]
)

@app.callback(
    [Output('example-graph1','figure'),
    Output('example-graph2','figure')],
    Input('tabs','value'),
    State('example-graph1','figure'),
    State('example-graph2','figure')
)

def controlGraphSizes(tabValue, figure1,figure2):

    if tabValue == 'tab-1':
        print(type(figure1))
        newFigure1 = go.Figure(figure1)
        newFigure1.update_layout(height=200)
        return [newFigure1,dash.no_update]
    
    elif tabValue == 'tab-2':
        print(type(figure2))
        newFigure2 = go.Figure(figure1)
        newFigure2.update_layout(height=200)
        return [dash.no_update,newFigure2]

    else:
        raise PreventUpdate

if __name__ == "__main__":
    app.run_server(debug=True)

In case anyone else is facing the same issue, and to complete a bit this answer, I managed to avoid the blink by having the tab content outside the Tab component, as in this example:
https://dash-bootstrap-components.opensource.faculty.ai/examples/graphs-in-tabs/

I have only tried it with dbc.Tabs and not with dcc.Tabs, so I don't know if it'd solve the issue. In any case I think it can be useful.

Cheers

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests