diff --git a/examples/text_labels_and_annotations/legend.py b/examples/text_labels_and_annotations/legend.py index 7e6162a51c25..03d2fbf35b9b 100644 --- a/examples/text_labels_and_annotations/legend.py +++ b/examples/text_labels_and_annotations/legend.py @@ -21,8 +21,17 @@ ax.plot(a, d, 'k:', label='Data length') ax.plot(a, c + d, 'k', label='Total message length') +#Creates an arrow with pre-defined label. +ax.annotate("", + xy=(1.5, 4.5), xytext=(1.5, 9.0), + arrowprops={'arrowstyle': '<->', 'color': 'C7'}, label='distance') + + legend = ax.legend(loc='upper center', shadow=True, fontsize='x-large') +ax.annotate("", xy=(1.5, 4.5), xytext=(1.5, 9.0), + arrowprops={'arrowstyle': '<->', 'color': 'C7'}, label='distance') + # Put a nicer background color on the legend. legend.get_frame().set_facecolor('C0') diff --git a/lib/matplotlib/legend.py b/lib/matplotlib/legend.py index 4553a605c67e..38311f6ed254 100644 --- a/lib/matplotlib/legend.py +++ b/lib/matplotlib/legend.py @@ -34,7 +34,7 @@ from matplotlib.font_manager import FontProperties from matplotlib.lines import Line2D from matplotlib.patches import (Patch, Rectangle, Shadow, FancyBboxPatch, - StepPatch) + FancyArrowPatch, StepPatch) from matplotlib.collections import (LineCollection, RegularPolyCollection, CircleCollection, PathCollection, PolyCollection) @@ -45,6 +45,7 @@ from matplotlib.offsetbox import DraggableOffsetBox from matplotlib.container import ErrorbarContainer, BarContainer, StemContainer +from matplotlib.text import Annotation from . import legend_handler @@ -632,8 +633,10 @@ def draw(self, renderer): update_func=legend_handler.update_from_first_child), tuple: legend_handler.HandlerTuple(), PathCollection: legend_handler.HandlerPathCollection(), - PolyCollection: legend_handler.HandlerPolyCollection() - } + PolyCollection: legend_handler.HandlerPolyCollection(), + FancyArrowPatch: legend_handler.HandlerFancyArrowPatch(), + Annotation: legend_handler.HandlerAnnotation() + } # (get|set|update)_default_handler_maps are public interfaces to # modify the default handler map. @@ -809,6 +812,7 @@ def _init_legend_box(self, handles, labels, markerfirst=True): children=[self._legend_title_box, self._legend_handle_box]) self._legend_box.set_figure(self.figure) + self._legend_box.set_offset(self._findoffset) self.texts = text_list self.legendHandles = handle_list @@ -1115,12 +1119,14 @@ def _get_legend_handles(axs, legend_handler_map=None): handles_original = [] for ax in axs: handles_original += (ax.lines + ax.patches + - ax.collections + ax.containers) + ax.collections + ax.containers + ax.texts) + # support parasite axes: if hasattr(ax, 'parasites'): for axx in ax.parasites: handles_original += (axx.lines + axx.patches + - axx.collections + axx.containers) + axx.collections + axx.containers + + axx.texts) handler_map = Legend.get_default_handler_map() diff --git a/lib/matplotlib/legend_handler.py b/lib/matplotlib/legend_handler.py index 45fb759d7b23..e64929bfa3be 100644 --- a/lib/matplotlib/legend_handler.py +++ b/lib/matplotlib/legend_handler.py @@ -29,7 +29,7 @@ def legend_artist(self, legend, orig_handle, fontsize, handlebox) from matplotlib import cbook from matplotlib.lines import Line2D -from matplotlib.patches import Rectangle +from matplotlib.patches import Rectangle, FancyArrowPatch import matplotlib.collections as mcoll import matplotlib.colors as mcolors @@ -768,3 +768,56 @@ def create_artists(self, legend, orig_handle, self.update_prop(p, orig_handle, legend) p.set_transform(trans) return [p] + + +class HandlerFancyArrowPatch(HandlerPatch): + """ + Handler for FancyArrowPatch instances. + """ + def _create_patch(self, legend, orig_handle, xdescent, ydescent, width, + height, fontsize): + arrow = FancyArrowPatch([-xdescent, + -ydescent + height / 2], + [-xdescent + width, + -ydescent + height / 2], + mutation_scale=width / 3) + arrow.set_arrowstyle(orig_handle.get_arrowstyle()) + return arrow + + +class HandlerAnnotation(HandlerBase): + """ + Handler for Annotation instances. + Defers to HandlerFancyArrowPatch to draw the annotation arrow (if any). + Parameters + ---------- + pad : float, optional + If None, fall back to `legend.borderpad` asstr the default. + In units of fraction of font size. + Default is None. + width_ratios : tuple, optional + The relative width of the respective text/arrow legend annotation pair. + Must be of length 2. + Default is [1,4]. + """ + def __init__(self, pad=None, width_ratios=[1, 4], **kwargs): + + self._pad = pad + self._width_ratios = width_ratios + + HandlerBase.__init__(self, **kwargs) + + def create_artists(self, legend, orig_handle, xdescent, + ydescent, width, height, fontsize, + trans): + + if orig_handle.arrow_patch is not None: + + # Arrow without text + + handler = HandlerFancyArrowPatch() + handle = orig_handle.arrow_patch + + return handler.create_artists(legend, handle, xdescent, + ydescent, width, height, + fontsize, trans)