From d1bac23215278eeb4375f886efeeacd03d6f3c4b Mon Sep 17 00:00:00 2001 From: Cimarron Mittelsteadt Date: Thu, 11 Sep 2014 10:48:27 -0700 Subject: [PATCH 1/5] ENH: Added new kwarg to legend to control the placement of legend markers and labels. One now has the ability to place the marker to the right of the label. --- lib/matplotlib/axes/_axes.py | 4 ++++ lib/matplotlib/figure.py | 4 ++++ lib/matplotlib/legend.py | 26 +++++++++++++++++++------- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index cbcff3fa2e2d..d8857db20d35 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -360,6 +360,10 @@ def legend(self, *args, **kwargs): drawn ones. Default is ``None`` which will take the value from the ``legend.markerscale`` :data:`rcParam `. + *markerfirst*: [ *True* | *False* ] + if *True*, legend marker is placed to the left of the legend label + if *False*, legend marker is placed to the right of the legend label + frameon : None or bool Control whether a frame should be drawn around the legend. Default is ``None`` which will take the value from the diff --git a/lib/matplotlib/figure.py b/lib/matplotlib/figure.py index af9c34d527d6..e8743ba9b425 100644 --- a/lib/matplotlib/figure.py +++ b/lib/matplotlib/figure.py @@ -1148,6 +1148,10 @@ def legend(self, handles, labels, *args, **kwargs): The relative size of legend markers vs. original. If *None*, use rc settings. + *markerfirst*: [ *True* | *False* ] + if *True*, legend marker is placed to the left of the legend label + if *False*, legend marker is placed to the right of the legend label + *fancybox*: [ *None* | *False* | *True* ] if *True*, draw a frame with a round fancybox. If *None*, use rc diff --git a/lib/matplotlib/legend.py b/lib/matplotlib/legend.py index 00ffe4485ce0..da20a7bfc069 100644 --- a/lib/matplotlib/legend.py +++ b/lib/matplotlib/legend.py @@ -150,6 +150,8 @@ def __init__(self, parent, handles, labels, numpoints=None, # the number of points in the legend line markerscale=None, # the relative size of legend markers # vs. original + 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 @@ -198,6 +200,8 @@ def __init__(self, parent, handles, labels, prop the font property fontsize the font size (used only if prop is not specified) markerscale the relative size of legend markers vs. original + markerfirst If true, place legend marker to left of label + If false, place legend marker to right of label numpoints the number of points in the legend for line scatterpoints the number of points in the legend for scatter plot scatteryoffsets a list of yoffsets for scatter symbols in legend @@ -365,7 +369,7 @@ def __init__(self, parent, handles, labels, self._drawFrame = rcParams["legend.frameon"] # init with null renderer - self._init_legend_box(handles, labels) + self._init_legend_box(handles, labels, markerfirst) if framealpha is None: self.get_frame().set_alpha(rcParams["legend.framealpha"]) @@ -558,7 +562,7 @@ def get_legend_handler(legend_handler_map, orig_handle): return handler - def _init_legend_box(self, handles, labels): + def _init_legend_box(self, handles, labels, markerfirst=True): """ Initialize the legend_box. The legend_box is an instance of the OffsetBox, which is packed with legend handles and @@ -669,16 +673,24 @@ def _init_legend_box(self, handles, labels): # pack handleBox and labelBox into itemBox itemBoxes = [HPacker(pad=0, sep=self.handletextpad * fontsize, - children=[h, t], align="baseline") + children=[h, t] if markerfirst else [t, h], + align="baseline") for h, t in handle_label[i0:i0 + di]] # minimumdescent=False for the text of the last row of the column - itemBoxes[-1].get_children()[1].set_minimumdescent(False) + if markerfirst: + itemBoxes[-1].get_children()[1].set_minimumdescent(False) + else: + itemBoxes[-1].get_children()[0].set_minimumdescent(False) # pack columnBox + if markerfirst: + alignment = "baseline" + else: + alignment = "right" columnbox.append(VPacker(pad=0, - sep=self.labelspacing * fontsize, - align="baseline", - children=itemBoxes)) + sep=self.labelspacing * fontsize, + align=alignment, + children=itemBoxes)) if self._mode == "expand": mode = "expand" From 296acb4207c209d7adfa39eda033ccfa5edbf4cc Mon Sep 17 00:00:00 2001 From: Cimarron Mittelsteadt Date: Thu, 11 Sep 2014 10:49:17 -0700 Subject: [PATCH 2/5] TST: Added test for legend with markers to the right of labels --- .../test_legend/legend_labels_first.png | Bin 0 -> 18875 bytes lib/matplotlib/tests/test_legend.py | 12 ++++++++++++ 2 files changed, 12 insertions(+) create mode 100644 lib/matplotlib/tests/baseline_images/test_legend/legend_labels_first.png diff --git a/lib/matplotlib/tests/baseline_images/test_legend/legend_labels_first.png b/lib/matplotlib/tests/baseline_images/test_legend/legend_labels_first.png new file mode 100644 index 0000000000000000000000000000000000000000..accaee423015764a5c351e1cf129c2122e2ee4b5 GIT binary patch literal 18875 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 literal 0 HcmV?d00001 diff --git a/lib/matplotlib/tests/test_legend.py b/lib/matplotlib/tests/test_legend.py index 6242e1782900..048357410b35 100644 --- a/lib/matplotlib/tests/test_legend.py +++ b/lib/matplotlib/tests/test_legend.py @@ -64,6 +64,18 @@ def test_various_labels(): ax.legend(numpoints=1, loc=0) +@image_comparison(baseline_images=['legend_labels_first'], extensions=['png'], + remove_text=True) +def test_labels_first(): + # test labels to left of markers + fig = plt.figure() + ax = fig.add_subplot(111) + ax.plot(np.arange(10), '-o', label=1) + ax.plot(np.ones(10)*5, ':x', label="x") + ax.plot(np.arange(20, 10, -1), 'd', label="diamond") + ax.legend(loc=0, markerfirst=False) + + @image_comparison(baseline_images=['fancy'], remove_text=True) def test_fancy(): # using subplot triggers some offsetbox functionality untested elsewhere From a024ca5db010a9fa535f1459dfcc869df14a1402 Mon Sep 17 00:00:00 2001 From: Cimarron Mittelsteadt Date: Thu, 11 Sep 2014 11:01:43 -0700 Subject: [PATCH 3/5] STY: pep8 fixes to legend --- lib/matplotlib/axes/_axes.py | 3 ++- lib/matplotlib/figure.py | 3 ++- lib/matplotlib/legend.py | 38 ++++++++++++++++++++---------------- 3 files changed, 25 insertions(+), 19 deletions(-) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index d8857db20d35..482ba62b5226 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -362,7 +362,8 @@ def legend(self, *args, **kwargs): *markerfirst*: [ *True* | *False* ] if *True*, legend marker is placed to the left of the legend label - if *False*, legend marker is placed to the right of the legend label + if *False*, legend marker is placed to the right of the legend + label frameon : None or bool Control whether a frame should be drawn around the legend. diff --git a/lib/matplotlib/figure.py b/lib/matplotlib/figure.py index e8743ba9b425..ee34e9d8b938 100644 --- a/lib/matplotlib/figure.py +++ b/lib/matplotlib/figure.py @@ -1150,7 +1150,8 @@ def legend(self, handles, labels, *args, **kwargs): *markerfirst*: [ *True* | *False* ] if *True*, legend marker is placed to the left of the legend label - if *False*, legend marker is placed to the right of the legend label + if *False*, legend marker is placed to the right of the legend + label *fancybox*: [ *None* | *False* | *True* ] if *True*, draw a frame with a round fancybox. If *None*, use rc diff --git a/lib/matplotlib/legend.py b/lib/matplotlib/legend.py index da20a7bfc069..e5c0fc47a05a 100644 --- a/lib/matplotlib/legend.py +++ b/lib/matplotlib/legend.py @@ -38,8 +38,9 @@ from matplotlib.font_manager import FontProperties from matplotlib.lines import Line2D from matplotlib.patches import Patch, Rectangle, Shadow, FancyBboxPatch -from matplotlib.collections import LineCollection, RegularPolyCollection, \ - CircleCollection, PathCollection, PolyCollection +from matplotlib.collections import (LineCollection, RegularPolyCollection, + CircleCollection, PathCollection, + PolyCollection) from matplotlib.transforms import Bbox, BboxBase, TransformedBbox from matplotlib.transforms import BboxTransformTo, BboxTransformFrom @@ -93,7 +94,8 @@ def _update_loc(self, loc_in_canvas): _bbox_transform = BboxTransformFrom(bbox) self.legend._loc = tuple( - _bbox_transform.transform_point(loc_in_canvas)) + _bbox_transform.transform_point(loc_in_canvas) + ) def _update_bbox_to_anchor(self, loc_in_canvas): @@ -178,7 +180,7 @@ def __init__(self, parent, handles, labels, shadow=None, title=None, # set a title for the legend - framealpha=None, # set frame alpha + framealpha=None, # set frame alpha bbox_to_anchor=None, # bbox that the legend will be anchored. bbox_transform=None, # transform for the bbox @@ -320,13 +322,15 @@ def __init__(self, parent, handles, labels, if self.isaxes: warnings.warn('Unrecognized location "%s". Falling back ' 'on "best"; valid locations are\n\t%s\n' - % (loc, '\n\t'.join(six.iterkeys(self.codes)))) + % (loc, '\n\t'.join( + six.iterkeys(self.codes)))) loc = 0 else: warnings.warn('Unrecognized location "%s". Falling back ' 'on "upper right"; ' 'valid locations are\n\t%s\n' - % (loc, '\n\t'.join(six.iterkeys(self.codes)))) + % (loc, '\n\t'.join( + six.iterkeys(self.codes)))) loc = 1 else: loc = self.codes[loc] @@ -400,8 +404,8 @@ def _set_loc(self, loc): else: _findoffset = self._findoffset_loc - #def findoffset(width, height, xdescent, ydescent): - # return _findoffset(width, height, xdescent, ydescent, renderer) +# def findoffset(width, height, xdescent, ydescent): +# return _findoffset(width, height, xdescent, ydescent, renderer) self._legend_box.set_offset(_findoffset) @@ -489,7 +493,7 @@ def _approx_text_height(self, renderer=None): RegularPolyCollection: legend_handler.HandlerRegularPolyCollection(), CircleCollection: legend_handler.HandlerCircleCollection(), BarContainer: legend_handler.HandlerPatch( - update_func=legend_handler.update_from_first_child), + update_func=legend_handler.update_from_first_child), tuple: legend_handler.HandlerTuple(), PathCollection: legend_handler.HandlerPathCollection(), PolyCollection: legend_handler.HandlerPolyCollection() @@ -610,10 +614,11 @@ def _init_legend_box(self, handles, labels, markerfirst=True): handler = self.get_legend_handler(legend_handler_map, orig_handle) if handler is None: warnings.warn( - "Legend does not support {!r} instances.\nA proxy artist " - "may be used instead.\nSee: " - "http://matplotlib.org/users/legend_guide.html" - "#using-proxy-artist".format(orig_handle)) + "Legend does not support {!r} instances.\nA proxy artist " + "may be used instead.\nSee: " + "http://matplotlib.org/users/legend_guide.html" + "#using-proxy-artist".format(orig_handle) + ) # We don't have a handle for this artist, so we just defer # to None. handle_list.append(None) @@ -658,11 +663,10 @@ def _init_legend_box(self, handles, labels, markerfirst=True): # starting index of each column and number of rows in it. largecol = safezip(list(xrange(0, num_largecol * (nrows + 1), - (nrows + 1))), + (nrows + 1))), [nrows + 1] * num_largecol) smallcol = safezip(list(xrange(num_largecol * (nrows + 1), - len(handleboxes), - nrows)), + len(handleboxes), nrows)), [nrows] * num_smallcol) else: largecol, smallcol = [], [] @@ -920,7 +924,7 @@ def _find_best_position(self, width, height, renderer, consider=None): renderer) for x in range(1, len(self.codes))] - #tx, ty = self.legendPatch.get_x(), self.legendPatch.get_y() +# tx, ty = self.legendPatch.get_x(), self.legendPatch.get_y() candidates = [] for l, b in consider: From 1d28d605e6bace3ffde809ce7648298375641de5 Mon Sep 17 00:00:00 2001 From: Cimarron Mittelsteadt Date: Thu, 11 Sep 2014 17:45:14 -0700 Subject: [PATCH 4/5] TST: Removed legend.py from excluded pep8 testing since it now passes --- lib/matplotlib/tests/test_coding_standards.py | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/matplotlib/tests/test_coding_standards.py b/lib/matplotlib/tests/test_coding_standards.py index 5a0c35331a25..ea372a8f0652 100644 --- a/lib/matplotlib/tests/test_coding_standards.py +++ b/lib/matplotlib/tests/test_coding_standards.py @@ -178,7 +178,6 @@ def test_pep8_conformance_installed_files(): 'font_manager.py', 'fontconfig_pattern.py', 'gridspec.py', - 'legend.py', 'legend_handler.py', 'mathtext.py', 'patheffects.py', From d7210ffc4276cba7c85e81f08d8d6d23aea1c298 Mon Sep 17 00:00:00 2001 From: Cimarron Mittelsteadt Date: Sat, 18 Oct 2014 10:39:48 -0700 Subject: [PATCH 5/5] DOC: Added some text in whats_new for the marker label placement functionality --- doc/users/whats_new.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/doc/users/whats_new.rst b/doc/users/whats_new.rst index 3590d725ff51..bd09d89be67d 100644 --- a/doc/users/whats_new.rst +++ b/doc/users/whats_new.rst @@ -29,6 +29,12 @@ revision, see the :ref:`github-stats`. new in matplotlib-1.5 ===================== +Legend +------ +Added ability to place the label before the marker in a legend box with +``markerfirst`` keyword + + Widgets -------