From: <lee...@us...> - 2010-05-19 00:32:25
|
Revision: 8320 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=8320&view=rev Author: leejjoon Date: 2010-05-19 00:32:18 +0000 (Wed, 19 May 2010) Log Message: ----------- merge mpl_toolkits.gridspec into the main tree Modified Paths: -------------- trunk/matplotlib/lib/matplotlib/axes.py trunk/matplotlib/lib/matplotlib/pyplot.py Added Paths: ----------- trunk/matplotlib/lib/matplotlib/gridspec.py Modified: trunk/matplotlib/lib/matplotlib/axes.py =================================================================== --- trunk/matplotlib/lib/matplotlib/axes.py 2010-05-18 17:14:45 UTC (rev 8319) +++ trunk/matplotlib/lib/matplotlib/axes.py 2010-05-19 00:32:18 UTC (rev 8320) @@ -8039,6 +8039,8 @@ triplot.__doc__ = mtri.triplot.__doc__ +from gridspec import GridSpec, SubplotSpec + class SubplotBase: """ Base class for subplots, which are :class:`Axes` instances with @@ -8062,92 +8064,63 @@ self.figure = fig - if len(args)==1: - s = str(args[0]) - if len(s) != 3: - raise ValueError('Argument to subplot must be a 3 digits long') - rows, cols, num = map(int, s) + if len(args) == 1: + if isinstance(args[0], SubplotSpec): + self._subplotspec = args[0] + + else: + s = str(args[0]) + if len(s) != 3: + raise ValueError('Argument to subplot must be a 3 digits long') + rows, cols, num = map(int, s) + self._subplotspec = GridSpec(rows, cols)[num-1] + # num - 1 for converting from matlab to python indexing elif len(args)==3: rows, cols, num = args + if isinstance(num, tuple) and len(num) == 2: + self._subplotspec = GridSpec(rows, cols)[num[0]-1:num[1]] + else: + self._subplotspec = GridSpec(rows, cols)[num-1] + # num - 1 for converting from matlab to python indexing else: raise ValueError( 'Illegal argument to subplot') - total = rows*cols - num -= 1 # convert from matlab to python indexing - # ie num in range(0,total) - if num >= total: - raise ValueError( 'Subplot number exceeds total subplots') - self._rows = rows - self._cols = cols - self._num = num - self.update_params() # _axes_class is set in the subplot_class_factory self._axes_class.__init__(self, fig, self.figbox, **kwargs) + + def get_geometry(self): 'get the subplot geometry, eg 2,2,3' - return self._rows, self._cols, self._num+1 + rows, cols, num1, num2 = self.get_subplotspec().get_geometry() + return rows, cols, num1+1 # for compatibility # COVERAGE NOTE: Never used internally or from examples def change_geometry(self, numrows, numcols, num): 'change subplot geometry, eg. from 1,1,1 to 2,2,3' - self._rows = numrows - self._cols = numcols - self._num = num-1 + self._subplotspec = GridSpec(numrows, numcols)[num-1] self.update_params() self.set_position(self.figbox) + def get_subplotspec(self): + 'get the SubplotSpec instance associated with the subplot' + return self._subplotspec + + def set_subplotspec(self, subplotspec): + 'set the SubplotSpec instance associated with the subplot' + self._subplotspec = subplotspec + def update_params(self): 'update the subplot position from fig.subplotpars' - rows = self._rows - cols = self._cols - num = self._num + self.figbox, self.rowNum, self.colNum, self.numRows, self.numCols = \ + self.get_subplotspec().get_position(self.figure, + return_all=True) - pars = self.figure.subplotpars - left = pars.left - right = pars.right - bottom = pars.bottom - top = pars.top - wspace = pars.wspace - hspace = pars.hspace - totWidth = right-left - totHeight = top-bottom - figH = totHeight/(rows + hspace*(rows-1)) - sepH = hspace*figH - - figW = totWidth/(cols + wspace*(cols-1)) - sepW = wspace*figW - - rowNum, colNum = divmod(num, cols) - - figBottom = top - (rowNum+1)*figH - rowNum*sepH - figLeft = left + colNum*(figW + sepW) - - self.figbox = mtransforms.Bbox.from_bounds(figLeft, figBottom, - figW, figH) - self.rowNum = rowNum - self.colNum = colNum - self.numRows = rows - self.numCols = cols - - if 0: - print 'rcn', rows, cols, num - print 'lbrt', left, bottom, right, top - print 'self.figBottom', self.figBottom - print 'self.figLeft', self.figLeft - print 'self.figW', self.figW - print 'self.figH', self.figH - print 'self.rowNum', self.rowNum - print 'self.colNum', self.colNum - print 'self.numRows', self.numRows - print 'self.numCols', self.numCols - - def is_first_col(self): return self.colNum==0 Added: trunk/matplotlib/lib/matplotlib/gridspec.py =================================================================== --- trunk/matplotlib/lib/matplotlib/gridspec.py (rev 0) +++ trunk/matplotlib/lib/matplotlib/gridspec.py 2010-05-19 00:32:18 UTC (rev 8320) @@ -0,0 +1,298 @@ +""" +:mod:`~matplotlib.gridspec` is a module which specifies the location +of the subplot in the figure. + + ``GridSpec`` + specifies the geometry of the grid that a subplot will be + placed. The number of rows and number of columns of the grid + need to be set. Optionally, the subplot layout parameters + (e.g., left, right, etc.) can be tuned. + + ``SubplotSpec`` + specifies the location of the subplot in the given *GridSpec*. + + +""" + +from __future__ import division + +import matplotlib +rcParams = matplotlib.rcParams + +import matplotlib.transforms as mtransforms + + +class GridSpec(object): + """ + A class that specifies the geometry of the grid that a subplot + will be placed. + """ + def __init__(self, nrows, ncols, + left=None, bottom=None, right=None, top=None, + wspace=None, hspace=None): + """ + The number of rows and number of columns of the + grid need to be set. Optionally, the subplot layout parameters + (e.g., left, right, etc.) can be tuned. + """ + #self.figure = figure + self._nrows , self._ncols = nrows, ncols + self.left=left + self.bottom=bottom + self.right=right + self.top=top + self.wspace=wspace + self.hspace=hspace + + def get_geometry(self): + 'get the geometry of the grid, eg 2,3' + return self._nrows, self._ncols + + _AllowedKeys = ["left", "bottom", "right", "top", "wspace", "hspace"] + + def update(self, **kwargs): + """ + Update the current values. If any kwarg is None, default to + the current value, if set, otherwise to rc. + """ + + for k, v in kwargs.items(): + if k in self._AllowedKeys: + setattr(self, k, v) + else: + raise AttributeError("%s is unknown keyword" % (k,)) + + + from matplotlib import _pylab_helpers + from matplotlib.axes import SubplotBase + for figmanager in _pylab_helpers.Gcf.figs.values(): + for ax in figmanager.canvas.figure.axes: + # copied from Figure.subplots_adjust + if not isinstance(ax, SubplotBase): + # Check if sharing a subplots axis + if ax._sharex is not None and isinstance(ax._sharex, SubplotBase): + ax._sharex.update_params() + ax.set_position(ax._sharex.figbox) + elif ax._sharey is not None and isinstance(ax._sharey,SubplotBase): + ax._sharey.update_params() + ax.set_position(ax._sharey.figbox) + else: + ax.update_params() + ax.set_position(ax.figbox) + + + def get_subplot_params(self, fig=None): + """ + return a dictionary of subplot layout parameters. The default + parameters are from rcParams unless a figure attribute is set. + """ + from matplotlib.figure import SubplotParams + import copy + if fig is None: + kw = dict([(k, rcParams["figure.subplot."+k]) \ + for k in self._AllowedKeys]) + subplotpars = SubplotParams(**kw) + else: + subplotpars = copy.copy(fig.subplotpars) + + update_kw = dict([(k, getattr(self, k)) for k in self._AllowedKeys]) + subplotpars.update(**update_kw) + + return subplotpars + + + def new_subplotspec(self, loc, rowspan=1, colspan=1): + """ + create and return a SuplotSpec instance. + """ + loc1, loc2 = loc + subplotspec = self[loc1:loc1+rowspan, loc2:loc2+colspan] + return subplotspec + + + def __getitem__(self, key): + """ + create and return a SuplotSpec instance. + """ + nrows, ncols = self.get_geometry() + total = nrows*ncols + + if isinstance(key, tuple): + try: + k1, k2 = key + except ValueError: + raise ValueError("unrecognized subplot spec") + + if isinstance(k1, slice): + row1, row2, _ = k1.indices(nrows) + else: + if k1 < 0: + k1 += nrows + row1, row2 = k1, k1+1 + + + if isinstance(k2, slice): + col1, col2, _ = k2.indices(ncols) + else: + if k2 < 0: + k2 += ncols + col1, col2 = k2, k2+1 + + + num1 = row1*nrows + col1 + num2 = (row2-1)*nrows + (col2-1) + + # single key + else: + if isinstance(key, slice): + num1, num2, _ = key.indices(total) + num2 -= 1 + else: + if key < 0: + key += total + num1, num2 = key, None + + + return SubplotSpec(self, num1, num2) + + +class GridSpecFromSubplotSpec(GridSpec): + """ + GridSpec whose subplot layout parameters are inherited from the + location specified by a given SubplotSpec. + """ + def __init__(self, nrows, ncols, + subplot_spec, + wspace=None, hspace=None): + """ + The number of rows and number of columns of the grid need to + be set. An instance of SubplotSpec is also need to be set from + which the layout parameters will be inheirted. The wspace and + hspace of the layout can be optionally specified or the + default values (from the figure or rcParams) will be used. + """ + self._nrows , self._ncols = nrows, ncols + self._wspace=wspace + self._hspace=hspace + + self._subplot_spec = subplot_spec + + def get_subplot_params(self, fig=None): + + if fig is None: + hspace = rcParams["figure.subplot.hspace"] + wspace = rcParams["figure.subplot.wspace"] + else: + hspace = fig.subplotpars.hspace + wspace = fig.subplotpars.wspace + + if self._hspace is not None: + hspace = self._hspace + + if self._wspace is not None: + wspace = self._wspace + + figbox = self._subplot_spec.get_position(fig, return_all=False) + + left, bottom, right, top = figbox.extents + + from matplotlib.figure import SubplotParams + sp = SubplotParams(left=left, + right=right, + bottom=bottom, + top=top, + wspace=wspace, + hspace=hspace) + + return sp + + + + +class SubplotSpec(object): + """ + specifies the location of the subplot in the given *GridSpec*. + """ + + def __init__(self, gridspec, num1, num2=None): + """ + The subplot will occupy the num1-th cell of the given + gridspec. If num2 is provided, the subplot will span between + num1-th cell and num2-th cell. + + The index stars from 0. + """ + + rows, cols = gridspec.get_geometry() + total = rows*cols + + self._gridspec = gridspec + self.num1 = num1 + self.num2 = num2 + + def get_gridspec(self): + return self._gridspec + + + def get_geometry(self): + """ + get the subplot geometry, eg 2,2,3. Unlike SuplorParams, + index is 0-based + """ + rows, cols = self.get_gridspec().get_geometry() + return rows, cols, self.num1, self.num2 + + + def get_position(self, fig, return_all=False): + """ + update the subplot position from fig.subplotpars + """ + + gridspec = self.get_gridspec() + rows, cols = gridspec.get_geometry() + + subplot_params = gridspec.get_subplot_params(fig) + left = subplot_params.left + right = subplot_params.right + bottom = subplot_params.bottom + top = subplot_params.top + wspace = subplot_params.wspace + hspace = subplot_params.hspace + totWidth = right-left + totHeight = top-bottom + + figH = totHeight/(rows + hspace*(rows-1)) + sepH = hspace*figH + + figW = totWidth/(cols + wspace*(cols-1)) + sepW = wspace*figW + + rowNum, colNum = divmod(self.num1, cols) + figBottom = top - (rowNum+1)*figH - rowNum*sepH + figLeft = left + colNum*(figW + sepW) + figTop = figBottom + figH + figRight = figLeft + figW + + if self.num2 is not None: + + rowNum2, colNum2 = divmod(self.num2, cols) + figBottom2 = top - (rowNum2+1)*figH - rowNum2*sepH + figLeft2 = left + colNum2*(figW + sepW) + figTop2 = figBottom2 + figH + figRight2 = figLeft2 + figW + + figBottom = min(figBottom, figBottom2) + figLeft = min(figLeft, figLeft2) + figTop = max(figTop, figTop2) + figRight = max(figRight, figRight2) + + figbox = mtransforms.Bbox.from_extents(figLeft, figBottom, + figRight, figTop) + + + if return_all: + return figbox, rowNum, colNum, rows, cols + else: + return figbox + + Modified: trunk/matplotlib/lib/matplotlib/pyplot.py =================================================================== --- trunk/matplotlib/lib/matplotlib/pyplot.py 2010-05-18 17:14:45 UTC (rev 8319) +++ trunk/matplotlib/lib/matplotlib/pyplot.py 2010-05-19 00:32:18 UTC (rev 8320) @@ -12,7 +12,7 @@ from matplotlib.rcsetup import interactive_bk as _interactive_bk from matplotlib.artist import getp, get, Artist from matplotlib.artist import setp as _setp -from matplotlib.axes import Axes +from matplotlib.axes import Axes, Subplot from matplotlib.projections import PolarAxes from matplotlib import mlab # for csv2rec, detrend_none, window_hanning from matplotlib.scale import get_scale_docs, get_scale_names @@ -764,6 +764,44 @@ return fig, axarr.reshape(nrows, ncols) +from gridspec import GridSpec +def subplot2grid(shape, loc, rowspan=1, colspan=1, **kwargs): + """ + + It creates a subplot in a grid of *shape*, at location of *loc*, + spanning *rowspan*, *colspan* cells in each direction. + The index for loc is 0-based. :: + + subplot2grid(shape, loc, rowspan=1, colspan=1) + + is identical to :: + + gridspec=GridSpec(shape[0], shape[2]) + subplotspec=gridspec.new_subplotspec(loc, rowspan, colspan) + subplot(subplotspec) + + + """ + + fig = gcf() + s1, s2 = shape + subplotspec = GridSpec(s1, s2).new_subplotspec(loc, + rowspan=rowspan, + colspan=colspan) + a = Subplot(fig, subplotspec, **kwargs) + fig.add_subplot(a) + bbox = a.bbox + byebye = [] + for other in fig.axes: + if other==a: continue + if bbox.fully_overlaps(other.bbox): + byebye.append(other) + for ax in byebye: delaxes(ax) + + draw_if_interactive() + return a + + def twinx(ax=None): """ Make a second axes overlay *ax* (or the current axes if *ax* is This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |