On Sep 17, 2010, at 8:58 AM, Erik Janssens wrote:
> Hi,
>
> I'm using matplotlib in a pyqt context, as described
> in the book 'Matplotlib for python developers'.
>
> When the window is large enough, everything works
> fine, but when the window is too small part of
> the x-axis labels get chopped.
>
> Is there a way to prevent this ? Preferable
> in combination with the user being able to
> resize the window.
>
> Thanks a lot,
>
> Erik
Hi Erik,
Last I checked there was nothing automatic for adjusting the spacing around
subplots. You can do so manually using ``plt.subplots_adjust`` (see for
example,
http://matplotlib.sourceforge.net/examples/pylab_examples/subplots_adjust.html).
A while back, I wrote some functions to calculate a good set of parameters for
subplots_adjust (see attached; examples in if-main block at bottom). I've been
using these functions pretty regularly, and it works pretty well for my
purposes.
Best,
-Tony
Some usage notes:
The function has to draw the figure a couple of times to calculate correct
spacing. When redrawing the figure (e.g. when you resize the window), you'd
have to re-call the function, which would redraw the figure a couple of times
before drawing the final figure. That's all to say: this is a fairly slow
function. If you don't have subplots (like in your example), you can call
"layout.tight_borders()" (instead of "layout.tight()"), which only requires a
single redraw.
When I originally posted this to the developers list, the functions didn't work
with the GtkAgg backend. As far as I know, this hasn't changed. It should work
fine for Qt4Agg, macosx, and TkAgg backends.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.transforms import TransformedBbox, Affine2D
PAD_INCHES = 0.1
def tight(pad_inches=PAD_INCHES, h_pad_inches=None, w_pad_inches=None):
"""Adjust subplot parameters to give specified padding.
Parameters
----------
pad_inches : float
minimum padding between the figure edge and the edges of subplots.
h_pad_inches, w_pad_inches : float
minimum padding (height/width) between edges of adjacent subplots.
Defaults to `pad_inches`.
"""
if h_pad_inches is None:
h_pad_inches = pad_inches
if w_pad_inches is None:
w_pad_inches = pad_inches
fig = plt.gcf()
tight_borders(fig, pad_inches=pad_inches)
# NOTE: border padding affects subplot spacing; tighten border first
tight_subplot_spacing(fig, h_pad_inches, w_pad_inches)
def tight_borders(fig, pad_inches=PAD_INCHES):
"""Stretch subplot boundaries to figure edges plus padding."""
# call draw to update the renderer and get accurate bboxes.
fig.canvas.draw()
bbox_original = fig.bbox_inches
bbox_tight = _get_tightbbox(fig, pad_inches)
# figure dimensions ordered like bbox.extents: x0, y0, x1, y1
lengths = np.array([bbox_original.width, bbox_original.height,
bbox_original.width, bbox_original.height])
whitespace = (bbox_tight.extents - bbox_original.extents) / lengths
# border padding ordered like bbox.extents: x0, y0, x1, y1
current_borders = np.array([fig.subplotpars.left, fig.subplotpars.bottom,
fig.subplotpars.right, fig.subplotpars.top])
left, bottom, right, top = current_borders - whitespace
fig.subplots_adjust(bottom=bottom, top=top, left=left, right=right)
def _get_tightbbox(fig, pad_inches):
renderer = fig.canvas.get_renderer()
bbox_inches = fig.get_tightbbox(renderer)
return bbox_inches.padded(pad_inches)
def tight_subplot_spacing(fig, h_pad_inches, w_pad_inches):
"""Stretch subplots so adjacent subplots are separated by given padding."""
# Zero hspace and wspace to make it easier to calculate the spacing.
fig.subplots_adjust(hspace=0, wspace=0)
fig.canvas.draw()
figbox = fig.bbox_inches
ax_bottom, ax_top, ax_left, ax_right = _get_grid_boundaries(fig)
nrows, ncols = ax_bottom.shape
subplots_height = fig.subplotpars.top - fig.subplotpars.bottom
if nrows > 1:
h_overlap_inches = ax_top[1:] - ax_bottom[:-1]
hspace_inches = h_overlap_inches.max() + h_pad_inches
hspace_fig_frac = hspace_inches / figbox.height
hspace = _fig_frac_to_cell_frac(hspace_fig_frac, subplots_height, nrows)
fig.subplots_adjust(hspace=hspace)
subplots_width = fig.subplotpars.right - fig.subplotpars.left
if ncols > 1:
w_overlap_inches = ax_right[:,:-1] - ax_left[:,1:]
wspace_inches = w_overlap_inches.max() + w_pad_inches
wspace_fig_frac = wspace_inches / figbox.width
wspace = _fig_frac_to_cell_frac(wspace_fig_frac, subplots_width, ncols)
fig.subplots_adjust(wspace=wspace)
def _get_grid_boundaries(fig):
"""Return grid boundaries for bboxes of subplots
Returns
-------
ax_bottom, ax_top, ax_left, ax_right : array
bbox cell-boundaries of subplot grid. If a subplot spans cells, the grid
boundaries cutting through that subplot will be masked.
"""
nrows, ncols, n = fig.axes[0].get_geometry()
# Initialize boundaries as masked arrays; in the future, support subplots
# that span multiple rows/columns, which would have masked values for grid
# boundaries that cut through the subplot.
ax_bottom, ax_top, ax_left, ax_right = [np.ma.masked_all((nrows, ncols))
for n in range(4)]
renderer = fig.canvas.get_renderer()
px2inches_trans = Affine2D().scale(1./fig.dpi)
for ax in fig.axes:
ax_bbox = ax.get_tightbbox(renderer)
x0, y0, x1, y1 = TransformedBbox(ax_bbox, px2inches_trans).extents
nrows, ncols, n = ax.get_geometry()
# subplot number starts at 1, matrix index starts at 0
i = n - 1
ax_bottom.flat[i] = y0
ax_top.flat[i] = y1
ax_left.flat[i] = x0
ax_right.flat[i] = x1
return ax_bottom, ax_top, ax_left, ax_right
def _fig_frac_to_cell_frac(fig_frac, subplots_frac, num_cells):
"""Return fraction of cell (row/column) from a given fraction of the figure
Parameters
----------
fig_frac : float
length given as a fraction of figure height or width
subplots_frac : float
fraction of figure (height or width) occupied by subplots
num_cells : int
number of rows or columns.
"""
# This function is reverse engineered from the calculation of `sepH` and
# `sepW` in `GridSpecBase.get_grid_positions`.
return (fig_frac * num_cells) / (subplots_frac - fig_frac*(num_cells-1))
if __name__ == '__main__':
import random
fontsizes = [8, 16, 24, 32]
def example_plot(ax):
ax.plot([1, 2])
ax.set_xlabel('x-label', fontsize=random.choice(fontsizes))
ax.set_ylabel('y-label', fontsize=random.choice(fontsizes))
ax.set_title('Title', fontsize=random.choice(fontsizes))
fig, ax = plt.subplots()
example_plot(ax)
tight()
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(nrows=2, ncols=2)
example_plot(ax1)
example_plot(ax2)
example_plot(ax3)
example_plot(ax4)
tight()
fig, (ax1, ax2) = plt.subplots(nrows=2, ncols=1)
example_plot(ax1)
example_plot(ax2)
tight()
fig, (ax1, ax2) = plt.subplots(nrows=1, ncols=2)
example_plot(ax1)
example_plot(ax2)
tight()
fig, axes = plt.subplots(nrows=3, ncols=3)
for row in axes:
for ax in row:
example_plot(ax)
tight()
plt.show()
------------------------------------------------------------------------------
Start uncovering the many advantages of virtual appliances
and start using them to simplify application deployment and
accelerate your shift to cloud computing.
http://p.sf.net/sfu/novell-sfdev2dev
_______________________________________________
Matplotlib-users mailing list
Matplotlib-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/matplotlib-users