diff --git a/lib/matplotlib/artist.py b/lib/matplotlib/artist.py index 7fb492367f1d..980958fd12c3 100644 --- a/lib/matplotlib/artist.py +++ b/lib/matplotlib/artist.py @@ -138,6 +138,10 @@ def remove(self): # protected attribute if Python supported that sort of thing. The # callback has one parameter, which is the child to be removed. if self._remove_method is not None: + # set the current axes to None + self._axes = None + # use the call back registered by the axes when the artist + # was added to remove it the artist from the axes self._remove_method(self) else: raise NotImplementedError('cannot remove artist') @@ -701,6 +705,9 @@ def get_rasterized(self): "return True if the artist is to be rasterized" return self._rasterized + def set_remove_method(self, f): + self._remove_method = f + def set_rasterized(self, rasterized): """ Force rasterized (bitmap) drawing in vector backend output. diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index fb82fd59b256..76335f7ea75b 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -3,7 +3,7 @@ import six from six.moves import reduce, xrange, zip, zip_longest - +import itertools import math import warnings @@ -13,7 +13,7 @@ import matplotlib import matplotlib.cbook as cbook -from matplotlib.cbook import _string_to_bool, mplDeprecation +from matplotlib.cbook import mplDeprecation import matplotlib.collections as mcoll import matplotlib.colors as mcolors import matplotlib.contour as mcontour @@ -35,7 +35,8 @@ import matplotlib.transforms as mtransforms import matplotlib.tri as mtri import matplotlib.transforms as mtrans -from matplotlib.container import BarContainer, ErrorbarContainer, StemContainer +from matplotlib.container import (BarContainer, ErrorbarContainer, + StemContainer, Container) from matplotlib.axes._base import _AxesBase from matplotlib.axes._base import _process_plot_format @@ -2666,6 +2667,8 @@ def errorbar(self, x, y, yerr=None, xerr=None, label = kwargs.pop("label", None) + zorder = kwargs.pop('zorder', 0) + # make sure all the args are iterable; use lists not arrays to # preserve units if not iterable(x): @@ -2755,7 +2758,7 @@ def xywhere(xs, ys, mask): if xerr is not None: if (iterable(xerr) and len(xerr) == 2 and - iterable(xerr[0]) and iterable(xerr[1])): + iterable(xerr[0]) and iterable(xerr[1])): # using list comps rather than arrays to preserve units left = [thisx - thiserr for (thisx, thiserr) in cbook.safezip(x, xerr[0])] @@ -2805,7 +2808,7 @@ def xywhere(xs, ys, mask): else: marker = mlines.CARETLEFT caplines.extend( - self.plot(leftlo, ylo, ls='None', marker=marker, + self.plot(leftlo, ylo, ls='None', marker=marker, **plot_kw)) if capsize > 0: xup, yup = xywhere(x, y, xuplims & everymask) @@ -2886,14 +2889,19 @@ def xywhere(xs, ys, mask): self.autoscale_view() self._hold = holdstate - errorbar_container = ErrorbarContainer((l0, tuple(caplines), - tuple(barcols)), + # hack to put these artist in the right place in the + # draw tree + for ll in itertools.chain((l0, ), caplines, barcols): + ll.remove() + + errorbar_container = ErrorbarContainer((l0, + Container(caplines), + Container(barcols)), has_xerr=(xerr is not None), has_yerr=(yerr is not None), label=label) - self.containers.append(errorbar_container) - - return errorbar_container # (l0, caplines, barcols) + errorbar_container.set_zorder(zorder) + return self.add_container(errorbar_container) def boxplot(self, x, notch=False, sym=None, vert=True, whis=1.5, positions=None, widths=None, patch_artist=False, diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index e7b9696947c9..fa2b9b86aa00 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -1605,13 +1605,19 @@ def add_container(self, container): Add a :class:`~matplotlib.container.Container` instance to the axes. - Returns the collection. + Returns the container. """ + container.set_axes(self) + self._set_artist_props(container) + self.containers.append(container) + + container.set_clip_path(self.patch) + container.set_remove_method(lambda h: self.containers.remove(h)) + label = container.get_label() if not label: container.set_label('_container%d' % len(self.containers)) - self.containers.append(container) - container.set_remove_method(lambda h: self.containers.remove(h)) + return container def relim(self, visible_only=False): @@ -1998,6 +2004,7 @@ def draw(self, renderer=None, inframe=False): artists.extend(self.lines) artists.extend(self.texts) artists.extend(self.artists) + artists.extend(self.containers) # the frame draws the edges around the axes patch -- we # decouple these so the patch can be in the background and the diff --git a/lib/matplotlib/cbook.py b/lib/matplotlib/cbook.py index c0f66c9f27cd..22e8974a5d2c 100644 --- a/lib/matplotlib/cbook.py +++ b/lib/matplotlib/cbook.py @@ -2311,6 +2311,70 @@ def get_instancemethod(self): return getattr(self.parent_obj, self.instancemethod_name) +_art_list_msg = ("The use of Axes.lines, Axes.patches, Axes.texts " + "Axes.tables, Axes.artists, Axes.collections, " + "Axes.containers, " + "and Axes.images have been " + "deprecated. All artists are now stored is a single " + "tree accessible via the Axes.artist_tree property. " + "Please use the ``remove`` method on the artists to remove" + "them from an Axes. \n\n" + "These lists will be removed in 1.7 or 2.0.") + + +class MPLRemoverList(list): + """ + + This is a sub-class of list which implements the logic to manage the + backwards compatibility during deprecation of the lines, patches, + texts, tables, artists, images, collections, and containers + attributes from the Axes class. This will allow users to continue + to use `ax.lines.pop()` to remove lines from an axes, even though + the draw method no longer looks at those lists at render time. + + This class will be removed when the list are. + + """ + def __delslice__(self, a, b): + # warn + warnings.warn(_art_list_msg, mplDeprecation, stacklevel=1) + # grab what we will be removing + res = self[a:b] + # remove it from this list + super(MPLRemoverList, self).__delslice__(self, a, b) + # see if we need to call the real remove + # Artist.remove sets _axes = None so if this is called + # remove it won't be called again, but if a user removes + # an artist from these lists directly, remove will correctly + # be called. + for a in res: + # this works because of details of how Artist.remove works + if a.axes: + a.remove() + + def __delitem__(self, y): + # see __delslice__ for explanation of logic + warnings.warn(_art_list_msg, mplDeprecation, stacklevel=1) + res = self[y] + super(MPLRemoverList, self).__delitem__(self, y) + if res.axes: + res.remove() + + def pop(self, i): + # see __delslice__ for explanation of logic + warnings.warn(_art_list_msg, mplDeprecation, stacklevel=1) + res = super(MPLRemoverList, self).pop(self, i) + if res.axes: + res.remove() + + def remove(self, item): + # see __delslice__ for explanation of logic + warnings.warn(_art_list_msg, mplDeprecation, stacklevel=1) + res = super(MPLRemoverList, self).remove(self, item) + if item.axes: + res.remove() + + # Numpy > 1.6.x deprecates putmask in favor of the new copyto. # So long as we support versions 1.6.x and less, we need the # following local version of putmask. We choose to make a diff --git a/lib/matplotlib/container.py b/lib/matplotlib/container.py index 4603e7e7bee0..8305798d1eba 100644 --- a/lib/matplotlib/container.py +++ b/lib/matplotlib/container.py @@ -2,14 +2,16 @@ unicode_literals) import six - +from matplotlib.artist import Artist, allow_rasterization import matplotlib.cbook as cbook -class Container(tuple): +class Container(tuple, Artist): """ Base class for containers. """ + _no_broadcast = ['label', 'visible', 'zorder', 'animated', + 'agg_filter'] def __repr__(self): return "" % (len(self)) @@ -17,92 +19,54 @@ def __repr__(self): def __new__(cls, *kl, **kwargs): return tuple.__new__(cls, kl[0]) - def __init__(self, kl, label=None): - - self.eventson = False # fire events only if eventson - self._oid = 0 # an observer id - self._propobservers = {} # a dict from oids to funcs - - self._remove_method = None - + def __init__(self, kl, label=None, **kwargs): + # set up the artist details + Artist.__init__(self, **kwargs) + # for some reason we special case label self.set_label(label) - def set_remove_method(self, f): - self._remove_method = f - def remove(self): + # remove the children for c in self: c.remove() - - if self._remove_method: - self._remove_method(self) - - def __getstate__(self): - d = self.__dict__.copy() - # remove the unpicklable remove method, this will get re-added on load - # (by the axes) if the artist lives on an axes. - d['_remove_method'] = None - return d - - def get_label(self): - """ - Get the label used for this artist in the legend. - """ - return self._label - - def set_label(self, s): - """ - Set the label to *s* for auto legend. - - ACCEPTS: string or anything printable with '%s' conversion. - """ - if s is not None: - self._label = '%s' % (s, ) - else: - self._label = None - self.pchanged() - - def add_callback(self, func): - """ - Adds a callback function that will be called whenever one of - the :class:`Artist`'s properties changes. - - Returns an *id* that is useful for removing the callback with - :meth:`remove_callback` later. - """ - oid = self._oid - self._propobservers[oid] = func - self._oid += 1 - return oid - - def remove_callback(self, oid): - """ - Remove a callback based on its *id*. - - .. seealso:: - - :meth:`add_callback` - For adding callbacks - - """ - try: - del self._propobservers[oid] - except KeyError: - pass - - def pchanged(self): - """ - Fire an event when property changed, calling all of the - registered callbacks. - """ - for oid, func in list(six.iteritems(self._propobservers)): - func(self) + # call up to the Artist remove method + super(Container, self).remove(self) def get_children(self): return list(cbook.flatten(self)) + def __getattribute__(self, key): + + # broadcast set_* and get_* methods across members + # except for these explicitly not. + if (('set' in key or 'get' in key) and + all(k not in key for k in self._no_broadcast)): + + def inner(*args, **kwargs): + return [getattr(a, key)(*args, **kwargs) + for a in self] + inner.__name__ = key + doc = getattr(self[0], key).__doc__ + inner.__doc__ = doc + return inner + else: + return super(Container, self).__getattribute__(key) + + @allow_rasterization + def draw(self, renderer, *args, **kwargs): + # just broadcast the draw down to children + for a in self: + a.draw(renderer, *args, **kwargs) + class BarContainer(Container): + def __new__(cls, patches, errorbar=None, **kwargs): + if errorbar is None: + errorbar = tuple() + else: + errorbar = tuple(errorbar) + patches = tuple(patches) + return super(BarContainer, cls).__new__(patches + errorbar, **kwargs) def __init__(self, patches, errorbar=None, **kwargs): self.patches = patches pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy