Spyder: Plots pane has problems saving images generated by Plotly

Created on 11 Dec 2019  路  10Comments  路  Source: spyder-ide/spyder

Issue Report Checklist

  • [x] Searched the issues page for similar reports
  • [x] Read the relevant sections of the Spyder Troubleshooting Guide and followed its advice
  • [x] Reproduced the issue after updating with conda update spyder (or pip, if not using Anaconda)
  • [x] Could not reproduce inside jupyter qtconsole (if console-related)
  • [x] Tried basic troubleshooting (if a bug/error)

    • [x] Restarted Spyder

    • [x] Reset preferences with spyder --reset

    • [x] Reinstalled the latest version of Anaconda

    • [x] Tried the other applicable steps from the Troubleshooting Guide

  • [x] Completed the Problem Description, Steps to Reproduce and Version sections below

Problem Description

The newly add feature: plots has problems with saving the plotted image produced by Plotly.

When using the Save Image As.. button, the saved image will have the default size of 700x450 px. This cannot be changed even if the size is specified otherwise in plotly API.

If the image is saved by Plotly itself, the image will have a correct size (ex. 800x600 px).

What steps reproduce the problem?

  1. plot anything in Plotly.
  2. specify the image size.
  3. save the image using "Save Image As.." button in Spyder.

What is the expected output? What do you see instead?

The saved image will have a default size (i.e. 700x450) which cannot be changed.

Paste Traceback/Error Below (if applicable)

PASTE TRACEBACK HERE

Versions

  • Spyder version: 4.0.0
  • Python version: 3.7.3
  • Plotly version: 4.1.1

Dependencies

Reproducing the problem:

import plotly.graph_objects as go
import os

script_path = os.path.dirname(os.path.realpath(__file__))
animals=['giraffes', 'orangutans', 'monkeys']

fig = go.Figure([go.Bar(x=animals, y=[20, 14, 23])])

fig.update_layout(height = 600,
              width = 800,)
fig.show()
fig.write_image(script_path + '/fig.png')
Plots Enhancement v4.2.0

All 10 comments

Please post an example of an image generated by Plotly to take a look at it.

If you could provide also some code that can reproduce the problem, that would be helpful, thanks.

Here is a simple code from Plotly that demonstrates the problem.

The saved fig.png is 800x600, which is the set size. But the figure saved from Spyder plot "Save as ..." button still has the default size of 700x450.

import plotly.graph_objects as go
import os

script_path = os.path.dirname(os.path.realpath(__file__))
animals=['giraffes', 'orangutans', 'monkeys']

fig = go.Figure([go.Bar(x=animals, y=[20, 14, 23])])

fig.update_layout(height = 600,
              width = 800,)
fig.show()
fig.write_image(script_path + '/fig.png')

Here is a simple code from Plotly that demonstrates the problem.

The saved fig.png is 800x600, which is the set size. But the figure saved from Spyder plot "Save as ..." button still has the default size of 700x450.

Excellent, thanks. I'll take a look at this and report back.

From the documentation https://plot.ly/python/renderers/, I think you need to pass the width and height value to the renderer. One way to do it is to pass the width and height as arguments of the show method :

```
fig.show(width=800, height=600)
````

Then your figures are going to be plotted in the IPython console and the Plots plugin with the right size.

@ccordoba12 I don't know much about Plotly, but I think it may not be a Spyder problem. It seems to be how Plotly is designed and it is working like that also in Jupyter Notebooks when using either the 'png' or 'svg' renderer, but I might be wrong.

But if we would like to support this in Spyder, I think we would have to write our own renderer.

image

Can we do this in our side and register it when Plotly is imported? Or how does this work @jnsebgosselin?

Can we do this in our side and register it when Plotly is imported? Or how does this work @jnsebgosselin?

I don't know, I'll take a look and report back.

Ok, so this is pretty simple actually. Basically, when plotly gets imported, if it detects that it is done from Spyder, it will assign the svg renderer by default.

https://github.com/plotly/plotly.py/blob/ad8b403785022f622ac0f39c9ca576f0cf01f393/packages/python/plotly/plotly/io/_renderers.py#L488-L499

image

The svg renderer, as well as the png renderer, is defined here:

https://github.com/plotly/plotly.py/blob/ad8b403785022f622ac0f39c9ca576f0cf01f393/packages/python/plotly/plotly/io/_base_renderers.py#L100-L186

By design, the svg and png renderers do not use the width and height values that were set in the layout of the figure to produce their static image. You need to pass those values again as independent arguments to the renderer directly.

To have a better integration of Plotly inside Spyder, we would simply need to create our own renderer and add it to the list of available renderers when Plotly gets imported.

For testing purpose, I've created this renderer that inherits from the PngRenderer where I override the to_mimebundle method to assign the values of the width and height of the renderer to the values saved in the layout of the figure, so that the size of the png produced is automatically updated when updating the layout of the figure.

import plotly.graph_objects as go
from plotly.io._base_renderers import PngRenderer
from plotly.io._renderers import renderers


class SpyderRenderer(PngRenderer):
    def to_mimebundle(self, fig_dict):
        self.width = fig_dict['layout']['width']
        self.height = fig_dict['layout']['height']
        return super().to_mimebundle(fig_dict)


renderers["spyder"] = SpyderRenderer(height=450, width=700)
renderers.default = "spyder"

# %%

animals = ['giraffes', 'orangutans', 'monkeys']
fig = go.Figure([go.Bar(x=animals, y=[20, 14, 23])])
fig.update_layout(autosize=False, width=800, height=500)
fig.show()

This is pretty nice @jnsebgosselin! I think we should add this to spyder-kernels, like this:

Was this page helpful?
0 / 5 - 0 ratings