From 525dcef84a4da2383230d49a0689c66ee49aa3b6 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Sat, 14 Oct 2017 14:55:07 -0700 Subject: [PATCH 1/2] Deprecate Tick.{gridOn,tick1On,label1On,...} in favor of set_visible. Visibility attributes should just be set on the underlying artists (`tick.gridline.set_visible`, etc.) rather than maintaining yet another layer of control. --- doc/api/api_changes/171014-AL-tickprops.rst | 13 + examples/specialty_plots/skewt.py | 82 ++--- lib/matplotlib/axis.py | 330 +++++++++----------- lib/matplotlib/backend_bases.py | 4 +- lib/matplotlib/backend_tools.py | 4 +- lib/matplotlib/projections/polar.py | 137 ++++---- lib/matplotlib/tests/test_skew.py | 82 ++--- lib/mpl_toolkits/mplot3d/axis3d.py | 10 +- 8 files changed, 264 insertions(+), 398 deletions(-) create mode 100644 doc/api/api_changes/171014-AL-tickprops.rst diff --git a/doc/api/api_changes/171014-AL-tickprops.rst b/doc/api/api_changes/171014-AL-tickprops.rst new file mode 100644 index 000000000000..f8aab393e6d8 --- /dev/null +++ b/doc/api/api_changes/171014-AL-tickprops.rst @@ -0,0 +1,13 @@ +Deprecation of redundant `Tick` attributes +`````````````````````````````````````````` + +The ``gridOn``, ``tick1On``, ``tick2On``, ``label1On``, and ``label2On`` +`~.Tick` attributes have been deprecated. Directly get and set the visibility +on the underlying artists, available as the ``gridline``, ``tick1line``, +``tick2line``, ``label1``, and ``label2`` attributes. + +The ``label`` attribute, which was an alias for ``label1``, has been +deprecated. + +Subclasses that relied on setting the above visibility attributes needs to be +updated; see e.g. :file:`examples/api/skewt.py`. diff --git a/examples/specialty_plots/skewt.py b/examples/specialty_plots/skewt.py index 5faf0572625f..bf4662123e6d 100644 --- a/examples/specialty_plots/skewt.py +++ b/examples/specialty_plots/skewt.py @@ -10,10 +10,12 @@ axes that are not orthogonal. This is handled by including a skew component to the basic Axes transforms. Additional complexity comes in handling the fact that the upper and lower X-axes have different data ranges, which necessitates -a bunch of custom classes for ticks,spines, and the axis to handle this. +a bunch of custom classes for ticks, spines, and axis to handle this. """ +from contextlib import ExitStack + from matplotlib.axes import Axes import matplotlib.transforms as transforms import matplotlib.axis as maxis @@ -24,66 +26,24 @@ # The sole purpose of this class is to look at the upper, lower, or total # interval as appropriate and see what parts of the tick to draw, if any. class SkewXTick(maxis.XTick): - def update_position(self, loc): - # This ensures that the new value of the location is set before - # any other updates take place - self._loc = loc - super().update_position(loc) - - def _has_default_loc(self): - return self.get_loc() is None - - def _need_lower(self): - return (self._has_default_loc() or - transforms.interval_contains(self.axes.lower_xlim, - self.get_loc())) - - def _need_upper(self): - return (self._has_default_loc() or - transforms.interval_contains(self.axes.upper_xlim, - self.get_loc())) - - @property - def gridOn(self): - return (self._gridOn and (self._has_default_loc() or - transforms.interval_contains(self.get_view_interval(), - self.get_loc()))) - - @gridOn.setter - def gridOn(self, value): - self._gridOn = value - - @property - def tick1On(self): - return self._tick1On and self._need_lower() - - @tick1On.setter - def tick1On(self, value): - self._tick1On = value - - @property - def label1On(self): - return self._label1On and self._need_lower() - - @label1On.setter - def label1On(self, value): - self._label1On = value - - @property - def tick2On(self): - return self._tick2On and self._need_upper() - - @tick2On.setter - def tick2On(self, value): - self._tick2On = value - - @property - def label2On(self): - return self._label2On and self._need_upper() - - @label2On.setter - def label2On(self, value): - self._label2On = value + def draw(self, renderer): + with ExitStack() as stack: + for artist in [self.gridline, self.tick1line, self.tick2line, + self.label1, self.label2]: + stack.callback(artist.set_visible, artist.get_visible()) + needs_lower = transforms.interval_contains( + self.axes.lower_xlim, self.get_loc()) + needs_upper = transforms.interval_contains( + self.axes.upper_xlim, self.get_loc()) + self.tick1line.set_visible( + self.tick1line.get_visible() and needs_lower) + self.label1.set_visible( + self.label1.get_visible() and needs_lower) + self.tick2line.set_visible( + self.tick2line.get_visible() and needs_upper) + self.label2.set_visible( + self.label2.get_visible() and needs_upper) + super(SkewXTick, self).draw(renderer) def get_view_interval(self): return self.axes.xaxis.get_view_interval() diff --git a/lib/matplotlib/axis.py b/lib/matplotlib/axis.py index 663d16468094..4010aaa055e0 100644 --- a/lib/matplotlib/axis.py +++ b/lib/matplotlib/axis.py @@ -52,21 +52,6 @@ class Tick(artist.Artist): label1 : Text label2 : Text - - gridOn : bool - Determines whether to draw the tickline. - - tick1On : bool - Determines whether to draw the first tickline. - - tick2On : bool - Determines whether to draw the second tickline. - - label1On : bool - Determines whether to draw the first tick label. - - label2On : bool - Determines whether to draw the second tick label. """ def __init__(self, axes, loc, label, size=None, # points @@ -173,19 +158,43 @@ def __init__(self, axes, loc, label, self.tick1line = self._get_tick1line() self.tick2line = self._get_tick2line() self.gridline = self._get_gridline() - self.label1 = self._get_text1() - self.label = self.label1 # legacy name self.label2 = self._get_text2() - self.gridOn = gridOn - self.tick1On = tick1On - self.tick2On = tick2On - self.label1On = label1On - self.label2On = label2On + self.gridline.set_visible(gridOn) + self.tick1line.set_visible(tick1On) + self.tick2line.set_visible(tick2On) + self.label1.set_visible(label1On) + self.label2.set_visible(label2On) self.update_position(loc) + for _old_name, _new_name in [ + ("gridOn", "gridline"), + ("tick1On", "tick1line"), + ("tick2On", "tick2line"), + ("label1On", "label1"), + ("label2On", "label2")]: + locals()[_old_name] = property( + cbook.deprecated( + "3.1", + name=_old_name, + alternative="Tick.{}.get_visible".format(_new_name))( + lambda self, _new_name=_new_name: + getattr(self, _new_name).get_visible()), + cbook.deprecated( + "3.1", + name=_old_name, + alternative="Tick.{}.set_visible".format(_new_name))( + lambda self, value, _new_name=_new_name: + getattr(self, _new_name).set_visible(value))) + del _old_name, _new_name + + @property + @cbook.deprecated("3.1", alternative="Tick.label1") + def label(self): + return self.label1 + def _set_labelrotation(self, labelrotation): if isinstance(labelrotation, str): mode = labelrotation @@ -201,18 +210,13 @@ def _set_labelrotation(self, labelrotation): self._labelrotation = (mode, angle) def apply_tickdir(self, tickdir): - """ - Calculate self._pad and self._tickmarkers - """ - pass + """Calculate self._pad and self._tickmarkers.""" def get_tickdir(self): return self._tickdir def get_tick_padding(self): - """ - Get the length of the tick outside of the axes. - """ + """Get the length of the tick outside of the axes.""" padding = { 'in': 0.0, 'inout': 0.5, @@ -290,21 +294,11 @@ def draw(self, renderer): if not self.get_visible(): self.stale = False return - renderer.open_group(self.__name__) - if self.gridOn: - self.gridline.draw(renderer) - if self.tick1On: - self.tick1line.draw(renderer) - if self.tick2On: - self.tick2line.draw(renderer) - - if self.label1On: - self.label1.draw(renderer) - if self.label2On: - self.label2.draw(renderer) + for artist in [self.gridline, self.tick1line, self.tick2line, + self.label1, self.label2]: + artist.draw(renderer) renderer.close_group(self.__name__) - self.stale = False def set_label1(self, s): @@ -339,9 +333,13 @@ def get_view_interval(self): raise NotImplementedError('Derived must override') def _apply_params(self, **kw): - for name in ['gridOn', 'tick1On', 'tick2On', 'label1On', 'label2On']: + for name, target in [("gridOn", self.gridline), + ("tick1On", self.tick1line), + ("tick2On", self.tick2line), + ("label1On", self.label1), + ("label2On", self.label2)]: if name in kw: - setattr(self, name, kw.pop(name)) + target.set_visible(kw.pop(name)) if any(k in kw for k in ['size', 'width', 'pad', 'tickdir']): self._size = kw.pop('size', self._size) # Width could be handled outside this block, but it is @@ -503,23 +501,17 @@ def _get_gridline(self): return l def update_position(self, loc): - 'Set the location of tick in data coords with scalar *loc*' - if self.tick1On: - self.tick1line.set_xdata((loc,)) - if self.tick2On: - self.tick2line.set_xdata((loc,)) - if self.gridOn: - self.gridline.set_xdata((loc,)) - if self.label1On: - self.label1.set_x(loc) - if self.label2On: - self.label2.set_x(loc) - + """Set the location of tick in data coords with scalar *loc*.""" + self.tick1line.set_xdata((loc,)) + self.tick2line.set_xdata((loc,)) + self.gridline.set_xdata((loc,)) + self.label1.set_x(loc) + self.label2.set_x(loc) self._loc = loc self.stale = True def get_view_interval(self): - 'return the Interval instance for this axis view limits' + """Return the Interval instance for this axis view limits.""" return self.axes.viewLim.intervalx @@ -626,23 +618,17 @@ def _get_gridline(self): return l def update_position(self, loc): - 'Set the location of tick in data coords with scalar *loc*' - if self.tick1On: - self.tick1line.set_ydata((loc,)) - if self.tick2On: - self.tick2line.set_ydata((loc,)) - if self.gridOn: - self.gridline.set_ydata((loc,)) - if self.label1On: - self.label1.set_y(loc) - if self.label2On: - self.label2.set_y(loc) - + """Set the location of tick in data coords with scalar *loc*.""" + self.tick1line.set_ydata((loc,)) + self.tick2line.set_ydata((loc,)) + self.gridline.set_ydata((loc,)) + self.label1.set_y(loc) + self.label2.set_y(loc) self._loc = loc self.stale = True def get_view_interval(self): - 'return the Interval instance for this axis view limits' + """Return the Interval instance for this axis view limits.""" return self.axes.viewLim.intervaly @@ -847,7 +833,7 @@ def set_tick_params(self, which='major', reset=False, **kw): dicts.append(self._major_tick_kw) if which == 'minor' or which == 'both': dicts.append(self._minor_tick_kw) - kwtrans = self._translate_tick_kw(kw, to_init_kw=True) + kwtrans = self._translate_tick_kw(kw) for d in dicts: if reset: d.clear() @@ -867,55 +853,49 @@ def set_tick_params(self, which='major', reset=False, **kw): self.stale = True @staticmethod - def _translate_tick_kw(kw, to_init_kw=True): + def _translate_tick_kw(kw): # The following lists may be moved to a more # accessible location. - kwkeys0 = ['size', 'width', 'color', 'tickdir', 'pad', - 'labelsize', 'labelcolor', 'zorder', 'gridOn', - 'tick1On', 'tick2On', 'label1On', 'label2On'] - kwkeys1 = ['length', 'direction', 'left', 'bottom', 'right', 'top', - 'labelleft', 'labelbottom', 'labelright', 'labeltop', - 'labelrotation'] - kwkeys2 = _gridline_param_names - kwkeys = kwkeys0 + kwkeys1 + kwkeys2 - kwtrans = dict() - if to_init_kw: - if 'length' in kw: - kwtrans['size'] = kw.pop('length') - if 'direction' in kw: - kwtrans['tickdir'] = kw.pop('direction') - if 'rotation' in kw: - kwtrans['labelrotation'] = kw.pop('rotation') - if 'left' in kw: - kwtrans['tick1On'] = _string_to_bool(kw.pop('left')) - if 'bottom' in kw: - kwtrans['tick1On'] = _string_to_bool(kw.pop('bottom')) - if 'right' in kw: - kwtrans['tick2On'] = _string_to_bool(kw.pop('right')) - if 'top' in kw: - kwtrans['tick2On'] = _string_to_bool(kw.pop('top')) - - if 'labelleft' in kw: - kwtrans['label1On'] = _string_to_bool(kw.pop('labelleft')) - if 'labelbottom' in kw: - kwtrans['label1On'] = _string_to_bool(kw.pop('labelbottom')) - if 'labelright' in kw: - kwtrans['label2On'] = _string_to_bool(kw.pop('labelright')) - if 'labeltop' in kw: - kwtrans['label2On'] = _string_to_bool(kw.pop('labeltop')) - if 'colors' in kw: - c = kw.pop('colors') - kwtrans['color'] = c - kwtrans['labelcolor'] = c - # Maybe move the checking up to the caller of this method. - for key in kw: - if key not in kwkeys: - raise ValueError( - "keyword %s is not recognized; valid keywords are %s" - % (key, kwkeys)) + kwkeys = ['size', 'width', 'color', 'tickdir', 'pad', + 'labelsize', 'labelcolor', 'zorder', 'gridOn', + 'tick1On', 'tick2On', 'label1On', 'label2On', + 'length', 'direction', 'left', 'bottom', 'right', 'top', + 'labelleft', 'labelbottom', 'labelright', 'labeltop', + 'labelrotation'] + _gridline_param_names + kwtrans = {} + if 'length' in kw: + kwtrans['size'] = kw.pop('length') + if 'direction' in kw: + kwtrans['tickdir'] = kw.pop('direction') + if 'rotation' in kw: + kwtrans['labelrotation'] = kw.pop('rotation') + if 'left' in kw: + kwtrans['tick1On'] = _string_to_bool(kw.pop('left')) + if 'bottom' in kw: + kwtrans['tick1On'] = _string_to_bool(kw.pop('bottom')) + if 'right' in kw: + kwtrans['tick2On'] = _string_to_bool(kw.pop('right')) + if 'top' in kw: + kwtrans['tick2On'] = _string_to_bool(kw.pop('top')) + if 'labelleft' in kw: + kwtrans['label1On'] = _string_to_bool(kw.pop('labelleft')) + if 'labelbottom' in kw: + kwtrans['label1On'] = _string_to_bool(kw.pop('labelbottom')) + if 'labelright' in kw: + kwtrans['label2On'] = _string_to_bool(kw.pop('labelright')) + if 'labeltop' in kw: + kwtrans['label2On'] = _string_to_bool(kw.pop('labeltop')) + if 'colors' in kw: + c = kw.pop('colors') + kwtrans['color'] = c + kwtrans['labelcolor'] = c + # Maybe move the checking up to the caller of this method. + for key in kw: + if key not in kwkeys: + raise ValueError( + "keyword %s is not recognized; valid keywords are %s" + % (key, kwkeys)) kwtrans.update(kw) - else: - raise NotImplementedError("Inverse translation is deferred") return kwtrans def set_clip_path(self, clippath, transform=None): @@ -1110,22 +1090,11 @@ def _update_ticks(self, renderer): return ticks_to_draw def _get_tick_bboxes(self, ticks, renderer): - """ - Given the list of ticks, return two lists of bboxes. One for - tick lable1's and another for tick label2's. - """ - - ticklabelBoxes = [] - ticklabelBoxes2 = [] - - for tick in ticks: - if tick.label1On and tick.label1.get_visible(): - extent = tick.label1.get_window_extent(renderer) - ticklabelBoxes.append(extent) - if tick.label2On and tick.label2.get_visible(): - extent = tick.label2.get_window_extent(renderer) - ticklabelBoxes2.append(extent) - return ticklabelBoxes, ticklabelBoxes2 + """Return lists of bboxes for ticks' label1's and label2's.""" + return ([tick.label1.get_window_extent(renderer) + for tick in ticks if tick.label1.get_visible()], + [tick.label2.get_window_extent(renderer) + for tick in ticks if tick.label2.get_visible()]) def get_tightbbox(self, renderer): """ @@ -1200,12 +1169,6 @@ def draw(self, renderer, *args, **kwargs): self.offsetText.set_text(self.major.formatter.get_offset()) self.offsetText.draw(renderer) - if 0: # draw the bounding boxes around the text for debug - for tick in self.majorTicks: - label = tick.label1 - mpatches.bbox_artist(label, renderer) - mpatches.bbox_artist(self.label, renderer) - renderer.close_group(__name__) self.stale = False @@ -1234,17 +1197,17 @@ def get_pickradius(self): return self.pickradius def get_majorticklabels(self): - 'Return a list of Text instances for the major ticklabels' + 'Return a list of Text instances for the major ticklabels.' ticks = self.get_major_ticks() - labels1 = [tick.label1 for tick in ticks if tick.label1On] - labels2 = [tick.label2 for tick in ticks if tick.label2On] + labels1 = [tick.label1 for tick in ticks if tick.label1.get_visible()] + labels2 = [tick.label2 for tick in ticks if tick.label2.get_visible()] return cbook.silent_list('Text major ticklabel', labels1 + labels2) def get_minorticklabels(self): - 'Return a list of Text instances for the minor ticklabels' + 'Return a list of Text instances for the minor ticklabels.' ticks = self.get_minor_ticks() - labels1 = [tick.label1 for tick in ticks if tick.label1On] - labels2 = [tick.label2 for tick in ticks if tick.label2On] + labels1 = [tick.label1 for tick in ticks if tick.label1.get_visible()] + labels2 = [tick.label2 for tick in ticks if tick.label2.get_visible()] return cbook.silent_list('Text minor ticklabel', labels1 + labels2) def get_ticklabels(self, minor=False, which=None): @@ -1353,16 +1316,10 @@ def _copy_tick_props(self, src, dest): return dest.label1.update_from(src.label1) dest.label2.update_from(src.label2) - dest.tick1line.update_from(src.tick1line) dest.tick2line.update_from(src.tick2line) dest.gridline.update_from(src.gridline) - dest.tick1On = src.tick1On - dest.tick2On = src.tick2On - dest.label1On = src.label1On - dest.label2On = src.label2On - def get_label_text(self): 'Get the text of the label' return self.label.get_text() @@ -1384,31 +1341,29 @@ def get_minor_formatter(self): return self.minor.formatter def get_major_ticks(self, numticks=None): - 'get the tick instances; grow as necessary' + 'Get the tick instances; grow as necessary.' if numticks is None: numticks = len(self.get_major_locator()()) while len(self.majorTicks) < numticks: - # update the new tick label properties from the old + # Update the new tick label properties from the old. tick = self._get_tick(major=True) self.majorTicks.append(tick) - if self._gridOnMajor: - tick.gridOn = True + tick.gridline.set_visible(self._gridOnMajor) self._copy_tick_props(self.majorTicks[0], tick) return self.majorTicks[:numticks] def get_minor_ticks(self, numticks=None): - 'get the minor tick instances; grow as necessary' + 'Get the minor tick instances; grow as necessary.' if numticks is None: numticks = len(self.get_minor_locator()()) while len(self.minorTicks) < numticks: - # update the new tick label properties from the old + # Update the new tick label properties from the old. tick = self._get_tick(major=False) self.minorTicks.append(tick) - if self._gridOnMinor: - tick.gridOn = True + tick.gridline.set_visible(self._gridOnMinor) self._copy_tick_props(self.minorTicks[0], tick) return self.minorTicks[:numticks] @@ -1709,9 +1664,9 @@ def set_ticklabels(self, ticklabels, *args, minor=False, **kwargs): tick.label2.set_text(tick_label) tick.label2.update(kwargs) # only return visible tick labels - if tick.label1On: + if tick.label1.get_visible(): ret.append(tick.label1) - if tick.label2On: + if tick.label2.get_visible(): ret.append(tick.label2) self.stale = True @@ -2068,8 +2023,7 @@ def tick_top(self): label = (self._major_tick_kw['label1On'] or self._major_tick_kw['label2On']) self.set_ticks_position('top') - # if labels were turned off before this was called - # leave them off + # If labels were turned off before this was called, leave them off. self.set_tick_params(which='both', labeltop=label) def tick_bottom(self): @@ -2081,39 +2035,35 @@ def tick_bottom(self): label = (self._major_tick_kw['label1On'] or self._major_tick_kw['label2On']) self.set_ticks_position('bottom') - # if labels were turned off before this was called - # leave them off + # If labels were turned off before this was called, leave them off. self.set_tick_params(which='both', labelbottom=label) def get_ticks_position(self): """ Return the ticks position (top, bottom, default or unknown) """ - majt = self.majorTicks[0] - mT = self.minorTicks[0] - - majorTop = ((not majt.tick1On) and majt.tick2On and - (not majt.label1On) and majt.label2On) - minorTop = ((not mT.tick1On) and mT.tick2On and - (not mT.label1On) and mT.label2On) - if majorTop and minorTop: - return 'top' - - MajorBottom = (majt.tick1On and (not majt.tick2On) and - majt.label1On and (not majt.label2On)) - MinorBottom = (mT.tick1On and (not mT.tick2On) and - mT.label1On and (not mT.label2On)) - if MajorBottom and MinorBottom: - return 'bottom' - - majorDefault = (majt.tick1On and majt.tick2On and - majt.label1On and (not majt.label2On)) - minorDefault = (mT.tick1On and mT.tick2On and - mT.label1On and (not mT.label2On)) - if majorDefault and minorDefault: - return 'default' - - return 'unknown' + major = self.majorTicks[0] + minor = self.minorTicks[0] + if all(tick.tick1line.get_visible() + and not tick.tick2line.get_visible() + and tick.label1.get_visible() + and not tick.label2.get_visible() + for tick in [major, minor]): + return "bottom" + elif all(tick.tick2line.get_visible() + and not tick.tick1line.get_visible() + and tick.label2.get_visible() + and not tick.label1.get_visible() + for tick in [major, minor]): + return "top" + elif all(tick.tick1line.get_visible() + and tick.tick2line.get_visible() + and tick.label1.get_visible() + and not tick.label2.get_visible() + for tick in [major, minor]): + return "default" + else: + return "unknown" def get_view_interval(self): 'return the Interval instance for this axis view limits' diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py index 7c7181e6a8ba..b39498aedab7 100644 --- a/lib/matplotlib/backend_bases.py +++ b/lib/matplotlib/backend_bases.py @@ -2337,9 +2337,9 @@ def key_press_handler(event, canvas, toolbar=None): def _get_uniform_gridstate(ticks): # Return True/False if all grid lines are on or off, None if they are # not all in the same state. - if all(tick.gridOn for tick in ticks): + if all(tick.gridline.get_visible() for tick in ticks): return True - elif not any(tick.gridOn for tick in ticks): + elif not any(tick.gridline.get_visible() for tick in ticks): return False else: return None diff --git a/lib/matplotlib/backend_tools.py b/lib/matplotlib/backend_tools.py index 4e5cd5789aed..f71e3e3f040a 100644 --- a/lib/matplotlib/backend_tools.py +++ b/lib/matplotlib/backend_tools.py @@ -458,9 +458,9 @@ def _get_uniform_grid_state(ticks): Returns True/False if all grid lines are on or off, None if they are not all in the same state. """ - if all(tick.gridOn for tick in ticks): + if all(tick.gridline.get_visible() for tick in ticks): return True - elif not any(tick.gridOn for tick in ticks): + elif not any(tick.gridline.get_visible() for tick in ticks): return False else: return None diff --git a/lib/matplotlib/projections/polar.py b/lib/matplotlib/projections/polar.py index bcffe5b21132..378a1d4fd92c 100644 --- a/lib/matplotlib/projections/polar.py +++ b/lib/matplotlib/projections/polar.py @@ -325,26 +325,25 @@ def update_position(self, loc): text_angle = np.rad2deg(angle) % 360 - 90 angle -= np.pi / 2 - if self.tick1On: - marker = self.tick1line.get_marker() - if marker in (mmarkers.TICKUP, '|'): - trans = mtransforms.Affine2D().scale(1.0, 1.0).rotate(angle) - elif marker == mmarkers.TICKDOWN: - trans = mtransforms.Affine2D().scale(1.0, -1.0).rotate(angle) - else: - # Don't modify custom tick line markers. - trans = self.tick1line._marker._transform - self.tick1line._marker._transform = trans - if self.tick2On: - marker = self.tick2line.get_marker() - if marker in (mmarkers.TICKUP, '|'): - trans = mtransforms.Affine2D().scale(1.0, 1.0).rotate(angle) - elif marker == mmarkers.TICKDOWN: - trans = mtransforms.Affine2D().scale(1.0, -1.0).rotate(angle) - else: - # Don't modify custom tick line markers. - trans = self.tick2line._marker._transform - self.tick2line._marker._transform = trans + marker = self.tick1line.get_marker() + if marker in (mmarkers.TICKUP, '|'): + trans = mtransforms.Affine2D().scale(1, 1).rotate(angle) + elif marker == mmarkers.TICKDOWN: + trans = mtransforms.Affine2D().scale(1, -1).rotate(angle) + else: + # Don't modify custom tick line markers. + trans = self.tick1line._marker._transform + self.tick1line._marker._transform = trans + + marker = self.tick2line.get_marker() + if marker in (mmarkers.TICKUP, '|'): + trans = mtransforms.Affine2D().scale(1, 1).rotate(angle) + elif marker == mmarkers.TICKDOWN: + trans = mtransforms.Affine2D().scale(1, -1).rotate(angle) + else: + # Don't modify custom tick line markers. + trans = self.tick2line._marker._transform + self.tick2line._marker._transform = trans mode, user_angle = self._labelrotation if mode == 'default': @@ -355,10 +354,8 @@ def update_position(self, loc): elif text_angle < -90: text_angle += 180 text_angle += user_angle - if self.label1On: - self.label1.set_rotation(text_angle) - if self.label2On: - self.label2.set_rotation(text_angle) + self.label1.set_rotation(text_angle) + self.label2.set_rotation(text_angle) # This extra padding helps preserve the look from previous releases but # is also needed because labels are anchored to their center. @@ -627,37 +624,31 @@ def update_position(self, loc): text_angle += user_angle else: text_angle = user_angle - if self.label1On: - if full: - ha = self.label1.get_horizontalalignment() - va = self.label1.get_verticalalignment() - else: - ha, va = self._determine_anchor(mode, angle, direction > 0) - self.label1.set_ha(ha) - self.label1.set_va(va) - self.label1.set_rotation(text_angle) - if self.tick1On: - marker = self.tick1line.get_marker() - if marker == mmarkers.TICKLEFT: - trans = (mtransforms.Affine2D() - .scale(1.0, 1.0) - .rotate(tick_angle)) - elif marker == '_': - trans = (mtransforms.Affine2D() - .scale(1.0, 1.0) - .rotate(tick_angle + np.pi / 2)) - elif marker == mmarkers.TICKRIGHT: - trans = (mtransforms.Affine2D() - .scale(-1.0, 1.0) - .rotate(tick_angle)) - else: - # Don't modify custom tick line markers. - trans = self.tick1line._marker._transform - self.tick1line._marker._transform = trans if full: - self.label2On = False - self.tick2On = False + ha = self.label1.get_horizontalalignment() + va = self.label1.get_verticalalignment() + else: + ha, va = self._determine_anchor(mode, angle, direction > 0) + self.label1.set_horizontalalignment(ha) + self.label1.set_verticalalignment(va) + self.label1.set_rotation(text_angle) + + marker = self.tick1line.get_marker() + if marker == mmarkers.TICKLEFT: + trans = mtransforms.Affine2D().rotate(tick_angle) + elif marker == '_': + trans = mtransforms.Affine2D().rotate(tick_angle + np.pi / 2) + elif marker == mmarkers.TICKRIGHT: + trans = mtransforms.Affine2D().scale(-1, 1).rotate(tick_angle) + else: + # Don't modify custom tick line markers. + trans = self.tick1line._marker._transform + self.tick1line._marker._transform = trans + + if full: + self.label2.set_visible(False) + self.tick2line.set_visible(False) else: angle = (thetamax * direction + offset) % 360 - 90 if direction > 0: @@ -675,29 +666,23 @@ def update_position(self, loc): text_angle += user_angle else: text_angle = user_angle - if self.label2On: - ha, va = self._determine_anchor(mode, angle, direction < 0) - self.label2.set_ha(ha) - self.label2.set_va(va) - self.label2.set_rotation(text_angle) - if self.tick2On: - marker = self.tick2line.get_marker() - if marker == mmarkers.TICKLEFT: - trans = (mtransforms.Affine2D() - .scale(1.0, 1.0) - .rotate(tick_angle)) - elif marker == '_': - trans = (mtransforms.Affine2D() - .scale(1.0, 1.0) - .rotate(tick_angle + np.pi / 2)) - elif marker == mmarkers.TICKRIGHT: - trans = (mtransforms.Affine2D() - .scale(-1.0, 1.0) - .rotate(tick_angle)) - else: - # Don't modify custom tick line markers. - trans = self.tick2line._marker._transform - self.tick2line._marker._transform = trans + + ha, va = self._determine_anchor(mode, angle, direction < 0) + self.label2.set_ha(ha) + self.label2.set_va(va) + self.label2.set_rotation(text_angle) + + marker = self.tick2line.get_marker() + if marker == mmarkers.TICKLEFT: + trans = mtransforms.Affine2D().rotate(tick_angle) + elif marker == '_': + trans = mtransforms.Affine2D().rotate(tick_angle + np.pi / 2) + elif marker == mmarkers.TICKRIGHT: + trans = mtransforms.Affine2D().scale(-1, 1).rotate(tick_angle) + else: + # Don't modify custom tick line markers. + trans = self.tick2line._marker._transform + self.tick2line._marker._transform = trans class RadialAxis(maxis.YAxis): diff --git a/lib/matplotlib/tests/test_skew.py b/lib/matplotlib/tests/test_skew.py index 7a4b62fa4c96..31d2ce7aabcb 100644 --- a/lib/matplotlib/tests/test_skew.py +++ b/lib/matplotlib/tests/test_skew.py @@ -1,6 +1,8 @@ """ -Testing that skewed axes properly work +Testing that skewed axes properly work. """ + +from contextlib import ExitStack import itertools import matplotlib.pyplot as plt @@ -17,66 +19,24 @@ # The sole purpose of this class is to look at the upper, lower, or total # interval as appropriate and see what parts of the tick to draw, if any. class SkewXTick(maxis.XTick): - def update_position(self, loc): - # This ensures that the new value of the location is set before - # any other updates take place - self._loc = loc - super().update_position(loc) - - def _has_default_loc(self): - return self.get_loc() is None - - def _need_lower(self): - return (self._has_default_loc() or - transforms.interval_contains(self.axes.lower_xlim, - self.get_loc())) - - def _need_upper(self): - return (self._has_default_loc() or - transforms.interval_contains(self.axes.upper_xlim, - self.get_loc())) - - @property - def gridOn(self): - return (self._gridOn and (self._has_default_loc() or - transforms.interval_contains(self.get_view_interval(), - self.get_loc()))) - - @gridOn.setter - def gridOn(self, value): - self._gridOn = value - - @property - def tick1On(self): - return self._tick1On and self._need_lower() - - @tick1On.setter - def tick1On(self, value): - self._tick1On = value - - @property - def label1On(self): - return self._label1On and self._need_lower() - - @label1On.setter - def label1On(self, value): - self._label1On = value - - @property - def tick2On(self): - return self._tick2On and self._need_upper() - - @tick2On.setter - def tick2On(self, value): - self._tick2On = value - - @property - def label2On(self): - return self._label2On and self._need_upper() - - @label2On.setter - def label2On(self, value): - self._label2On = value + def draw(self, renderer): + with ExitStack() as stack: + for artist in [self.gridline, self.tick1line, self.tick2line, + self.label1, self.label2]: + stack.callback(artist.set_visible, artist.get_visible()) + needs_lower = transforms.interval_contains( + self.axes.lower_xlim, self.get_loc()) + needs_upper = transforms.interval_contains( + self.axes.upper_xlim, self.get_loc()) + self.tick1line.set_visible( + self.tick1line.get_visible() and needs_lower) + self.label1.set_visible( + self.label1.get_visible() and needs_lower) + self.tick2line.set_visible( + self.tick2line.get_visible() and needs_upper) + self.label2.set_visible( + self.label2.get_visible() and needs_upper) + super(SkewXTick, self).draw(renderer) def get_view_interval(self): return self.axes.xaxis.get_view_interval() diff --git a/lib/mpl_toolkits/mplot3d/axis3d.py b/lib/mpl_toolkits/mplot3d/axis3d.py index 53b0411b25c6..8b1d40fbec2d 100644 --- a/lib/mpl_toolkits/mplot3d/axis3d.py +++ b/lib/mpl_toolkits/mplot3d/axis3d.py @@ -34,12 +34,10 @@ def move_from_center(coord, centers, deltas, axmask=(True, True, True)): def tick_update_position(tick, tickxs, tickys, labelpos): '''Update tick line and label position and style.''' - for (label, on) in [(tick.label1, tick.label1On), - (tick.label2, tick.label2On)]: - if on: - label.set_position(labelpos) - - tick.tick1On, tick.tick2On = True, False + tick.label1.set_position(labelpos) + tick.label2.set_position(labelpos) + tick.tick1line.set_visible(True) + tick.tick2line.set_visible(False) tick.tick1line.set_linestyle('-') tick.tick1line.set_marker('') tick.tick1line.set_data(tickxs, tickys) From c61d71dc951d5fc29eb11fcdd5279cdaa6f001c6 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Mon, 24 Sep 2018 22:42:46 +0200 Subject: [PATCH 2/2] Placate flake8. --- examples/specialty_plots/skewt.py | 4 ++++ lib/matplotlib/axis.py | 22 +++++++++++----------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/examples/specialty_plots/skewt.py b/examples/specialty_plots/skewt.py index bf4662123e6d..3d2d56b72f77 100644 --- a/examples/specialty_plots/skewt.py +++ b/examples/specialty_plots/skewt.py @@ -27,6 +27,10 @@ # interval as appropriate and see what parts of the tick to draw, if any. class SkewXTick(maxis.XTick): def draw(self, renderer): + # When adding the callbacks with `stack.callback`, we fetch the current + # visibility state of the artist with `get_visible`; the ExitStack will + # restore these states (`set_visible`) at the end of the block (after + # the draw). with ExitStack() as stack: for artist in [self.gridline, self.tick1line, self.tick2line, self.label1, self.label2]: diff --git a/lib/matplotlib/axis.py b/lib/matplotlib/axis.py index 4010aaa055e0..b12cd06dee82 100644 --- a/lib/matplotlib/axis.py +++ b/lib/matplotlib/axis.py @@ -9,7 +9,7 @@ import numpy as np from matplotlib import rcParams -import matplotlib.artist as artist +import matplotlib.artist as martist import matplotlib.cbook as cbook from matplotlib.cbook import _string_to_bool import matplotlib.font_manager as font_manager @@ -27,14 +27,14 @@ # This list is being used for compatibility with Axes.grid, which # allows all Line2D kwargs. -_line_AI = artist.ArtistInspector(mlines.Line2D) +_line_AI = martist.ArtistInspector(mlines.Line2D) _line_param_names = _line_AI.get_setters() _line_param_aliases = [list(d)[0] for d in _line_AI.aliasd.values()] _gridline_param_names = ['grid_' + name for name in _line_param_names + _line_param_aliases] -class Tick(artist.Artist): +class Tick(martist.Artist): """ Abstract base class for the axis ticks, grid lines and labels @@ -81,7 +81,7 @@ def __init__(self, axes, loc, label, loc is the tick location in data coords size is the tick size in points """ - artist.Artist.__init__(self) + martist.Artist.__init__(self) if gridOn is None: if major and (rcParams['axes.grid.which'] in ('both', 'major')): @@ -230,11 +230,11 @@ def get_children(self): return children def set_clip_path(self, clippath, transform=None): - artist.Artist.set_clip_path(self, clippath, transform) + martist.Artist.set_clip_path(self, clippath, transform) self.gridline.set_clip_path(clippath, transform) self.stale = True - set_clip_path.__doc__ = artist.Artist.set_clip_path.__doc__ + set_clip_path.__doc__ = martist.Artist.set_clip_path.__doc__ def get_pad_pixels(self): return self.figure.dpi * self._base_pad / 72 @@ -289,7 +289,7 @@ def get_loc(self): 'Return the tick location (data coords) as a scalar' return self._loc - @artist.allow_rasterization + @martist.allow_rasterization def draw(self, renderer): if not self.get_visible(): self.stale = False @@ -669,7 +669,7 @@ def __get__(self, instance, cls): return instance.minorTicks -class Axis(artist.Artist): +class Axis(martist.Artist): """ Public attributes @@ -687,7 +687,7 @@ def __init__(self, axes, pickradius=15): """ Init the axis with the parent Axes instance """ - artist.Artist.__init__(self) + martist.Artist.__init__(self) self.set_figure(axes.figure) self.isDefault_label = True @@ -899,7 +899,7 @@ def _translate_tick_kw(kw): return kwtrans def set_clip_path(self, clippath, transform=None): - artist.Artist.set_clip_path(self, clippath, transform) + martist.Artist.set_clip_path(self, clippath, transform) for child in self.majorTicks + self.minorTicks: child.set_clip_path(clippath, transform) self.stale = True @@ -1141,7 +1141,7 @@ def get_tick_padding(self): values.append(self.minorTicks[0].get_tick_padding()) return max(values, default=0) - @artist.allow_rasterization + @martist.allow_rasterization def draw(self, renderer, *args, **kwargs): 'Draw the axis lines, grid lines, tick lines and labels'