Size of graph breaks when using dcc graph

I am trying to create a dash app, where on clicking different buttons different graphs should display.
But when the graph is initially loaded it breaks and then automatically adjusts itself.
Why the graph breaks when loaded initially, I have given a sample code.

button_style = {'background-color': 'transparent', 'color': 'inherit', 'border': 'none', 'border-radius': '0px'}
style_after_click = {'background-color': 'transparent', 'color': '#0000FF', 'border-style': 'solid',
                     'border-color': '#0000FF', 'border-width': '0px 0px 2px 0px', 'border-radius': '0px'}


app.layout = html.Div([
        dbc.Card([
            dbc.CardBody([
                html.Div([

                    dbc.Button('Option1', id='option1_button', n_clicks=0, style=style_after_click,
                               className='margin_buttons_1', size="lg"),
                    dbc.Button('Option2', id='option2_button', n_clicks=0, style=button_style, className='margin_buttons', size="lg"),
            
                    dbc.Button('Option3', id='option3_button', n_clicks=0, style=button_style, className='margin_buttons', size="lg"),

                ], style={'display': 'flex'}),
                html.Br(),
                html.Div([
                    dcc.Graph(figure =fig_sample(), config={'displayModeBar': False}, style = {'height':'200px'})
                   
                ], id="option1_graph"),
                html.Div([
                    dcc.Graph(figure =fig_sample(), config={'displayModeBar': False}, style = {'height':'200px'} )
                    
                ], id="option2_graph", style={"display": "none"}),
                html.Div([
                    dcc.Graph(figure =fig_sample(), config={'displayModeBar': False},  style = {'height':'200px'})
                ], id="option3_graph", style={"display": "none"}),
            ], style={'padding': '20px'})
        ], style={'height': '348px'})
    ])

@callback(
    Output(component_id="option1_graph", component_property='style'),
    Output(component_id="option2_graph", component_property='style'),
    Output(component_id="option3_graph", component_property='style'),
    Output(component_id='option1_button', component_property='style'),
    Output(component_id='option2_button', component_property='style'),
    Output(component_id='option3_button', component_property='style'),
    [
        Input(component_id='option1_button', component_property='n_clicks'),
        Input(component_id='option2_button', component_property='n_clicks'),
        Input(component_id='option3_button', component_property='n_clicks'),
    ], prevent_initial_call=True)

def clicked_p2p(option1_button, option2_button, option3_button):
    if 'option1_button' == ctx.triggered_id:
        return {'display': 'block'}, {'display': 'none'}, {'display': 'none'}, style_after_click, button_style, button_style
    elif 'option2_button' == ctx.triggered_id:
        return {'display': 'none'}, {'display': 'block'}, {'display': 'none'},  button_style, style_after_click, button_style
    elif 'option3_button' == ctx.triggered_id:
        return {'display': 'none'}, {'display': 'none'}, {'display': 'block'}, button_style, button_style, style_after_click
    else:
        return {'display': 'block'}, {'display': 'none'}, {'display': 'none'}, style_after_click, button_style, button_style

The graph in the image below was intended to occupy the entire width of the screen. However, during rendering, the graph initially appears at only half of its intended width and then readjusts to occupy the full width
image
l

Hey @sangeetasaha welcome to the community!

It’s difficult to tell as I can’t reproduce the issue with the code you provided. Maybe you can edit the code?

Hi @AIMPED

Thank you for the response, I have shared below the full code.

def fig_sample():
    fig = px.area(x=[0, 1], y=[0, 1], color_discrete_sequence=['#F5BBAA'], template='plotly_white')
    fig.update_layout(
        dragmode=False,
        yaxis=dict(
            overlaying='y',  
            color='#808080'  
        ),
        xaxis_showgrid=False,
        yaxis_showgrid=False,
        margin=dict(
            l=0,  
            r=0,  
            t=0,  
            b=0  
        ),
    )
    fig.update_xaxes(title=None, visible=False)
    fig.update_yaxes(title=None, visible=False)
    fig.update_traces(
        hovertemplate=None,
        hoverinfo="skip")

    return fig




app = dash.Dash(__name__,external_stylesheets=[dbc.themes.BOOTSTRAP, dbc.icons.BOOTSTRAP])


button_style = {'background-color': 'transparent', 'color': 'inherit', 'border': 'none', 'border-radius': '0px'}
style_after_click = {'background-color': 'transparent', 'color': '#0000FF', 'border-style': 'solid',
                     'border-color': '#0000FF', 'border-width': '0px 0px 2px 0px', 'border-radius': '0px'}


app.layout = html.Div([
        dbc.Card([
            dbc.CardBody([
                html.Div([

                    dbc.Button('Option1', id='option1_button', n_clicks=0, style=style_after_click,
                               className='margin_buttons_1', size="lg"),
                    dbc.Button('Option2', id='option2_button', n_clicks=0, style=button_style, className='margin_buttons', size="lg"),
            
                    dbc.Button('Option3', id='option3_button', n_clicks=0, style=button_style, className='margin_buttons', size="lg"),

                ], style={'display': 'flex'}),
                html.Br(),
                html.Div([
                    dcc.Graph(figure =fig_sample(), config={'displayModeBar': False}, style = {'height':'200px'})
                   
                ], id="option1_graph"),
                html.Div([
                    dcc.Graph(figure =fig_sample(), config={'displayModeBar': False}, style = {'height':'200px'} )
                    
                ], id="option2_graph", style={"display": "none"}),
                html.Div([
                    dcc.Graph(figure =fig_sample(), config={'displayModeBar': False},  style = {'height':'200px'})
                ], id="option3_graph", style={"display": "none"}),
            ], style={'padding': '20px'})
        ], style={'height': '348px'})
    ])

@callback(
    Output(component_id="option1_graph", component_property='style'),
    Output(component_id="option2_graph", component_property='style'),
    Output(component_id="option3_graph", component_property='style'),
    Output(component_id='option1_button', component_property='style'),
    Output(component_id='option2_button', component_property='style'),
    Output(component_id='option3_button', component_property='style'),
    [
        Input(component_id='option1_button', component_property='n_clicks'),
        Input(component_id='option2_button', component_property='n_clicks'),
        Input(component_id='option3_button', component_property='n_clicks'),
    ], prevent_initial_call=True)

def clicked_p2p(option1_button, option2_button, option3_button):
    if 'option1_button' == ctx.triggered_id:
        return {'display': 'block'}, {'display': 'none'}, {'display': 'none'}, style_after_click, button_style, button_style
    elif 'option2_button' == ctx.triggered_id:
        return {'display': 'none'}, {'display': 'block'}, {'display': 'none'},  button_style, style_after_click, button_style
    elif 'option3_button' == ctx.triggered_id:
        return {'display': 'none'}, {'display': 'none'}, {'display': 'block'}, button_style, button_style, style_after_click
    else:
        return {'display': 'block'}, {'display': 'none'}, {'display': 'none'}, style_after_click, button_style, button_style

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

While searching for the solution, I see that there was an issue open in https://github.com/plotly/plotly.js/issues/2769

In that, the solution was to replace {‘display’:‘block’} / {‘display’:‘none’} by {‘visibility’:‘hidden’, ‘position’:‘absolute’} / {‘visibility’:‘visible’, ‘position’:‘absolute’}.

I have applied that in my code, it solves the issue of breaking but the graph doesn’t take the full width. I cannot keep the width of the graph fixed, as it will be different on different screens.

Also, if I apply the above solution, the hover of the graph doesn’t respond in option 1 and 2 but only responds in option 3.

Below is the whole code and snapshot of the graph

def fig_sample():
    fig = px.area(x=[0, 1, 4, 5, 6], y=[0, 1, 3, 5, 34], color_discrete_sequence=['#F5BBAA'], template='plotly_white',
                 markers = True)
    fig.update_layout(
        dragmode=False,
        yaxis=dict(
            overlaying='y',  
            color='#808080'  
        ),
        xaxis_showgrid=False,
        yaxis_showgrid=False,
        margin=dict(
            l=0,  
            r=0,  
            t=0,  
            b=0  
        ),
    )
    fig.update_xaxes(title=None, visible=False)
    fig.update_yaxes(title=None, visible=False)
    return fig




app = dash.Dash(__name__,external_stylesheets=[dbc.themes.BOOTSTRAP, dbc.icons.BOOTSTRAP])


button_style = {'background-color': 'transparent', 'color': 'inherit', 'border': 'none', 'border-radius': '0px'}
style_after_click = {'background-color': 'transparent', 'color': '#0000FF', 'border-style': 'solid',
                     'border-color': '#0000FF', 'border-width': '0px 0px 2px 0px', 'border-radius': '0px'}


app.layout = html.Div([
        dbc.Card([
            dbc.CardBody([
                html.Div([

                    dbc.Button('Option1', id='option1_button', n_clicks=0, style=style_after_click,
                               className='margin_buttons_1', size="lg"),
                    dbc.Button('Option2', id='option2_button', n_clicks=0, style=button_style, 
                               className='margin_buttons', size="lg"),
            
                    dbc.Button('Option3', id='option3_button', n_clicks=0, style=button_style, 
                               className='margin_buttons', size="lg"),

                ], style={'display': 'flex'}),
                html.Br(),
                html.Div([
                    dcc.Graph(figure =fig_sample(), config={'displayModeBar': False}, style = {'height':'200px', 'width': '100%'})
                   
                ], id="option1_graph", style={'visibility':'visible', 'position': 'absolute'}),
                html.Div([
                    dcc.Graph(figure =fig_sample(), config={'displayModeBar': False}, style = {'height':'200px', 'width': '100%'} )
                    
                ], id="option2_graph", style={'visibility':'hidden', 'position': 'absolute'}),
                html.Div([
                    dcc.Graph(figure =fig_sample(), config={'displayModeBar': False},  style = {'height':'200px', 'width': '100%'})
                ], id="option3_graph", style={'visibility':'hidden', 'position': 'absolute'}),
            ], style={'padding': '20px'}),
        ], style={'height': '348px'}),
])

@callback(
    Output(component_id="option1_graph", component_property='style'),
    Output(component_id="option2_graph", component_property='style'),
    Output(component_id="option3_graph", component_property='style'),
    Output(component_id='option1_button', component_property='style'),
    Output(component_id='option2_button', component_property='style'),
    Output(component_id='option3_button', component_property='style'),
    [
        Input(component_id='option1_button', component_property='n_clicks'),
        Input(component_id='option2_button', component_property='n_clicks'),
        Input(component_id='option3_button', component_property='n_clicks'),
    ], prevent_initial_call=True)

def clicked_p2p(option1_button, option2_button, option3_button):
    if 'option1_button' == ctx.triggered_id:
        return {'visibility':'visible', 'position':'absolute'}, {'visibility':'hidden', 'position':'absolute'}, {'visibility':'hidden', 'position':'absolute'}, style_after_click, button_style, button_style
    elif 'option2_button' == ctx.triggered_id:
        return {'visibility':'hidden', 'position':'absolute'}, {'visibility':'visible', 'position':'absolute'}, {'visibility':'hidden', 'position':'absolute'},  button_style, style_after_click, button_style
    elif 'option3_button' == ctx.triggered_id:
        return {'visibility':'hidden', 'position':'absolute'}, {'visibility':'hidden', 'position':'absolute'}, {'visibility':'visible', 'position':'absolute'}, button_style, button_style, style_after_click
    else:
        return {'visibility':'visible', 'position':'absolute'}, {'visibility':'hidden', 'position':'absolute'}, {'visibility':'hidden', 'position':'absolute'}, style_after_click, button_style, button_style

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

On the sizing question, setting the width of the containing div seems to fix it, this sort of thing:

 html.Div([
     dcc.Graph(figure =fig_sample(), config={'displayModeBar': False}, 
                          style = {'height':'200px', 'width': '100%'})
                ], 
    id="option1_graph", 
    style={'visibility':'visible', 'position': 'absolute', 'width': '90%'}),

I think this solves both problems, but I’m left feeling there should be a more elegant way of doing it.

The hover issue is (I think) that graph 3 remains in the layout even when not visible and is handling all the hover events. This can be fixed by bumping up the zindex of the visible plot.

import plotly.express as px
from dash import dash, Input, ctx, html, callback, Output, dcc
import dash_bootstrap_components as dbc

