8000 tight_layout relies on get_renderer, which is not available on all backends · Issue #1852 · matplotlib/matplotlib · GitHub
[go: up one dir, main page]

Skip to content

tight_layout relies on get_renderer, which is not available on all backends #1852

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
mdehoon opened this issue Mar 24, 2013 · 20 comments
Closed
Milestone

Comments

@mdehoon
Copy link
Contributor
mdehoon commented Mar 24, 2013

With the MacOSX backend and possibly other backends, tight_layout falls back to the Agg backend. For example, demo_tight_layout.py does the following:

$ python -i examples/pylab_examples/demo_tight_layout.py
/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/matplotlib-1.3.x-py2.7-macosx-10.6-intel.egg/matplotlib/tight_layout.py:225: UserWarning: tight_layout : falling back to Agg renderer
warnings.warn("tight_layout : falling back to Agg renderer")

This is the relevant code in tight_layout.py:

def get_renderer(fig):
    if fig._cachedRenderer:
        renderer = fig._cachedRenderer
    else:
        canvas = fig.canvas  
        if canvas and hasattr(canvas, "get_renderer"):
            renderer = canvas.get_renderer()
        else:
            # not sure if this can happen
            warnings.warn("tight_layout : falling back to Agg renderer")
            from matplotlib.backends.backend_agg import FigureCanvasAgg
            canvas = FigureCanvasAgg(fig)
            renderer = canvas.get_renderer()
    return renderer

Neither backend_template.py nor backend_bases.py implement a get_renderer method, nor do the MacOSX and gtkcairo backend (and possibly other backends).

Even if I were to add a get_renderer method to the canvas in the MacOSX backend, this still fails. The problem is that this code tries to grab a renderer and use it outside of the event loop (i.e., outside of the figure.draw method). Outside of the event loop, the graphics context is not defined, and the code fails as follows:

$ python -i examples/pylab_examples/demo_tight_layout.py
Traceback (most recent call last):
File "examples/pylab_examples/demo_tight_layout.py", line 15, in
plt.tight_layout()
...
RuntimeError: CGContextRef is NULL

I would suggest to restructure the code in tight_layout.py to avoid using the renderer directly.

@dmcdougall
Copy link
Member

I noticed this too. It also happens with the PDF backend:

$ python
Python 2.7.3 (default, Nov 29 2012, 11:01:29)
[GCC 4.2.1 Compatible Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import matplotlib
>>> matplotlib.use('pdf')
>>> import matplotlib.pyplot as plt
>>> fig = plt.figure()
>>> ax = fig.add_subplot(1, 1, 1)
>>> fig.tight_layout()
/Users/damon/python/lib/matplotlib-1.3.x-py2.7-macosx-10.8-x86_64.egg/matplotlib/tight_layout.py:225: UserWarning: tight_layout : falling back to Agg renderer
  warnings.warn("tight_layout : falling back to Agg renderer")

We could move from using the renderer directly to updating the fig.draw() method so it returns the calculations needed by fig.tight_layout() from the renderer; utilising the event loop correctly.

@mdboom
Copy link
Member
mdboom commented Mar 25, 2013

I agree with @dmcdougall: I think that's what needs to be done. It has the added benefit of making tight_layout less sensitive to other backend differences.

@leejjoon
Copy link
Contributor
leejjoon commented Apr 1, 2013

@mdehoon , @dmcdougall : Just to clarify, tight_layout still works without error (only warning)? Or does it raise an exception?

The only reason that we need a renderer instance is to calculate the size and location of texts. @mdehoon, is this something impossible outside the event loop?

I am not quite clear about the suggestion by @dmcdougall. Does it mean that we call fig.draw inside tight_layout? I am not sure if that is a good idea.

@mdehoon
Copy link
Contributor Author
mdehoon commented Apr 1, 2013

tight_layout still works, but it works by falling back to the Agg renderer, which should not be necessary.

To calculate the size of texts, I presume you will need to call renderer.get_text_width_height_descent (either directly or indirectly). But renderer.get_text_width_height_descent needs a graphics context to be able to do its job, and it can only get a (properly initialized) graphics context from inside the callback in the event loop. This callback calls fig.draw, so I think @dmcdougall 's suggestion is to reorganize the tight_layout code such that the calculations are done as part of fig.draw.

@leejjoon
Copy link
Contributor
leejjoon commented Apr 3, 2013

We have Figure.set_tight_layout that makes Figure.draw to call tight_layout.
As far as I can see, tight_layout needs to be a one-time action that reflects the figure layout at the time when this function is called. And I don't see any easy way 8000 to achieve @dmcdougall 's suggestion given this behavior. But I will be happy to be proven wrong.

As far as I can see, we need to access renderer at the time when tight_layout is called. I don't see how we can avoid this. But again, I will be happy to be proven wrong.

@dmcdougall
Copy link
Member

@leejjoon The method Figure.set_tight_layout seems to be what we're trying to achieve, I think. The PDF backend now works this way:

In [1]: import matplotlib
In [2]: matplotlib.use('pdf')
In [3]: import matplotlib.pyplot as plt
In [4]: fig = plt.figure()
In [5]: ax = fig.add_subplot(1,1,1)
In [6]: fig.set_tight_layout(True)
In [7]: fig.savefig('asd.pdf')  # No warning now

@mdehoon I'm on a linux box at the moment, would you be able to try this on OS X?

If this works on Mac, could we add tight_layout kwarg to fig.savefig that, when set to True, Figure.set_tight_layout gets called?

@mdehoon
Copy link
Contributor Author
mdehoon commented Apr 4, 2013

@dmcdougall : fig.set_tight_layout seems to work fine with the MacOSX backend.
I noted though that with your example, fig.add_subplot does nothing in interactive mode, even if I skip the fig.set_tight_layout command.
In non-interactive mode the script works as expected.

Also if I replace fig.add_subplot by plt.subplot, then the set_tight_layout works fine, both in interactive and in non-interactive mode.

So it looks like set_tight_layout is working fine. I have no idea why fig.add_subplot does nothing while plt.subplot works fine, but anyway this is independent of set_tight_layout.

@mdehoon
Copy link
Contributor Author
mdehoon commented Apr 4, 2013

@dmcdougall It turned out that the difference between fig.add_subplot and plt.subplot is due to the fact that the latter includes a call to draw_if_interactive while the former doesn't. If I add plt.draw_if_interactive() to your example, fig.set_tight_layout works fine with the MacOSX backend both in interactive and in non-interactive mode.

@dmcdougall
Copy link
Member

@mdehoon Yeah, sorry. I rarely use the interactive stuff. My go-to is the PDF or Agg backend.

@khyox
Copy link
Contributor
khyox commented Aug 7, 2013

@leejjoon @dmcdougall @mdehoon : Thank you to all of you... the fig.set_tight_layout(True) tip has been very useful to avoid the warning, the backend issue and to get even a better appearance than plt.tight_layout().

@nilsbecker
Copy link

not sure if this is helpful: for me, calling fig.tight_layout() resulted in RuntimeError: CGContextRef is NULL errors, while fig.set_tight_layout(True) worked.
OS X, osx backend, python 2.7.5, mpl 1.3.1

@mdehoon
Copy link
Contributor Author
mdehoon commented Dec 26, 2013

See issue #2654 for more information; essentially, these two are the same problem. Redesigning the tight_layout code, as proposed in issue #2654 , should solve the problem.

@tacaswell tacaswell added this to the v1.5.x milestone Aug 17, 2014
mmortonson added a commit to mmortonson/CosmoCombo that referenced this issue Oct 21, 2014
@orome
Copy link
orome commented Oct 31, 2014

FWIW: As @nilsbecker notes, fig.set_tight_layout(True) also works for me in the 'MacOSX' backend (OS X 10.10; Python 2.7.6; matplotlib 1.4.2). However in IPython Notebook (2.3.0; backed = 'module://IPython.kernel.zmq.pylab.backend_inline') it gives me warnings, and I need to use fig.tight_layout() instead:

/Library/Python/2.7/site-packages/matplotlib/figure.py:1644: UserWarning: This figure includes Axes that are not compatible with tight_layout, so its results might be incorrect.

So, for example, in my code, where I typically encounter 'Agg', 'MacOSX' and the Notebook backend, and since set_tight_layout seems to give slightly better results when it works without warning, I have

if matplotlib.get_backend().lower() in ['agg', 'macosx']:
    fig.set_tight_layout(True)
else:
    fig.tight_layout()

@c-benko
Copy link
c-benko commented Nov 6, 2014

Currently on OSX 10.10, Python 3.3.4, Matplotlib 1.4.0 backend "MacOSX".

Calling tight_layout() returns:

/usr/local/lib/python3.3/site-packages/matplotlib/tight_layout.py:225: UserWarning: tight_layout : falling back to Agg renderer

The figure is still produced and appears to be fine.

The warning also occurs if below is implemented.

import matplotlib
matplotlib.use('pdf')

nilbus added a commit to andrewjesaitis/hexbug-tracker that referenced this issue Nov 28, 2014
@aflansburg
Copy link

@c-benko Error is present on my Mac OSX 10.10.1 as well. Python 2.7.9, matplotlib 1.4.0 backend "MacOSX"

/Users/myName/anaconda/lib/python2.7/site-packages/matplotlib/tight_layout.py:225: UserWarning: tight_layout : falling back to Agg renderer
  warnings.warn("tight_layout : falling back to Agg renderer")

Using

from matplotlib import pyplot as plt

and calling

plt.tight_layout()
plt.show

@diego898
Copy link

I am seeing this warning while running anything using pycharm and anaconda with python 3.5.2 and matplotlib 1.5.3.

I dont see this if I run through the jupyter notebook.

@janssen
Copy link
janssen commented Jan 21, 2017

I'm seeing this with 2.0.0rc1 and the Kivy backend.

@tacaswell tacaswell modified the milestones: 2.1 (next point release), 2.2 (next next feature release) Oct 3, 2017
@bayesfactor
Copy link

I'm having this issue with SVG backend on Windows + python 3.5.5 + matplotlib 2.1.1

Timothy-W-Hilton pushed a commit to Timothy-W-Hilton/WRF_pre_post_processing that referenced this issue Mar 19, 2019
- self.fig.set_tight_layout(True) and self.fig.tight_layout() both
  issue warnings (see
  matplotlib/matplotlib#1852,
  https://stackoverflow.com/questions/15455029/python-matplotlib-agg-vs-interactive-plotting-and-tight-layout).
  Removing the figure title and panel titles produces a figure without
  a lot of whitespace in the margins, so that's the solution for now.
@smithsp
Copy link
Contributor
smithsp commented Mar 22, 2019

Bump for PDF backend.

@anntzer
Copy link
Contributor
anntzer commented Feb 24, 2022

Closed by #15221, I think.

@anntzer anntzer closed this as completed Feb 24, 2022
@QuLogic QuLogic modified the milestones: needs sorting, v3.3.0 Feb 24, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

0