How to position the legend and remove the empty spaces in dcc.Graph?

I have written a code to have pattern-matching callback. Whenever I click the button, it adds a new figure to the existing ones. I have the problem that I cannot move the legends to the top. Also, I want to remove the white areas around the plot to make it look better. I have used ‘legend’: {‘x’: -1, ‘y’: -1} or ‘margin’: {‘autoexpand’: False} in the layout., but nothing works. My dataframe has 5 columns, and col5 is on the y_axis.

Here is my code:

import plotly.express as px
import pandas as pd
import numpy as np
import dash_bootstrap_components as dbc

df = pd.read_csv('test.csv')

app = Dash(external_stylesheets=[dbc.themes.SUPERHERO])

app.layout = html.Div([
    html.Div(children=[
        html.Button('Add Chart', id='add-chart', n_clicks=0),
    ]),
    html.Div(id='container', children=[])
])


@app.callback(
    Output('container', 'children'),
    [Input('add-chart', 'n_clicks')],
    [State('container', 'children')]
)
def display_graphs(n_clicks, div_children):
    new_child = html.Div(
        style={'width': '25%',
               'display': 'inline-block',
               'outline': 'thin lightgrey solid',
               'padding': 10},

        children=[
            dcc.Dropdown(id={'type': 'feature-choice',
                             'index': n_clicks},
                         options=df.columns.values[0:4],
                         multi=True,
                         clearable=True,
                         value=[]
                         ),
            dcc.Graph(id={'type': 'dynamic-graph',
                          'index': n_clicks},
                      figure={'layout': {'height': 600,
                                         'legend': {'x': -1, 'y': -1},
                                         'margin': {'autoexpand': False}
                                         }
                              }
                      )
        ]
    )
    div_children.append(new_child)
    return div_children


@app.callback(
    Output({'type': 'dynamic-graph', 'index': MATCH}, 'figure'),
    [Input({'type': 'feature-choice', 'index': MATCH}, 'value')]
)
def update_graph(X):
    if X is None:
        raise PreventUpdate
    fig1 = px.line(df, x=X, y='col5')
    return fig1

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

Hi @Peyman

you will have to update the figure layout before returning it. I am not sure, but I guess you are trying to define the layout here:

Since you update the figure from your callback, whatever is the current value of figure will be overwritten.

Try this as a starting point to define your desired layout:

from dash import Dash, html, dcc, Input, Output, State, MATCH
from dash.exceptions import PreventUpdate
import plotly.express as px
import pandas as pd
import numpy as np
import dash_bootstrap_components as dbc

df = pd.read_csv('test.csv')

app = Dash(external_stylesheets=[dbc.themes.SUPERHERO])

app.layout = html.Div([
    html.Div(children=[
        html.Button('Add Chart', id='add-chart', n_clicks=0),
    ]),
    html.Div(id='container', children=[])
])


@app.callback(
    Output('container', 'children'),
    [Input('add-chart', 'n_clicks')],
    [State('container', 'children')]
)
def display_graphs(n_clicks, div_children):
    new_child = html.Div(
        style={'width': '25%',
               'display': 'inline-block',
               'outline': 'thin lightgrey solid',
               'padding': 10},

        children=[
            dcc.Dropdown(id={'type': 'feature-choice',
                             'index': n_clicks},
                         options=df.columns.values[0:4],
                         multi=True,
                         clearable=True,
                         value=[]
                         ),
            dcc.Graph(id={'type': 'dynamic-graph',
                          'index': n_clicks},
                      figure={}
                      )
        ]
    )
    div_children.append(new_child)
    return div_children


@app.callback(
    Output({'type': 'dynamic-graph', 'index': MATCH}, 'figure'),
    [Input({'type': 'feature-choice', 'index': MATCH}, 'value')]
)
def update_graph(X):
    if X is None:
        raise PreventUpdate
    fig1 = px.line(df, x=X, y='col5')
    fig1.update_layout(
        {'height': 600,
         'legend': {'x': 0, 'y': 0},
         'margin': {'l': 0, 'r': 0, 't': 0, 'b': 0}
         }
    )
    return fig1


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

Thank you. you are right. That solved the problem.