savefig bbox_inches='tight' does not consider suptitle

Which version of matplotlib are you using? This example works for me using
the latest matplotlib from source. Also, why the awkward usage and

Yes, with matplotlib 1.0 bbox_extra_artists now works.

I consider bbox_extra_artists some kind of a hack (IMHO, all artists should be considered with a 'tight' box), but coming from gnuplot/asymptote maybe my point of view is biased.
What would be the point of a 'tight' box that excludes parts of the plot? I would specify the coordinates myself if I needed clipping.

imports? If you want to force the Agg backend to be used, you could just
do:

import matplotlib
matplotlib.use("Agg")

before any other matplotlib imports.

Thanks for the suggestion, that looks promising, but doesn't work:

···

On Fri, 4 Mar 2011 14:57:34 -0600 Benjamin Root <ben.root@...1304...> wrote:

----
import matplotlib as mpl
mpl.use("Agg")
import matplotlib.figure
fig = mpl.figure.Figure()
fig.set_size_inches((20,20))
plot = fig.add_subplot(111)
plot.set_title("Subtitle")
plot.plot([1,2,3], [3,2,1])
st = fig.suptitle("Horray!", fontsize=20)
fig.savefig("out.png", bbox_inches='tight', bbox_extra_artists=[st])
----

produces:

Traceback (most recent call last):
  File "test.py", line 13, in <module>
    fig.savefig("out.png", bbox_inches='tight', bbox_extra_artists=[st])
  File "/usr/lib/pymodules/python2.6/matplotlib/figure.py", line 1084, in savefig
    self.canvas.print_figure(*args, **kwargs)
AttributeError: 'NoneType' object has no attribute 'print_figure'

I find the documentation a bit scattered around this subject.
I'm not using the pyplot interface, so I guess that .use("Agg") doesn't do anything for me?
I also have no reason to use the pyplot interface, why should I? I have no matlab background, and I mostly use matplotlib procedurally (ie not interactively).

Which version of matplotlib are you using? This example works for me using

the latest matplotlib from source. Also, why the awkward usage and

Yes, with matplotlib 1.0 bbox_extra_artists now works.

I consider bbox_extra_artists some kind of a hack (IMHO, all artists should be considered with a ‘tight’ box), but coming from gnuplot/asymptote maybe my point of view is biased.

What would be the point of a ‘tight’ box that excludes parts of the plot? I would specify the coordinates myself if I needed clipping.

imports? If you want to force the Agg backend to be used, you could just

do:

import matplotlib

matplotlib.use(“Agg”)

before any other matplotlib imports.

Thanks for the suggestion, that looks promising, but doesn’t work:


import matplotlib as mpl

mpl.use(“Agg”)

import matplotlib.figure

fig = mpl.figure.Figure()
fig.set_size_inches((20,20))

plot = fig.add_subplot(111)

plot.set_title(“Subtitle”)

plot.plot([1,2,3], [3,2,1])

st = fig.suptitle(“Horray!”, fontsize=20)

fig.savefig(“out.png”, bbox_inches=‘tight’, bbox_extra_artists=[st])


The problem is that you are creating your figure wrong. Try this:

import matplotlib as mpl
mpl.use(“Agg”)
import matplotlib.pyplot as plt

fig = plt.figure(figsize=(20, 20))

ax = fig.add_subplot(111)
ax.set_title(“Subtitle”)
ax.plot([1, 2, 3], [3, 2, 1])
st = fig.suptitle(“Horray!”, fontsize=20)
fig.savefig(“out.png”, bbox_inches=‘tight’, bbox_extra_artists=[st])

Notice that I am using the pyplot interface. Matplotlib was intended to be used through either this pyplot interface or the pylab interface (which I do not personally recommend if you want full control over your plots). If you don’t use either interfaces, then don’t be surprised when things do not work properly. In particular, creating the figure object by directly calling the Figure constructor bypasses important steps that involves attaching the appropriate backend to the figure object.

Also notice that I name the object coming from add_subplot() “ax” instead of “plot”. This is for clarity and to avoid confusion with the command “plot”. This is also the generally accepted coding convention in matplotlib.

I find the documentation a bit scattered around this subject.

I’m not using the pyplot interface, so I guess that .use(“Agg”) doesn’t do anything for me?

I also have no reason to use the pyplot interface, why should I? I have no matlab background, and I mostly use matplotlib procedurally (ie not interactively).

The only accepted ways to use matplotlib are through either the pyplot or the pylab interfaces. This is why they are called interfaces. It does not matter if you are using matplotlib interactively or procedurally. I personally use the pyplot interface in all of my scripts, as I rarely ever do interactive plotting. The pylab interface is more geared towards interactive plotting. These interfaces are designed to make your life easier.

Think of it this way. The developers can make many changes to matplotlib internals from one release to the next, but we do our best to make the interface as stable as possible. If you write code that do not utilize the pylab or pyplot interfaces, then your scripts will likely break at the next release.

Trust me, a lot of your problems will be solved by picking an interface (I recommend pyplot) and sticking with it.

I hope this is helpful,
Ben Root

···

On Mon, Mar 7, 2011 at 4:36 AM, Yuri D’Elia <wavexx@…878…867…> wrote:

On Fri, 4 Mar 2011 14:57:34 -0600 > > Benjamin Root <ben.root@…3286…> wrote:

The problem is that you are creating your figure wrong. Try this:

import matplotlib as mpl
mpl.use("Agg")
import matplotlib.pyplot as plt

fig = plt.figure(figsize=(20, 20))
ax = fig.add_subplot(111)
ax.set_title("Subtitle")
ax.plot([1, 2, 3], [3, 2, 1])
st = fig.suptitle("Horray!", fontsize=20)
fig.savefig("out.png", bbox_inches='tight', bbox_extra_artists=[st])

Notice that I am using the pyplot interface. Matplotlib was intended to be
used through either this pyplot interface or the pylab interface (which I do
not personally recommend if you want full control over your plots). If you
don't use either interfaces, then don't be surprised when things do not work
properly. In particular, creating the figure object by directly calling the
Figure constructor bypasses important steps that involves attaching the
appropriate backend to the figure object.

I was reading this at the time:

http://matplotlib.sourceforge.net/faq/usage_faq.html

I inferred pyplot was just a matlab-like interface on top of matplotlib, and figured using directly the matplotlib was acceptable.

Reading the documentation of the single objects of matplotlib was enough to get me started. I see pyplot as having more shortcuts for common operations, but I was unsure how far can I could go by using pyplot only.

Generally, I didn't have any trouble. The includes are a bit verbose, but that's it.

Also notice that I name the object coming from add_subplot() "ax" instead of
"plot". This is for clarity and to avoid confusion with the command
"plot". This is also the generally accepted coding convention in
matplotlib.

Indeed, thanks for the remark.

Thanks again for your comments.

···

On Mon, 7 Mar 2011 09:25:23 -0600 Benjamin Root <ben.root@...1304...> wrote:

The problem is that you are creating your figure wrong. Try this:

import matplotlib as mpl

mpl.use(“Agg”)

import matplotlib.pyplot as plt

fig = plt.figure(figsize=(20, 20))

ax = fig.add_subplot(111)

ax.set_title(“Subtitle”)

ax.plot([1, 2, 3], [3, 2, 1])

st = fig.suptitle(“Horray!”, fontsize=20)

fig.savefig(“out.png”, bbox_inches=‘tight’, bbox_extra_artists=[st])

Notice that I am using the pyplot interface. Matplotlib was intended to be

used through either this pyplot interface or the pylab interface (which I do

not personally recommend if you want full control over your plots). If you

don’t use either interfaces, then don’t be surprised when things do not work

properly. In particular, creating the figure object by directly calling the

Figure constructor bypasses important steps that involves attaching the

appropriate backend to the figure object.

I was reading this at the time:

http://matplotlib.sourceforge.net/faq/usage_faq.html

I inferred pyplot was just a matlab-like interface on top of matplotlib, and figured using directly the matplotlib was acceptable.

Yeah, I am guessing that page is a little out-dated and could be better worded. However, the page does say that the preferred style is the pyplot interface. Also notice that it is extremely rare for any of the documentation to directly create the matplotlib objects without the pyplot or pylab interface.

The pointof the statement “MATLAB-like” is that most of the plotting functions available in MATLAB are also available in matplotlib. In addition, the phrase “more MATLAB-like” is meant to state that various mathematical functions are made available as well. This was designed to make transitions from MATLAB to matplotlib easier for the user. Ultimately, we desire that the user transitions completely over to the pyplot interface.

Note that this has nothing to do with interactivity or proceedural. If anything, pylab was more intended for interactivity because its syntax is more concise, but you lose a lot of control.

Reading the documentation of the single objects of matplotlib was enough to get me started. I see pyplot as having more shortcuts for common operations, but I was unsure how far can I could go by using pyplot only.

Think of it this way. Matplotlib depends a lot upon hierarchical design. The pyplot (or pylab) interfaces sit on top of that heirarchy and represents the matplotlib environment. This environment creates figures. These figures can many components, the most important of which are one or more axes objects. Each axes object has two or more axis objects and are responsible for plotting themselves. While there are some convenience functions through pyplot for doing some things like setting an axis label, it is not required to use pyplot for that. You can (and often should) use the axes object’s method for doing so.

The point is that you can use the matplotlib objects for a lot of things, and it is great that you are embracing the object oriented nature of matplotlib. However, your problem was caused by-passing the hierarchy:

The interface should create the figure objects, the figure objects should create the axes objects, the axes objects should create the axis objects, and so on and so forth.

I hope that makes it clearer for you, and I hope you continue to use matplotlib and find it useful!

Ben Root

···

On Mon, Mar 7, 2011 at 10:33 AM, Yuri D’Elia <wavexx@…3457…> wrote:

On Mon, 7 Mar 2011 09:25:23 -0600 > Benjamin Root <ben.root@…1304…> wrote:

In fact, supporting the "bbox_inches" is a real hack.
As I mentioned in my previous email, matplotlib artists can have
spline paths. And artists can also be clipped by an arbitrary spline
path. And, generally speaking, finding out the exact bounding box of
an artist is difficult (but I must confess that I'm not an expert on
this field and any correction or advise will be appreciated). I
believe the AGG library, that matplotlib is based on, can provide an
approximate bounding box for spline paths, but I'm not sure if it will
work when clipping is involved (at least, the *get_window_extent*
does not properly work when clipping is involved).

I'll consider to support all artists in a "tight" bbox mode if
*get_window_extent* gives back exact bounding box (accounting the
clipping) for "all" artists. Otherwise, I'm not inclined to do this.

Regards,

-JJ

···

On Mon, Mar 7, 2011 at 7:36 PM, Yuri D'Elia <wavexx@...867...> wrote:

I consider bbox_extra_artists some kind of a hack (IMHO, all artists should be considered with a 'tight' box), but coming from gnuplot/asymptote maybe my point of view is biased.
What would be the point of a 'tight' box that excludes parts of the plot? I would specify the coordinates myself if I needed clipping.

In fact, supporting the "bbox_inches" is a real hack.
As I mentioned in my previous email, matplotlib artists can have
spline paths. And artists can also be clipped by an arbitrary spline
path. And, generally speaking, finding out the exact bounding box of
an artist is difficult (but I must confess that I'm not an expert on
this field and any correction or advise will be appreciated). I
believe the AGG library, that matplotlib is based on, can provide an
approximate bounding box for spline paths, but I'm not sure if it will
work when clipping is involved (at least, the *get_window_extent*
does not properly work when clipping is involved).

I see. But can't you simply skip spline paths from the calculation?
This would remove the need for bbox_extra_artists for 99% of the cases.

I'll consider to support all artists in a "tight" bbox mode if
*get_window_extent* gives back exact bounding box (accounting the
clipping) for "all" artists. Otherwise, I'm not inclined to do this.

I don't know if you read this already, but: all artists should at least work with bbox_extra_artists, right?

While placing the legend outside the plot I tried the following:

egend = plot.legend()
pic.savefig(..., bbox_extra_artists=[legend])

but it fails:

Traceback (most recent call last):
  File "./plot.py", line 108, in <module>
    fig.savefig(sys.argv[1], bbox_inches='tight', bbox_extra_artists=xa)
  File "/usr/lib/pymodules/python2.6/matplotlib/figure.py", line 1084, in savefig
    self.canvas.print_figure(*args, **kwargs)
  File "/usr/lib/pymodules/python2.6/matplotlib/backend_bases.py", line 1894, in print_figure
    in kwargs.pop("bbox_extra_artists", )]
TypeError: get_window_extent() takes exactly 1 argument (2 given)

···

On Tue, 8 Mar 2011 02:03:54 +0900 Jae-Joon Lee <lee.j.joon-Re5JQEeQqe8AvxtiuMwx3w@...1455...> wrote:

On Mon, Mar 7, 2011 at 7:36 PM, Yuri D'Elia <wavexx-iA+eEnwkJgzk1uMJSBkQmQ@...1455...> wrote:

And this appears to be a bug. Looks like the call signature for the legend object’s get_window_extent() doesn’t match the call signature for all other artists.

I will write up a patch for this.

Ben Root

···

On Mon, Mar 7, 2011 at 11:09 AM, Yuri D’Elia <wavexx@…120…867…> wrote:

On Tue, 8 Mar 2011 02:03:54 +0900 > Jae-Joon Lee <lee.j.joon@…287…> wrote:

On Mon, Mar 7, 2011 at 7:36 PM, Yuri D’Elia <wavexx@…867…> wrote:

In fact, supporting the “bbox_inches” is a real hack.

As I mentioned in my previous email, matplotlib artists can have

spline paths. And artists can also be clipped by an arbitrary spline

path. And, generally speaking, finding out the exact bounding box of

an artist is difficult (but I must confess that I’m not an expert on

this field and any correction or advise will be appreciated). I

believe the AGG library, that matplotlib is based on, can provide an

approximate bounding box for spline paths, but I’m not sure if it will

work when clipping is involved (at least, the get_window_extent

does not properly work when clipping is involved).

I see. But can’t you simply skip spline paths from the calculation?

This would remove the need for bbox_extra_artists for 99% of the cases.

I’ll consider to support all artists in a “tight” bbox mode if

get_window_extent gives back exact bounding box (accounting the

clipping) for “all” artists. Otherwise, I’m not inclined to do this.

I don’t know if you read this already, but: all artists should at least work with bbox_extra_artists, right?

While placing the legend outside the plot I tried the following:

egend = plot.legend()

pic.savefig(…, bbox_extra_artists=[legend])

but it fails:

Traceback (most recent call last):

File “./plot.py”, line 108, in

fig.savefig(sys.argv[1], bbox_inches=‘tight’, bbox_extra_artists=xa)
File “/usr/lib/pymodules/python2.6/matplotlib/figure.py”, line 1084, in savefig

self.canvas.print_figure(*args, **kwargs)

File “/usr/lib/pymodules/python2.6/matplotlib/backend_bases.py”, line 1894, in print_figure

in kwargs.pop("bbox_extra_artists", [])]

TypeError: get_window_extent() takes exactly 1 argument (2 given)

Yes. It is a bug.

Meanwhile, you may use

bbox_extra_artists=[leg.legendPatch]

as a workaround.

Regards,

-JJ

···

On Tue, Mar 8, 2011 at 2:20 AM, Benjamin Root <ben.root@...1304...> wrote:

And this appears to be a bug. Looks like the call signature for the legend
object's get_window_extent() doesn't match the call signature for all other
artists.

I was reading this at the time:

http://matplotlib.sourceforge.net/faq/usage_faq.html

I inferred pyplot was just a matlab-like interface on top of matplotlib,
and figured using directly the matplotlib was acceptable.

Yeah, I am guessing that page is a little out-dated and could be better
worded. However, the page does say that the preferred style is the pyplot
interface. Also notice that it is extremely rare for any of the
documentation to directly create the matplotlib objects without the pyplot
or pylab interface.

  I think this documentation should definitely be updated, then. I've been using matplotlib a lot the last few months and was totally unaware that pyplot was "required". Good thing I read this message! :slight_smile:

The interface should create the figure objects, the figure objects should
create the axes objects, the axes objects should create the axis objects,
and so on and so forth.

  That makes perfect sense, but is not at all what's implied by the text on the page linked above.

Best wishes,

···

On 2011-03-07 08:59, Benjamin Root wrote:

On Mon, Mar 7, 2011 at 10:33 AM, Yuri D'Elia<wavexx@...3457...> wrote:

--
Brendan Barnwell
"Do not follow where the path may lead. Go, instead, where there is no path, and leave a trail."
    --author unknown

Tell me what you think about this wording. Don’t worry about the links on the page:

https://github.com/WeatherGod/matplotlib/blob/62a02cce1ef98ff2360049ef31074bd9e82670d3/doc/faq/usage_faq.rst

I greatly appreciate any further comments you have. Your perspective is invaluable for improving our docs for users like you.

Ben Root

···

On Mon, Mar 7, 2011 at 12:42 PM, Brendan Barnwell <brenbarn@…1219…> wrote:

On 2011-03-07 08:59, Benjamin Root wrote:

On Mon, Mar 7, 2011 at 10:33 AM, Yuri D’Elia<wavexx@…3457…> wrote:

I was reading this at the time:

http://matplotlib.sourceforge.net/faq/usage_faq.html

I inferred pyplot was just a matlab-like interface on top of matplotlib,

and figured using directly the matplotlib was acceptable.

Yeah, I am guessing that page is a little out-dated and could be better

worded. However, the page does say that the preferred style is the pyplot

interface. Also notice that it is extremely rare for any of the

documentation to directly create the matplotlib objects without the pyplot

or pylab interface.

I think this documentation should definitely be updated, then. I’ve

been using matplotlib a lot the last few months and was totally

unaware that pyplot was “required”. Good thing I read this message! :slight_smile:

The interface should create the figure objects, the figure objects should

create the axes objects, the axes objects should create the axis objects,

and so on and so forth.

That makes perfect sense, but is not at all what’s implied by the

text on the page linked above.

Best wishes,

Brendan Barnwell

"Do not follow where the path may lead. Go, instead, where there is

no path, and leave a trail."

--author unknown