From 9cb6472d66b3bc707f133544c676f41c4610c0c1 Mon Sep 17 00:00:00 2001 From: Leeonadoh Date: Sat, 18 Apr 2015 01:00:11 -0400 Subject: [PATCH 1/3] Introduced UniformLegend class and associated handlers. TODO: Modify axes.legend and axes.figlegend to use UniformLegend when uniform size is desired. --- lib/matplotlib/legend.py | 99 +++++++++++++++++++++++++++++++- lib/matplotlib/legend_handler.py | 49 ++++++++++++++++ 2 files changed, 147 insertions(+), 1 deletion(-) mode change 100644 => 100755 lib/matplotlib/legend.py mode change 100644 => 100755 lib/matplotlib/legend_handler.py diff --git a/lib/matplotlib/legend.py b/lib/matplotlib/legend.py old mode 100644 new mode 100755 index a312bbd113a3..c978a4da4a53 --- a/lib/matplotlib/legend.py +++ b/lib/matplotlib/legend.py @@ -803,7 +803,7 @@ def get_title(self): return self._legend_title_box._text def get_window_extent(self, *args, **kwargs): - 'return a extent of the legend' + 'return a extent of the the legend' return self.legendPatch.get_window_extent(*args, **kwargs) def get_frame_on(self): @@ -993,3 +993,100 @@ def draggable(self, state=None, use_blit=False, update="loc"): self._draggable = None return self._draggable + + +class UniformLegend(Legend): + def __str__(self): + return "uniformLegend" + + def __init__(self, parent, handles, labels,uniform_size, + loc=None, + numpoints=None, # the number of points in the legend line + markerfirst=True, # controls ordering (left-to-right) of + # legend marker and label + scatterpoints=None, # number of scatter points + scatteryoffsets=None, + prop=None, # properties for the legend texts + fontsize=None, # keyword to set font size directly + + # spacing & pad defined as a fraction of the font-size + borderpad=None, # the whitespace inside the legend border + labelspacing=None, # the vertical space between the legend + # entries + handlelength=None, # the length of the legend handles + handleheight=None, # the height of the legend handles + handletextpad=None, # the pad between the legend handle + # and text + borderaxespad=None, # the pad between the axes and legend + # border + columnspacing=None, # spacing between columns + + ncol=1, # number of columns + mode=None, # mode for horizontal distribution of columns. + # None, "expand" + + fancybox=None, # True use a fancy box, false use a rounded + # box, none use rc + shadow=None, + title=None, # set a title for the legend + + framealpha=None, # set frame alpha + + bbox_to_anchor=None, # bbox that the legend will be anchored. + bbox_transform=None, # transform for the bbox + frameon=None + ): + uniformHandlerMap = { + StemContainer: + legend_handler.HandlerUniformStem(uniform_size=uniform_size), + ErrorbarContainer: + legend_handler.HandlerUniformErrorBar(uniform_size=uniform_size), + Line2D: legend_handler.HandlerUniformLine2D(uniform_size=uniform_size), + PathCollection: + legend_handler.HandlerPathCollection(sizes=[uniform_size]*3), + RegularPolyCollection: + legend_handler.HandlerRegularPolyCollection(sizes=[uniform_size]*3), + CircleCollection: + legend_handler.HandlerCircleCollection(sizes=[uniform_size]*3), + } + + Legend.__init__(self,parent,handles,labels, + loc=loc, + numpoints=numpoints, # the number of points in the legend line + markerscale=None, # the relative size of legend markers + # vs. original + markerfirst=markerfirst, # controls ordering (left-to-right) of + # legend marker and label + scatterpoints=scatterpoints, # number of scatter points + scatteryoffsets=scatteryoffsets, + prop=prop, # properties for the legend texts + fontsize=fontsize, # keyword to set font size directly + + # spacing & pad defined as a fraction of the font-size + borderpad=borderpad, # the whitespace inside the legend border + labelspacing=labelspacing, # the vertical space between the legend + # entries + handlelength=handlelength, # the length of the legend handles + handleheight=handleheight, # the height of the legend handles + handletextpad=handletextpad, # the pad between the legend handle + # and text + borderaxespad=borderaxespad, # the pad between the axes and legend + # border + columnspacing=columnspacing, # spacing between columns + + ncol=1, # number of columns + mode=mode, # mode for horizontal distribution of columns. + # None, "expand" + + fancybox=fancybox, # True use a fancy box, false use a rounded + # box, none use rc + shadow=shadow, + title=title, # set a title for the legend + + framealpha=framealpha, # set frame alpha + + bbox_to_anchor=bbox_to_anchor, # bbox that the legend will be anchored. + bbox_transform=bbox_transform, # transform for the bbox + frameon=frameon, # draw frame + handler_map=uniformHandlerMap, + ) diff --git a/lib/matplotlib/legend_handler.py b/lib/matplotlib/legend_handler.py old mode 100644 new mode 100755 index 69af7aca28c8..296b4d7caf7c --- a/lib/matplotlib/legend_handler.py +++ b/lib/matplotlib/legend_handler.py @@ -617,3 +617,52 @@ def create_artists(self, legend, orig_handle, self.update_prop(p, orig_handle, legend) p.set_transform(trans) return [p] +class HandlerUniformLine2D(HandlerLine2D): + """ + Handler for Uniform sized Line2D instances + """ + + def __init__(self, uniform_size, **kw): + self._uniform_size = uniform_size + HandlerLine2D.__init__(self, **kw) + + def create_artists(self, legend, orig_handle, + xdescent, ydescent, width, height, fontsize, + trans): + artists = HandlerLine2D.create_artists(self, legend, orig_handle, + xdescent, ydescent, width, height, fontsize, + trans) + artists[-1].set_markersize(self._uniform_size) + return artists +class HandlerUniformErrorBar(HandlerErrorbar): + """ + Handler for Uniform sized Error instances + """ + + def __init__(self, uniform_size, **kw): + self._uniform_size = uniform_size + HandlerErrorbar.__init__(self, **kw) + + def create_artists(self, legend, orig_handle, + xdescent, ydescent, width, height, fontsize, + trans): + artists = HandlerErrorbar.create_artists(self, legend, orig_handle, + xdescent, ydescent, width, height, fontsize, + trans) + artists[-1].set_markersize(self._uniform_size) + return artists +class HandlerUniformStem(HandlerStem): + def __init__(self, uniform_size, **kw): + + HandlerStem.__init__(self, **kw) + self._uniform_size = uniform_size + + def create_artists(self, legend, orig_handle, + xdescent, ydescent, width, height, fontsize, + trans): + artists = HandlerStem.create_artists(self, legend, orig_handle, + xdescent, ydescent, + width, height, fontsize, + trans) + artists[0].set_markersize(self._uniform_size) + return artists From 3f0ae275e92d93746d94e9dfa90b07524967471c Mon Sep 17 00:00:00 2001 From: leeonadoh Date: Sat, 18 Apr 2015 14:40:55 -0400 Subject: [PATCH 2/3] fixing for pep8 --- lib/matplotlib/legend.py | 88 ++++++++++++++------------------ lib/matplotlib/legend_handler.py | 20 +++++--- 2 files changed, 53 insertions(+), 55 deletions(-) diff --git a/lib/matplotlib/legend.py b/lib/matplotlib/legend.py index c978a4da4a53..0e85f7615d2e 100755 --- a/lib/matplotlib/legend.py +++ b/lib/matplotlib/legend.py @@ -803,7 +803,7 @@ def get_title(self): return self._legend_title_box._text def get_window_extent(self, *args, **kwargs): - 'return a extent of the the legend' + 'return a extent of the legend' return self.legendPatch.get_window_extent(*args, **kwargs) def get_frame_on(self): @@ -999,7 +999,7 @@ class UniformLegend(Legend): def __str__(self): return "uniformLegend" - def __init__(self, parent, handles, labels,uniform_size, + def __init__(self, parent, handles, labels, uniform_size, loc=None, numpoints=None, # the number of points in the legend line markerfirst=True, # controls ordering (left-to-right) of @@ -1037,56 +1037,46 @@ def __init__(self, parent, handles, labels,uniform_size, frameon=None ): uniformHandlerMap = { - StemContainer: + StemContainer: legend_handler.HandlerUniformStem(uniform_size=uniform_size), - ErrorbarContainer: + ErrorbarContainer: legend_handler.HandlerUniformErrorBar(uniform_size=uniform_size), - Line2D: legend_handler.HandlerUniformLine2D(uniform_size=uniform_size), - PathCollection: + Line2D: + legend_handler.HandlerUniformLine2D(uniform_size=uniform_size), + PathCollection: legend_handler.HandlerPathCollection(sizes=[uniform_size]*3), - RegularPolyCollection: - legend_handler.HandlerRegularPolyCollection(sizes=[uniform_size]*3), - CircleCollection: + RegularPolyCollection: + legend_handler.HandlerRegularPolyCollection( + sizes=[uniform_size]*3), + CircleCollection: legend_handler.HandlerCircleCollection(sizes=[uniform_size]*3), } - Legend.__init__(self,parent,handles,labels, - loc=loc, - numpoints=numpoints, # the number of points in the legend line - markerscale=None, # the relative size of legend markers - # vs. original - markerfirst=markerfirst, # controls ordering (left-to-right) of - # legend marker and label - scatterpoints=scatterpoints, # number of scatter points - scatteryoffsets=scatteryoffsets, - prop=prop, # properties for the legend texts - fontsize=fontsize, # keyword to set font size directly - - # spacing & pad defined as a fraction of the font-size - borderpad=borderpad, # the whitespace inside the legend border - labelspacing=labelspacing, # the vertical space between the legend - # entries - handlelength=handlelength, # the length of the legend handles - handleheight=handleheight, # the height of the legend handles - handletextpad=handletextpad, # the pad between the legend handle - # and text - borderaxespad=borderaxespad, # the pad between the axes and legend - # border - columnspacing=columnspacing, # spacing between columns - - ncol=1, # number of columns - mode=mode, # mode for horizontal distribution of columns. - # None, "expand" - - fancybox=fancybox, # True use a fancy box, false use a rounded - # box, none use rc - shadow=shadow, - title=title, # set a title for the legend - - framealpha=framealpha, # set frame alpha - - bbox_to_anchor=bbox_to_anchor, # bbox that the legend will be anchored. - bbox_transform=bbox_transform, # transform for the bbox - frameon=frameon, # draw frame - handler_map=uniformHandlerMap, - ) + # Consult Legend.__init__ for purpose of each parameter. + Legend.__init__(self, parent, handles, labels, + loc=loc, + numpoints=numpoints, + markerscale=None, + markerfirst=markerfirst, + scatterpoints=scatterpoints, + scatteryoffsets=scatteryoffsets, + prop=prop, + fontsize=fontsize, + borderpad=borderpad, + labelspacing=labelspacing, + handlelength=handlelength, + handleheight=handleheight, + handletextpad=handletextpad, + borderaxespad=borderaxespad, + columnspacing=columnspacing, + ncol=1, + mode=mode, + fancybox=fancybox, + shadow=shadow, + title=title, + framealpha=framealpha, + bbox_to_anchor=bbox_to_anchor, + bbox_transform=bbox_transform, + frameon=frameon, + handler_map=uniformHandlerMap, + ) diff --git a/lib/matplotlib/legend_handler.py b/lib/matplotlib/legend_handler.py index 296b4d7caf7c..f36de5c808f5 100755 --- a/lib/matplotlib/legend_handler.py +++ b/lib/matplotlib/legend_handler.py @@ -617,6 +617,8 @@ def create_artists(self, legend, orig_handle, self.update_prop(p, orig_handle, legend) p.set_transform(trans) return [p] + + class HandlerUniformLine2D(HandlerLine2D): """ Handler for Uniform sized Line2D instances @@ -630,10 +632,13 @@ def create_artists(self, legend, orig_handle, xdescent, ydescent, width, height, fontsize, trans): artists = HandlerLine2D.create_artists(self, legend, orig_handle, - xdescent, ydescent, width, height, fontsize, - trans) + xdescent, ydescent, + width, height, + fontsize, trans) artists[-1].set_markersize(self._uniform_size) return artists + + class HandlerUniformErrorBar(HandlerErrorbar): """ Handler for Uniform sized Error instances @@ -647,10 +652,13 @@ def create_artists(self, legend, orig_handle, xdescent, ydescent, width, height, fontsize, trans): artists = HandlerErrorbar.create_artists(self, legend, orig_handle, - xdescent, ydescent, width, height, fontsize, - trans) + xdescent, ydescent, + width, height, + fontsize, trans) artists[-1].set_markersize(self._uniform_size) return artists + + class HandlerUniformStem(HandlerStem): def __init__(self, uniform_size, **kw): @@ -662,7 +670,7 @@ def create_artists(self, legend, orig_handle, trans): artists = HandlerStem.create_artists(self, legend, orig_handle, xdescent, ydescent, - width, height, fontsize, - trans) + width, height, + fontsize, trans) artists[0].set_markersize(self._uniform_size) return artists From 455f580e9ab2b8a0f5bc8acdbe4bfb253ceda1e1 Mon Sep 17 00:00:00 2001 From: leeonadoh Date: Sat, 18 Apr 2015 16:37:31 -0400 Subject: [PATCH 3/3] Added support for `uniform_size` Modified `Axes.legend` and `Figure.legend` to support `uniform_size`. Added checks to throw exception on conflicting parameters. `uniform_size` specified with `markerscale` or `handler_map`. --- lib/matplotlib/axes/_axes.py | 14 +++++++++++++- lib/matplotlib/figure.py | 17 ++++++++++++++++- lib/matplotlib/legend.py | 3 +++ 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index ded4595fbfb6..44fb904620c0 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -506,7 +506,19 @@ def legend(self, *args, **kwargs): else: raise TypeError('Invalid arguments to legend.') - self.legend_ = mlegend.Legend(self, handles, labels, **kwargs) + # If uniform_size is specified, verify that there's no conflicting + # parameters. + if 'uniform_size' in kwargs: + if 'markerscale' in kwargs or 'handler_map' in kwargs: + raise TypeError("Cannot specify 'markerscale' or " + + "'handler_map' to legend when " + + "'uniform_size' is specified.") + uniform_size = kwargs.pop('uniform_size') + self.legend_ = mlegend.UniformLegend(self, handles, labels, + uniform_size, **kwargs) + else: + self.legend_ = mlegend.Legend(self, handles, labels, **kwargs) + self.legend_._remove_method = lambda h: setattr(self, 'legend_', None) return self.legend_ diff --git a/lib/matplotlib/figure.py b/lib/matplotlib/figure.py index 78ce8d98e18d..825643e36e4c 100644 --- a/lib/matplotlib/figure.py +++ b/lib/matplotlib/figure.py @@ -40,6 +40,7 @@ from matplotlib.axes import Axes, SubplotBase, subplot_class_factory from matplotlib.blocking_input import BlockingMouseInput, BlockingKeyMouseInput from matplotlib.legend import Legend +from matplotlib.legend import UniformLegend from matplotlib.patches import Rectangle from matplotlib.projections import (get_projection_names, process_projection_requirements) @@ -1222,7 +1223,21 @@ def legend(self, handles, labels, *args, **kwargs): .. plot:: mpl_examples/pylab_examples/figlegend_demo.py """ - l = Legend(self, handles, labels, *args, **kwargs) + + # If uniform_size is specified, verify that there's no conflicting + # parameters. + l = None + if 'uniform_size' in kwargs: + if 'markerscale' in kwargs or 'handler_map' in kwargs: + raise TypeError("Cannot specify 'markerscale' or " + + "'handler_map' to legend when " + + "'uniform_size' is specified.") + uniform_size = kwargs.pop('uniform_size') + l = UniformLegend(self, handles, labels, uniform_size, *args, + **kwargs) + else: + l = Legend(self, handles, labels, *args, **kwargs) + self.legends.append(l) l._remove_method = lambda h: self.legends.remove(h) return l diff --git a/lib/matplotlib/legend.py b/lib/matplotlib/legend.py index 0e85f7615d2e..2140d937f408 100755 --- a/lib/matplotlib/legend.py +++ b/lib/matplotlib/legend.py @@ -1056,6 +1056,7 @@ def __init__(self, parent, handles, labels, uniform_size, Legend.__init__(self, parent, handles, labels, loc=loc, numpoints=numpoints, + # markerscale does nothing when using uniform sizing. markerscale=None, markerfirst=markerfirst, scatterpoints=scatterpoints, @@ -1078,5 +1079,7 @@ def __init__(self, parent, handles, labels, uniform_size, bbox_to_anchor=bbox_to_anchor, bbox_transform=bbox_transform, frameon=frameon, + # Override with custom handler map that forces uniform + # sizing. handler_map=uniformHandlerMap, )