[Matplotlib-users] Hints on sizing plot elements?

2010-08-11 Thread Russell E. Owen
I'm making a strip chart widget (which I plan to make publicly available 
when finished). The basics are working fine, but the automatic sizing is 
not doing so well. Strip charts are typically short, and when suitably 
short the X axis annotations are partially truncated.

So...can I convince the automatic sizer to always show the full X (time) 
axis annotations and put all the variable sizing into the data area? Or 
do I have to manually set them somehow?

Also, is there a way from my software (not a .matplotlibrc file) to 
globally make the default background color white for axis annotation 
areas? Right now the background is gray for annotations and while for 
plot area and I'd prefer it was all white.

-- Russell


--
This SF.net email is sponsored by 

Make an app they can't live without
Enter the BlackBerry Developer Challenge
http://p.sf.net/sfu/RIM-dev2dev 
___
Matplotlib-users mailing list
Matplotlib-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/matplotlib-users


Re: [Matplotlib-users] Hints on sizing plot elements?

2010-08-12 Thread Tony S Yu

On Aug 12, 2010, at 1:08 AM, Russell E. Owen wrote:

> I'm making a strip chart widget (which I plan to make publicly available 
> when finished). The basics are working fine, but the automatic sizing is 
> not doing so well. Strip charts are typically short, and when suitably 
> short the X axis annotations are partially truncated.
> 
> So...can I convince the automatic sizer to always show the full X (time) 
> axis annotations and put all the variable sizing into the data area? Or 
> do I have to manually set them somehow?

As far as I know, there's nothing to automatically resize the padding around 
the the axes. (The manual way to do it is to call `fig.subplots_adjust`). I 
wrote a helper script (attached below) to adjust the layout so that there's a 
specified amount of padding around the axes.

Unfortunately, this resizer doesn't function correctly when using the GTK 
backend. It apparently works fine with TkAgg, MacOSX, and Qt4Agg backends.


> Also, is there a way from my software (not a .matplotlibrc file) to 
> globally make the default background color white for axis annotation 
> areas? Right now the background is gray for annotations and while for 
> plot area and I'd prefer it was all white.
> 
> -- Russell


Just set plt.rc('figure', facecolor='w') in your code (assuming you've imported 
matplotlib.pyplot as plt).

Best,
-Tony


import numpy as np

import matplotlib.pyplot as plt
from matplotlib.transforms import TransformedBbox, Affine2D


PAD_INCHES = 0.1


def tight_layout(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

Re: [Matplotlib-users] Hints on sizing plot elements?

2010-08-12 Thread Russell Owen

On Aug 12, 2010, at 7:37 AM, Tony S Yu wrote:


On Aug 12, 2010, at 1:08 AM, Russell E. Owen wrote:

I'm making a strip chart widget (which I plan to make publicly  
available
when finished). The basics are working fine, but the automatic  
sizing is
not doing so well. Strip charts are typically short, and when  
suitably

short the X axis annotations are partially truncated.

So...can I convince the automatic sizer to always show the full X  
(time)
axis annotations and put all the variable sizing into the data  
area? Or

do I have to manually set them somehow?


As far as I know, there's nothing to automatically resize the  
padding around the the axes. (The manual way to do it is to call  
`fig.subplots_adjust`). I wrote a helper script (attached below) to  
adjust the layout so that there's a specified amount of padding  
around the axes.


Unfortunately, this resizer doesn't function correctly when using  
the GTK backend. It apparently works fine with TkAgg, MacOSX, and  
Qt4Agg backends.


Fortunately I'm using TkAgg. I'm sorry (though not surprised) I'll  
have to size it manually (especially since users of my application can  
change the font size), but I very much appreciate the code.



Also, is there a way from my software (not a .matplotlibrc file) to

globally make the default background color white for axis annotation
areas? Right now the background is gray for annotations and while for
plot area and I'd prefer it was all white.


Just set plt.rc('figure', facecolor='w') in your code (assuming  
you've imported matplotlib.pyplot as plt).


Perfect. Thanks!




Usage note: Since it sounds like you're not creating subplots, you  
can just use the `tight_borders` function, instead of the  
`tight_layout` function; the later requires two redraws while the  
first requires only one.


Thank you very much for the advice and script!

I do plan to support strip charts stacked atop each other (all using  
the same time range, with tick labels only along the bottom x axis),  
which I suspect will need subplots (unless there is now a better way)  
but so far all I have is multiple lines (including dynamic and static  
-- e.g. to display limits) one one plot.


-- Russell

--
This SF.net email is sponsored by 

Make an app they can't live without
Enter the BlackBerry Developer Challenge
http://p.sf.net/sfu/RIM-dev2dev ___
Matplotlib-users mailing list
Matplotlib-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/matplotlib-users


Re: [Matplotlib-users] Hints on sizing plot elements?

2010-08-15 Thread Jae-Joon Lee
On Thu, Aug 12, 2010 at 2:08 PM, Russell E. Owen  wrote:
> So...can I convince the automatic sizer to always show the full X (time)
> axis annotations and put all the variable sizing into the data area? Or
> do I have to manually set them somehow?
>

Another option you may try is to use axes_grid1 toolkit.
Attached is a simplified version of the script I have been using.
matplotlib v1.0 is required.

IHTH,

-JJ
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import SubplotDivider
import mpl_toolkits.axes_grid1.axes_size as axes_size

class SizeFromFunc(axes_size._Base):
def __init__(self, func):
self._func = func

def get_size(self, renderer):
rel_size = 0.

bb = self._func(renderer)
dpi = renderer.points_to_pixels(72.)
abs_size = bb/dpi

return rel_size, abs_size

class GetExtentHelper(object):
def __init__(self, ax):
self._ax = ax
def __call__(self, renderer):
# return width of ticklabels on the left side of axes
return self._ax.yaxis.get_ticklabel_extents(renderer)[0].width

fig = plt.figure()
ax = fig.add_subplot(111)
ax.set_yticks([0.5])
ax.set_yticklabels(["very long label"])

helper = GetExtentHelper(ax)

divider = SubplotDivider(fig, 1, 1, 1,
 horizontal=[SizeFromFunc(helper),
 axes_size.Scaled(1.)],
 vertical=[axes_size.Scaled(1.)],
 aspect=False)
ax.set_axes_locator(divider.new_locator(nx=1, ny=0))

plt.show()

--
This SF.net email is sponsored by 

Make an app they can't live without
Enter the BlackBerry Developer Challenge
http://p.sf.net/sfu/RIM-dev2dev ___
Matplotlib-users mailing list
Matplotlib-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/matplotlib-users