Example #1
0
    def _update_positions(self, renderer):
        # called from renderer to allow more precise estimates of
        # widths and heights with get_window_extent

        if not len(self.legendHandles) and not len(self.texts): return

        def get_tbounds(text):  #get text bounds in axes coords
            bbox = text.get_window_extent(renderer)
            bboxa = bbox.inverse_transformed(self.get_transform())
            return bboxa.bounds

        hpos = []
        for t, tabove in safezip(self.texts[1:], self.texts[:-1]):
            x, y = t.get_position()
            l, b, w, h = get_tbounds(tabove)
            b -= self.labelsep
            h += 2 * self.labelsep
            hpos.append((b, h))
            t.set_position((x, b - 0.1 * h))

        # now do the same for last line

        l, b, w, h = get_tbounds(self.texts[-1])
        b -= self.labelsep
        h += 2 * self.labelsep
        hpos.append((b, h))

        for handle, tup in safezip(self.legendHandles, hpos):
            y, h = tup
            if isinstance(handle, Line2D):
                ydata = y * np.ones(handle.get_xdata().shape, float)
                handle.set_ydata(ydata + h / 2.)
                handle._legmarker.set_ydata(ydata + h / 2.)
            elif isinstance(handle, Rectangle):
                handle.set_y(y + 1 / 4 * h)
                handle.set_height(h / 2)

        # Set the data for the legend patch
        bbox = self._get_handle_text_bbox(renderer)

        bbox = bbox.expanded(1 + self.pad, 1 + self.pad)
        l, b, w, h = bbox.bounds
        self.legendPatch.set_bounds(l, b, w, h)

        ox, oy = 0, 0  # center

        if iterable(self._loc) and len(self._loc) == 2:
            xo = self.legendPatch.get_x()
            yo = self.legendPatch.get_y()
            x, y = self._loc
            ox, oy = x - xo, y - yo
        elif self._loc == 0:  # "best"
            ox, oy = self._find_best_position(w, h)
        else:
            x, y = self._loc_to_axes_coords(self._loc, w, h)
            ox, oy = x - l, y - b

        self._offset(ox, oy)
Example #2
0
    def _update_positions(self, renderer):
        # called from renderer to allow more precise estimates of
        # widths and heights with get_window_extent

        if not len(self.legendHandles) and not len(self.texts): return
        def get_tbounds(text):  #get text bounds in axes coords
            bbox = text.get_window_extent(renderer)
            bboxa = bbox.inverse_transformed(self.get_transform())
            return bboxa.bounds

        hpos = []
        for t, tabove in safezip(self.texts[1:], self.texts[:-1]):
            x,y = t.get_position()
            l,b,w,h = get_tbounds(tabove)
            b -= self.labelsep
            h += 2*self.labelsep
            hpos.append( (b,h) )
            t.set_position( (x, b-0.1*h) )

        # now do the same for last line

        l,b,w,h = get_tbounds(self.texts[-1])
        b -= self.labelsep
        h += 2*self.labelsep
        hpos.append( (b,h) )

        for handle, tup in safezip(self.legendHandles, hpos):
            y,h = tup
            if isinstance(handle, Line2D):
                ydata = y*np.ones(handle.get_xdata().shape, float)
                handle.set_ydata(ydata+h/2.)
                handle._legmarker.set_ydata(ydata+h/2.)
            elif isinstance(handle, Rectangle):
                handle.set_y(y+1/4*h)
                handle.set_height(h/2)

        # Set the data for the legend patch
        bbox = self._get_handle_text_bbox(renderer)

        bbox = bbox.expanded(1 + self.pad, 1 + self.pad)
        l, b, w, h = bbox.bounds
        self.legendPatch.set_bounds(l, b, w, h)

        ox, oy = 0, 0                           # center

        if iterable(self._loc) and len(self._loc)==2:
            xo = self.legendPatch.get_x()
            yo = self.legendPatch.get_y()
            x, y = self._loc
            ox, oy = x-xo, y-yo
        elif self._loc == 0:  # "best"
            ox, oy = self._find_best_position(w, h)
        else:
            x, y = self._loc_to_axes_coords(self._loc, w, h)
            ox, oy = x-l, y-b

        self._offset(ox, oy)
