From 2f9b45de803c48191c4ed714da66645e397adfdd Mon Sep 17 00:00:00 2001 From: Jody Klymak Date: Fri, 5 Oct 2018 13:20:34 -0700 Subject: [PATCH 1/4] ENH: fig.set_size to allow non-inches units --- lib/matplotlib/cbook/__init__.py | 18 +++++++++++ lib/matplotlib/figure.py | 52 ++++++++++++++++++++++++++++++-- 2 files changed, 67 insertions(+), 3 deletions(-) diff --git a/lib/matplotlib/cbook/__init__.py b/lib/matplotlib/cbook/__init__.py index 554abe8d6677..1d22aa6d42ef 100644 --- a/lib/matplotlib/cbook/__init__.py +++ b/lib/matplotlib/cbook/__init__.py @@ -2075,3 +2075,21 @@ def _check_and_log_subprocess(command, logger, **kwargs): .format(command, exc.output.decode('utf-8'))) logger.debug(report) return report + + +def _print_unit(st): + """ + Convert from a string with a number and units into inches + """ + print_units = {'cm': 2.54, 'pt': 72.0, 'mm': 25.4, 'in': 1.0} + try: + num = float(st[:-2]) + except: + # let the parent handle the errors + return st + unit = st[-2:] + if unit in print_units: + return num / print_units[unit] + else: + # let the parent handle the errors + return st diff --git a/lib/matplotlib/figure.py b/lib/matplotlib/figure.py index 819525482cb7..1aeb56c6b0eb 100644 --- a/lib/matplotlib/figure.py +++ b/lib/matplotlib/figure.py @@ -862,19 +862,63 @@ def figimage(self, X, xo=0, yo=0, alpha=None, norm=None, cmap=None, self.stale = True return im + def set_size(self, w, h=None, forward=True): + """ + Set the figure size + + Parameters + ---------- + + w: float, string, or two-tuple of either. + + If a two-tuple, (width, height) of the figure. + + If a float, width of figure in inches. + + If a string, its a float followed by a 2-character suffix + of either 'in', 'cm', 'mm', 'pt' (inches, centimeters, + millimeters, or points). i.e. ``w='4.6cm'``. + + h: float or string + As above, if a float it is the hieght of the figure in inches, + or a string decoded as above. + + forward : bool, default True + Forward to the window so the canvas size is updated; + e.g., you can resize the figure window from the shell + + See Also + -------- + matplotlib.Figure.set_size_inches + + """ + if h is None: + w, h = w + if isinstance(w, str): + w = cbook._print_unit(w) + if isinstance(w, str): + raise ValueError('Could not convert str {} to ' + 'inches'.format(w)) + if isinstance(h, str): + h = cbook._print_unit(h) + if isinstance(h, str): + raise ValueError('Could not convert str {} to ' + 'inches'.format(h)) + self.set_size_inches(w, h, forward=forward) + def set_size_inches(self, w, h=None, forward=True): - """Set the figure size in inches. + """Set the figure size. Call signatures:: fig.set_size_inches(w, h) # OR fig.set_size_inches((w, h)) - optional kwarg *forward=True* will cause the canvas size to be + Optional kwarg *forward=True* will cause the canvas size to be automatically updated; e.g., you can resize the figure window from the shell - ACCEPTS: a (w, h) tuple with w, h in inches + ACCEPTS: a (w, h) tuple with w, h in inches. See Also -------- @@ -883,8 +927,10 @@ def set_size_inches(self, w, h=None, forward=True): # the width and height have been passed in as a tuple to the first # argument, so unpack them + if h is None: w, h = w + if not all(np.isfinite(_) for _ in (w, h)): raise ValueError('figure size must be finite not ' '({}, {})'.format(w, h)) From 4b8c8f916ced2c1f632fc0fe18ed3c3d03caebe1 Mon Sep 17 00:00:00 2001 From: Jody Klymak Date: Sat, 6 Oct 2018 21:04:12 -0700 Subject: [PATCH 2/4] ENH: unit argument --- lib/matplotlib/figure.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/figure.py b/lib/matplotlib/figure.py index 1aeb56c6b0eb..d084e27b37d4 100644 --- a/lib/matplotlib/figure.py +++ b/lib/matplotlib/figure.py @@ -862,7 +862,7 @@ def figimage(self, X, xo=0, yo=0, alpha=None, norm=None, cmap=None, self.stale = True return im - def set_size(self, w, h=None, forward=True): + def set_size(self, w, h=None, units=None, forward=True): """ Set the figure size @@ -883,6 +883,11 @@ def set_size(self, w, h=None, forward=True): As above, if a float it is the hieght of the figure in inches, or a string decoded as above. + units : string + If specified, will be applied to floats in (w, h). If + either (w, h) are strings, the units they specify are used + for that dimension. Allows ``fig.set_size(12, 15, units='cm')`` + forward : bool, default True Forward to the window so the canvas size is updated; e.g., you can resize the figure window from the shell @@ -894,16 +899,32 @@ def set_size(self, w, h=None, forward=True): """ if h is None: w, h = w + + conv = 1 # default units are inches. + if isinstance(units, str): + conv = cbook._print_unit('1'+units) + if isinstance(conv, str): + raise ValueError('Could not convert units str {} to ' + 'inches'.format(conv)) + if isinstance(w, str): w = cbook._print_unit(w) if isinstance(w, str): raise ValueError('Could not convert str {} to ' 'inches'.format(w)) + else: + # convert using units arg + w = w * conv + if isinstance(h, str): h = cbook._print_unit(h) if isinstance(h, str): raise ValueError('Could not convert str {} to ' 'inches'.format(h)) + else: + # convert using units arg + h = h * conv + self.set_size_inches(w, h, forward=forward) def set_size_inches(self, w, h=None, forward=True): From 07efa68bc6e2e274749a2716d574b111afa25a3e Mon Sep 17 00:00:00 2001 From: Jody Klymak Date: Sun, 7 Oct 2018 09:42:16 -0700 Subject: [PATCH 3/4] ENH --- lib/matplotlib/cbook/__init__.py | 7 +++---- lib/matplotlib/figure.py | 27 ++++++++++++++++++--------- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/lib/matplotlib/cbook/__init__.py b/lib/matplotlib/cbook/__init__.py index 1d22aa6d42ef..e6b44574ab9e 100644 --- a/lib/matplotlib/cbook/__init__.py +++ b/lib/matplotlib/cbook/__init__.py @@ -2082,11 +2082,10 @@ def _print_unit(st): Convert from a string with a number and units into inches """ print_units = {'cm': 2.54, 'pt': 72.0, 'mm': 25.4, 'in': 1.0} - try: + if len(st) > 2: num = float(st[:-2]) - except: - # let the parent handle the errors - return st + else: + num = 1 unit = st[-2:] if unit in print_units: return num / print_units[unit] diff --git a/lib/matplotlib/figure.py b/lib/matplotlib/figure.py index d084e27b37d4..baabdf05cde4 100644 --- a/lib/matplotlib/figure.py +++ b/lib/matplotlib/figure.py @@ -344,10 +344,13 @@ def __init__(self, if frameon is None: frameon = rcParams['figure.frameon'] - if not np.isfinite(figsize).all(): - raise ValueError('figure size must be finite not ' - '{}'.format(figsize)) - self.bbox_inches = Bbox.from_bounds(0, 0, *figsize) + self.canvas = None + self._suptitle = None + + # init the bbox to dummy values: + self.bbox_inches = Bbox.from_bounds(0, 0, 1, 1) + # set properly. + self.set_size(figsize) self.dpi_scale_trans = Affine2D().scale(dpi, dpi) # do not use property as it will trigger @@ -364,9 +367,6 @@ def __init__(self, self._set_artist_props(self.patch) self.patch.set_antialiased(False) - self.canvas = None - self._suptitle = None - if subplotpars is None: subplotpars = SubplotParams() @@ -391,6 +391,14 @@ def __init__(self, # list of child gridspecs for this figure self._gridspecs = [] + def _parse_figsize(self, figsize): + if len(figsize) == 3: + unit = figsize[-1] + figsize = figsize[:2] + conv = cbook._print_unit(unit) + figsize = [f * conv for f in figsize] + return figsize + # TODO: I'd like to dynamically add the _repr_html_ method # to the figure in the right context, but then IPython doesn't # use it, for some reason. @@ -898,11 +906,12 @@ def set_size(self, w, h=None, units=None, forward=True): """ if h is None: - w, h = w + figsize = self._parse_figsize(w) + w, h = figsize conv = 1 # default units are inches. if isinstance(units, str): - conv = cbook._print_unit('1'+units) + conv = cbook._print_unit(units) if isinstance(conv, str): raise ValueError('Could not convert units str {} to ' 'inches'.format(conv)) From 8bbab23ca4f0d77920e910c456e777a280b0377c Mon Sep 17 00:00:00 2001 From: Jody Klymak Date: Sun, 7 Oct 2018 13:32:21 -0700 Subject: [PATCH 4/4] ENH: add px --- lib/matplotlib/cbook/__init__.py | 10 ++++++++-- lib/matplotlib/figure.py | 16 ++++++++-------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/lib/matplotlib/cbook/__init__.py b/lib/matplotlib/cbook/__init__.py index e6b44574ab9e..ce0ddbfe4216 100644 --- a/lib/matplotlib/cbook/__init__.py +++ b/lib/matplotlib/cbook/__init__.py @@ -2077,7 +2077,7 @@ def _check_and_log_subprocess(command, logger, **kwargs): return report -def _print_unit(st): +def _print_unit(st, dpi=None): """ Convert from a string with a number and units into inches """ @@ -2087,7 +2087,13 @@ def _print_unit(st): else: num = 1 unit = st[-2:] - if unit in print_units: + # special case "px" + if unit == 'px': + if dpi is not None: + return num / dpi + else: + raise ValueError('px units need dpi to be set') + elif unit in print_units: return num / print_units[unit] else: # let the parent handle the errors diff --git a/lib/matplotlib/figure.py b/lib/matplotlib/figure.py index baabdf05cde4..9a0212f46722 100644 --- a/lib/matplotlib/figure.py +++ b/lib/matplotlib/figure.py @@ -346,15 +346,15 @@ def __init__(self, self.canvas = None self._suptitle = None + self.dpi_scale_trans = Affine2D().scale(dpi, dpi) + # do not use property as it will trigger + self._dpi = dpi # init the bbox to dummy values: self.bbox_inches = Bbox.from_bounds(0, 0, 1, 1) # set properly. self.set_size(figsize) - self.dpi_scale_trans = Affine2D().scale(dpi, dpi) - # do not use property as it will trigger - self._dpi = dpi self.bbox = TransformedBbox(self.bbox_inches, self.dpi_scale_trans) self.frameon = frameon @@ -395,7 +395,7 @@ def _parse_figsize(self, figsize): if len(figsize) == 3: unit = figsize[-1] figsize = figsize[:2] - conv = cbook._print_unit(unit) + conv = cbook._print_unit(unit, dpi=self._dpi) figsize = [f * conv for f in figsize] return figsize @@ -911,13 +911,13 @@ def set_size(self, w, h=None, units=None, forward=True): conv = 1 # default units are inches. if isinstance(units, str): - conv = cbook._print_unit(units) + conv = cbook._print_unit(units, dpi=self._dpi) if isinstance(conv, str): raise ValueError('Could not convert units str {} to ' 'inches'.format(conv)) if isinstance(w, str): - w = cbook._print_unit(w) + w = cbook._print_unit(w, dpi=self._dpi) if isinstance(w, str): raise ValueError('Could not convert str {} to ' 'inches'.format(w)) @@ -926,14 +926,14 @@ def set_size(self, w, h=None, units=None, forward=True): w = w * conv if isinstance(h, str): - h = cbook._print_unit(h) + h = cbook._print_unit(h, dpi=self._dpi) if isinstance(h, str): raise ValueError('Could not convert str {} to ' 'inches'.format(h)) else: # convert using units arg h = h * conv - + # all done: set... self.set_size_inches(w, h, forward=forward) def set_size_inches(self, w, h=None, forward=True):