Save plot as png; sizing and positioning

Hi Dashers -

I have an app that contains several graphs that have large legends and include custom updatemenu buttons.

They look great in the app but when I attempt to download them as .png files using the camera button in the modebar, the legend tends to cut into the graph and my custom buttons block some of the axis.

Here’s a screenshot of the plot as it appears in the browser in my app:

Here’s what the .png file looks like when downloaded using the modebar button:

Here’s some pseudo code for how I’m creating the graph:

# config options to apply to all graphs
plotConfig = {'showLink': False, 'modeBarButtonsToRemove': ['sendDataToCloud'], 'displaylogo': False}
legendConfig = dict(orientation='h', x=0, y=1.1)

# app layout
app.layout = html.Div([
    other components........,
    dcc.Graph(id='adoption-graph', config=plotConfig)]
)

# module to create the graph in question using some data set that i'm not showing here
@app.callback(
    Output('adoption-graph', 'figure'),
    [Input('dataset', 'children')])
def make_adoption_graph(dataset):

    allStats = DataFrame(dataset)

    # make traces for the plot
    traces = [scatter1, scatter2, scatter3]

    updatemenus = list([
            dict(active=3, showactive=True, type='buttons', xanchor='left', x=-.15,
                 buttons=list([
                     dict(label='Day', method='update', args=[{'visible': ([False]*12 + [True]*4)}]),
                     dict(label='Week', method='update', args=[{'visible': ([False]*8 + [True]*4 + [False]*4)}]),
                     dict(label='Month', method='update', args=[{'visible': ([False]*4 + [True]*4 + [False]*8)}]),
                     dict(label='Cumulative', method='update', args=[{'visible': ([True]*4 + [False]*12)}])
                 ]))
        ])

    layout = dict(title='Adoption Over Time',
                  xaxis=dict(rangeselector=dict(x=.8, y=1, buttons=list([
                      dict(count=1, label='1mo', step='month', stepmode='backward'),
                      dict(count=6, label='6mo', step='month', stepmode='backward'),
                      dict(step='all')])),type='date'),
                  yaxis=dict(tickformat=tickForm, hoverformat=hForm),
                  legend=legendConfig,
                  updatemenus=updatemenus)

    return dict(data=traces, layout=layout)

Is there a way to tell plotly how to format the png so that it looks like the graph in my browser?

Thanks!

1 Like

Thanks for reporting! This seems like a bug with plotly.js, in that it’s not getting the size of the container correctly. Could you open an issue in the plotly.js repo https://github.com/plotly/plotly.js/issues? You can mention this thread. Once you’ve created an issue, could you update this thread with the link to the issue?
Thanks!

Thanks for the quick response, Chris. I created the issue on github but it got flagged as a duplicate of this issue: https://github.com/plotly/plotly.js/issues/1576

I’m going to proceed with the workaround recommended in that issue.

1 Like

Thanks for reporting! If this isn’t going to be solved in the plotly.js side of things, it seems like we should make it the default behaviour in the dash-core-components library. Here’s an issue: https://github.com/plotly/dash-core-components/issues/199

Awesome, thanks again Chris!

I actually have another question if you don’t mind. I’m attempting to implement the suggested fix from plotly but am having trouble converting this JS to python code:

modeBarButtonsToAdd: [{
    name: 'toImage2',
    icon: Plotly.Icons.camera,
    click: function(gd) {
      Plotly.downloadImage(gd)
    }
  }]

any tips on how to get this into python?

Unfortunately, since this includes some JS code (rather than just straight JSON-ifyable arguments), it’s not possible to add this through Dash’s interfaces. It’d need to be added in the dcc.Graph component itself: https://github.com/plotly/dash-core-components/blob/master/src/components/Graph.react.js

An alternative to all of this is for you to fix the width and height of your graph itself, rather than autosizing it w.r.t. to the container. This is more annoying because it means that the graph can’t be responsive (it won’t resize with the screen) but it will mean that exporting it will be fixed size.

You can now pass in configuration to dcc.Graph that fixes sizing issues with downloaded png. The following config will make downloaded png same size as on screen.

config = {"toImageButtonOptions": {"width": None, "height": None}}
dcc.Graph(figure=..., config=config)

This was resolved by a fix in the plotly.js lib: https://github.com/plotly/plotly.js/pull/3746

2 Likes