Example #3
0
    def __init__(self, parent, handles, labels,
                 loc = None,
                 numpoints = None,     # the number of points in the legend line
                 prop = None,
                 pad = None,           # the fractional whitespace inside the legend border
                 markerscale = None,   # the relative size of legend markers vs. original
                 # the following dimensions are in axes coords
                 labelsep = None,      # the vertical space between the legend entries
                 handlelen = None,     # the length of the legend lines
                 handletextsep = None, # the space between the legend line and legend text
                 axespad = None,       # the border between the axes and legend edge

                 shadow = None
                 ):
        """
  parent                # the artist that contains the legend
  handles               # a list of artists (lines, patches) to add to the legend
  labels                # a list of strings to label the legend
  loc                   # a location code
  numpoints = 4         # the number of points in the legend line
  prop = FontProperties(size='smaller')  # the font property
  pad = 0.2             # the fractional whitespace inside the legend border
  markerscale = 0.6     # the relative size of legend markers vs. original
  shadow                # if True, draw a shadow behind legend

The following dimensions are in axes coords
  labelsep = 0.005     # the vertical space between the legend entries
  handlelen = 0.05     # the length of the legend lines
  handletextsep = 0.02 # the space between the legend line and legend text
  axespad = 0.02       # the border between the axes and legend edge
        """
        from axes import Axes     # local import only to avoid circularity
        from figure import Figure # local import only to avoid circularity

        Artist.__init__(self)

        proplist=[numpoints, pad, markerscale, labelsep, handlelen, handletextsep, axespad, shadow]
        propnames=['numpoints', 'pad', 'markerscale', 'labelsep', 'handlelen', 'handletextsep', 'axespad', 'shadow']
        for name, value in safezip(propnames,proplist):
            if value is None:
                value=rcParams["legend."+name]
            setattr(self,name,value)
        if self.numpoints <= 0:
            raise ValueError("numpoints must be >= 0; it was %d"% numpoints)
        if prop is None:
            self.prop=FontProperties(size=rcParams["legend.fontsize"])
        else:
            self.prop=prop
        self.fontsize = self.prop.get_size_in_points()

        if isinstance(parent,Axes):
            self.isaxes = True
            self.set_figure(parent.figure)
        elif isinstance(parent,Figure):
            self.isaxes = False
            self.set_figure(parent)
        else:
            raise TypeError("Legend needs either Axes or Figure as parent")
        self.parent = parent
        self._offsetTransform = Affine2D()
        self._parentTransform = BboxTransformTo(parent.bbox)
        Artist.set_transform(self, self._offsetTransform + self._parentTransform)

        if loc is None:
            loc = rcParams["legend.loc"]
            if not self.isaxes and loc in [0,'best']:
                loc = 'upper right'
        if is_string_like(loc):
            if not self.codes.has_key(loc):
                if self.isaxes:
                    warnings.warn('Unrecognized location "%s". Falling back on "best"; '
                                  'valid locations are\n\t%s\n'
                                  % (loc, '\n\t'.join(self.codes.keys())))
                    loc = 0
                else:
                    warnings.warn('Unrecognized location "%s". Falling back on "upper right"; '
                                  'valid locations are\n\t%s\n'
                                   % (loc, '\n\t'.join(self.codes.keys())))
                    loc = 1
            else:
                loc = self.codes[loc]
        if not self.isaxes and loc == 0:
            warnings.warn('Automatic legend placement (loc="best") not implemented for figure legend. '
                          'Falling back on "upper right".')
            loc = 1

        self._loc = loc

        self.legendPatch = Rectangle(
            xy=(0.0, 0.0), width=0.5, height=0.5,
            facecolor='w', edgecolor='k',
            )
        self._set_artist_props(self.legendPatch)

        # make a trial box in the middle of the axes.  relocate it
        # based on it's bbox
        left, top = 0.5, 0.5
        textleft = left+ self.handlelen+self.handletextsep
        self.texts = self._get_texts(labels, textleft, top)
        self.legendHandles = self._get_handles(handles, self.texts)

        self._drawFrame = True
Example #4
0
    def _get_handles(self, handles, texts):
        handles = list(handles)
        texts = list(texts)
        HEIGHT = self._approx_text_height()
        left = 0.5

        ret = []   # the returned legend lines

        # we need to pad the text with empties for the numpoints=1
        # centered marker proxy

        for handle, label in safezip(handles, texts):
            if self.numpoints > 1:
                xdata = np.linspace(left, left + self.handlelen, self.numpoints)
                xdata_marker = xdata
            elif self.numpoints == 1:
                xdata = np.linspace(left, left + self.handlelen, 2)
                xdata_marker = [left + 0.5*self.handlelen]

            x, y = label.get_position()
            x -= self.handlelen + self.handletextsep
            if isinstance(handle, Line2D):
                ydata = (y-HEIGHT/2)*np.ones(xdata.shape, float)
                legline = Line2D(xdata, ydata)

                legline.update_from(handle)
                self._set_artist_props(legline) # after update
                legline.set_clip_box(None)
                legline.set_clip_path(None)
                ret.append(legline)
                legline.set_marker('None')

                legline_marker = Line2D(xdata_marker, ydata[:len(xdata_marker)])
                legline_marker.update_from(handle)
                legline_marker.set_linestyle('None')
                self._set_artist_props(legline_marker)
                # we don't want to add this to the return list because
                # the texts and handles are assumed to be in one-to-one
                # correpondence.
                legline._legmarker = legline_marker

            elif isinstance(handle, Patch):
                p = Rectangle(xy=(min(xdata), y-3/4*HEIGHT),
                              width = self.handlelen, height=HEIGHT/2,
                              )
                p.update_from(handle)
                self._set_artist_props(p)
                p.set_clip_box(None)
                p.set_clip_path(None)
                ret.append(p)
            elif isinstance(handle, LineCollection):
                ydata = (y-HEIGHT/2)*np.ones(xdata.shape, float)
                legline = Line2D(xdata, ydata)
                self._set_artist_props(legline)
                legline.set_clip_box(None)
                legline.set_clip_path(None)
                lw = handle.get_linewidth()[0]
                dashes = handle.get_dashes()[0]
                color = handle.get_colors()[0]
                legline.set_color(color)
                legline.set_linewidth(lw)
                legline.set_dashes(dashes)
                ret.append(legline)

            elif isinstance(handle, RegularPolyCollection):
                if self.numpoints == 1:
                    xdata = np.array([left])
                p = Rectangle(xy=(min(xdata), y-3/4*HEIGHT),
                              width = self.handlelen, height=HEIGHT/2,
                              )
                p.set_facecolor(handle._facecolors[0])
                if handle._edgecolors != 'none' and len(handle._edgecolors):
                    p.set_edgecolor(handle._edgecolors[0])
                self._set_artist_props(p)
                p.set_clip_box(None)
                p.set_clip_path(None)
                ret.append(p)

            else:
                ret.append(None)

        return ret