def fig_sample():
    fig = px.area(x=[0, 1, 4, 5, 6], y=[0, 1, 3, 5, 34], color_discrete_sequence=['#F5BBAA'], template='plotly_white',
                 markers = True)
    fig.update_layout(
        dragmode=False,
        yaxis=dict(
            overlaying='y',  
            color='#808080'  
        ),
        xaxis_showgrid=False,
        yaxis_showgrid=False,
        margin=dict(
            l=0,  
            r=0,  
            t=0,  
            b=0  
        ),
    )
    fig.update_xaxes(title=None, visible=False)
    fig.update_yaxes(title=None, visible=False)
    return fig

app = dash.Dash(__name__,external_stylesheets=[dbc.themes.BOOTSTRAP, dbc.icons.BOOTSTRAP])

button_style = {'background-color': 'transparent', 'color': 'inherit', 'border': 'none', 'border-radius': '0px'}
style_after_click = {'background-color': 'transparent', 'color': '#0000FF', 'border-style': 'solid',
                     'border-color': '#0000FF', 'border-width': '0px 0px 2px 0px', 'border-radius': '0px'}

dhide = {'visibility':'hidden', 'position':'absolute', 'zindex':0}
dshow = {'visibility':'visible', 'position':'absolute', 'zindex':1000, 'width': '90%'}

app.layout = html.Div([
        dbc.Card([
            dbc.CardBody([
                html.Div([

                    dbc.Button('Option1', id='option1_button', n_clicks=0, style=style_after_click,
                               className='margin_buttons_1', size="lg"),
                    dbc.Button('Option2', id='option2_button', n_clicks=0, style=button_style, 
                               className='margin_buttons', size="lg"),
            
                    dbc.Button('Option3', id='option3_button', n_clicks=0, style=button_style, 
                               className='margin_buttons', size="lg"),

                ], style={'display': 'flex'}),
                html.Br(),
                html.Div([
                    dcc.Graph(figure =fig_sample(), config={'displayModeBar': False}, style = {'height':'200px', 'width': '100%'})
                   
                ], id="option1_graph", style=dshow),
                html.Div([
                    dcc.Graph(figure =fig_sample(), config={'displayModeBar': False}, style = {'height':'200px', 'width': '100%'} )
                    
                ], id="option2_graph", style=dhide),
                html.Div([
                    dcc.Graph(figure =fig_sample(), config={'displayModeBar': False},  style = {'height':'200px', 'width': '100%'})
                ], id="option3_graph", style=dhide),
            ], style={'padding': '20px'}),
        ], style={'height': '348px'}),
])

@callback(
    Output(component_id="option1_graph", component_property='style'),
    Output(component_id="option2_graph", component_property='style'),
    Output(component_id="option3_graph", component_property='style'),
    Output(component_id='option1_button', component_property='style'),
    Output(component_id='option2_button', component_property='style'),
    Output(component_id='option3_button', component_property='style'),
    [
        Input(component_id='option1_button', component_property='n_clicks'),
        Input(component_id='option2_button', component_property='n_clicks'),
        Input(component_id='option3_button', component_property='n_clicks'),
    ], prevent_initial_call=True)

def clicked_p2p(option1_button, option2_button, option3_button):
    if 'option1_button' == ctx.triggered_id:
        return dshow, dhide, dhide, style_after_click, button_style, button_style
    elif 'option2_button' == ctx.triggered_id:
        return dhide, dshow, dhide,  button_style, style_after_click, button_style
    elif 'option3_button' == ctx.triggered_id:
        return dhide, dhide, dshow, button_style, button_style, style_after_click
    else:
        return dhide, dhide, dhide, style_after_click, button_style, button_style

if __name__=='__main__':
    app.run_server()

1 Like

Hi @davidharris , thank you for your solution.

It solves the issues of width and hover. However, the graph initially breaks while adjusting to the correct width when it is first loaded.

This was the same issue I was facing when I was using {‘display’:‘block’} and {‘display’:‘none’} instead of visibility parameter.

I see errors on the console if I use app.run_server(debug=True) but I don’t get these errors if I use app.run_server(debug=False), and I don’t see any other problems.

Let me know if I’ve missed something, but if it’s an error that only turns up when debug=True, I’m afraid it’s not one I know how to solve.