diff --git a/lib/matplotlib/axis.py b/lib/matplotlib/axis.py index 5a1778cb311a..35257aec36a5 100644 --- a/lib/matplotlib/axis.py +++ b/lib/matplotlib/axis.py @@ -99,38 +99,28 @@ def __init__(self, axes, loc, label, self._loc = loc + major_minor = "major" if major else "minor" + if size is None: - if major: - size = rcParams['%s.major.size' % name] - else: - size = rcParams['%s.minor.size' % name] + size = rcParams[f"{name}.{major_minor}.size"] self._size = size if width is None: - if major: - width = rcParams['%s.major.width' % name] - else: - width = rcParams['%s.minor.width' % name] + width = rcParams[f"{name}.{major_minor}.width"] self._width = width if color is None: - color = rcParams['%s.color' % name] - self._color = color + color = rcParams[f"{name}.color"] if pad is None: - if major: - pad = rcParams['%s.major.pad' % name] - else: - pad = rcParams['%s.minor.pad' % name] + pad = rcParams[f"{name}.{major_minor}.pad"] self._base_pad = pad if labelcolor is None: - labelcolor = rcParams['%s.color' % name] - self._labelcolor = labelcolor + labelcolor = rcParams[f"{name}.color"] if labelsize is None: - labelsize = rcParams['%s.labelsize' % name] - self._labelsize = labelsize + labelsize = rcParams[f"{name}.labelsize"] self._set_labelrotation(labelrotation) @@ -141,30 +131,61 @@ def __init__(self, axes, loc, label, zorder = mlines.Line2D.zorder self._zorder = zorder - self._grid_color = (rcParams['grid.color'] - if grid_color is None else grid_color) - self._grid_linestyle = (rcParams['grid.linestyle'] - if grid_linestyle is None else grid_linestyle) - self._grid_linewidth = (rcParams['grid.linewidth'] - if grid_linewidth is None else grid_linewidth) - self._grid_alpha = (rcParams['grid.alpha'] - if grid_alpha is None else grid_alpha) - - self._grid_kw = {k[5:]: v for k, v in kw.items()} + if grid_color is None: + grid_color = rcParams["grid.color"] + if grid_linestyle is None: + grid_linestyle = rcParams["grid.linestyle"] + if grid_linewidth is None: + grid_linewidth = rcParams["grid.linewidth"] + if grid_alpha is None: + grid_alpha = rcParams["grid.alpha"] + grid_kw = {k[5:]: v for k, v in kw.items()} self.apply_tickdir(tickdir) - self.tick1line = self._get_tick1line() - self.tick2line = self._get_tick2line() - self.gridline = self._get_gridline() - self.label1 = self._get_text1() - self.label2 = self._get_text2() - - 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.tick1line = mlines.Line2D( + [], [], + color=color, linestyle="none", zorder=zorder, visible=tick1On, + markersize=size, markeredgewidth=width, + ) + self.tick2line = mlines.Line2D( + [], [], + color=color, linestyle="none", zorder=zorder, visible=tick2On, + markersize=size, markeredgewidth=width, + ) + self.gridline = mlines.Line2D( + [], [], + color=grid_color, alpha=grid_alpha, visible=gridOn, + linestyle=grid_linestyle, linewidth=grid_linewidth, marker="", + **grid_kw, + ) + self.gridline.get_path()._interpolation_steps = \ + GRIDLINE_INTERPOLATION_STEPS + self.label1 = mtext.Text( + np.nan, np.nan, + fontproperties=font_manager.FontProperties(size=labelsize), + color=labelcolor, visible=label1On, + ) + self.label2 = mtext.Text( + np.nan, np.nan, + fontproperties=font_manager.FontProperties(size=labelsize), + color=labelcolor, visible=label2On, + ) + for meth, attr in [("_get_tick1line", "tick1line"), + ("_get_tick2line", "tick2line"), + ("_get_gridline", "gridline"), + ("_get_text1", "label1"), + ("_get_text2", "label2")]: + if getattr(self, meth) != getattr(Tick, meth).__get__(self): + cbook.warn_deprecated( + "3.3", message=f"Relying on {meth} to initialize " + f"Tick.{attr} is deprecated since %(since)s and will not " + f"work %(removal)s; please directly set the attribute in " + "the subclass' __init__ instead.") + setattr(self, attr, getattr(self, meth)()) + for artist in [self.tick1line, self.tick2line, self.gridline, + self.label1, self.label2]: + self._set_artist_props(artist) self.update_position(loc) @@ -264,23 +285,18 @@ def get_pad(self): def _get_text1(self): 'Get the default Text 1 instance' - pass def _get_text2(self): 'Get the default Text 2 instance' - pass def _get_tick1line(self): 'Get the default line2D instance for tick1' - pass def _get_tick2line(self): 'Get the default line2D instance for tick2' - pass def _get_gridline(self): 'Get the default grid Line2d instance for this tick' - pass def get_loc(self): 'Return the tick location (data coords) as a scalar' @@ -404,6 +420,35 @@ class XTick(Tick): """ __name__ = 'xtick' + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + # x in data coords, y in axes coords + self.tick1line.set( + xdata=[0], ydata=[0], + transform=self.axes.get_xaxis_transform(which="tick1"), + marker=self._tickmarkers[0], + ) + self.tick2line.set( + xdata=[0], ydata=[1], + transform=self.axes.get_xaxis_transform(which="tick2"), + marker=self._tickmarkers[1], + ) + self.gridline.set( + xdata=[0, 0], ydata=[0, 1], + transform=self.axes.get_xaxis_transform(which="grid"), + ) + # the y loc is 3 points below the min of y axis + trans, va, ha = self._get_text1_transform() + self.label1.set( + x=0, y=0, + verticalalignment=va, horizontalalignment=ha, transform=trans, + ) + trans, va, ha = self._get_text2_transform() + self.label2.set( + x=0, y=1, + verticalalignment=va, horizontalalignment=ha, transform=trans, + ) + def _get_text1_transform(self): return self.axes.get_xaxis_text1_transform(self._pad) @@ -424,80 +469,6 @@ def apply_tickdir(self, tickdir): self._pad = self._base_pad + self.get_tick_padding() self.stale = True - def _get_text1(self): - 'Get the default Text instance' - # the y loc is 3 points below the min of y axis - # get the affine as an a, b, c, d, tx, ty list - # x in data coords, y in axes coords - trans, vert, horiz = self._get_text1_transform() - t = mtext.Text( - x=0, y=0, - fontproperties=font_manager.FontProperties(size=self._labelsize), - color=self._labelcolor, - verticalalignment=vert, - horizontalalignment=horiz, - ) - t.set_transform(trans) - self._set_artist_props(t) - return t - - def _get_text2(self): - 'Get the default Text 2 instance' - # x in data coords, y in axes coords - trans, vert, horiz = self._get_text2_transform() - t = mtext.Text( - x=0, y=1, - fontproperties=font_manager.FontProperties(size=self._labelsize), - color=self._labelcolor, - verticalalignment=vert, - horizontalalignment=horiz, - ) - t.set_transform(trans) - self._set_artist_props(t) - return t - - def _get_tick1line(self): - 'Get the default line2D instance' - # x in data coords, y in axes coords - l = mlines.Line2D(xdata=(0,), ydata=(0,), color=self._color, - linestyle='None', marker=self._tickmarkers[0], - markersize=self._size, - markeredgewidth=self._width, zorder=self._zorder) - l.set_transform(self.axes.get_xaxis_transform(which='tick1')) - self._set_artist_props(l) - return l - - def _get_tick2line(self): - 'Get the default line2D instance' - # x in data coords, y in axes coords - l = mlines.Line2D(xdata=(0,), ydata=(1,), - color=self._color, - linestyle='None', - marker=self._tickmarkers[1], - markersize=self._size, - markeredgewidth=self._width, - zorder=self._zorder) - - l.set_transform(self.axes.get_xaxis_transform(which='tick2')) - self._set_artist_props(l) - return l - - def _get_gridline(self): - 'Get the default line2D instance' - # x in data coords, y in axes coords - l = mlines.Line2D(xdata=(0.0, 0.0), ydata=(0, 1.0), - color=self._grid_color, - linestyle=self._grid_linestyle, - linewidth=self._grid_linewidth, - alpha=self._grid_alpha, - markersize=0, - **self._grid_kw) - l.set_transform(self.axes.get_xaxis_transform(which='grid')) - l.get_path()._interpolation_steps = GRIDLINE_INTERPOLATION_STEPS - self._set_artist_props(l) - - return l - def update_position(self, loc): """Set the location of tick in data coords with scalar *loc*.""" self.tick1line.set_xdata((loc,)) @@ -520,6 +491,35 @@ class YTick(Tick): """ __name__ = 'ytick' + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + # x in axes coords, y in data coords + self.tick1line.set( + xdata=[0], ydata=[0], + transform=self.axes.get_yaxis_transform(which="tick1"), + marker=self._tickmarkers[0], + ) + self.tick2line.set( + xdata=[1], ydata=[0], + transform=self.axes.get_yaxis_transform(which="tick2"), + marker=self._tickmarkers[1], + ) + self.gridline.set( + xdata=[0, 1], ydata=[0, 0], + transform=self.axes.get_yaxis_transform(which="grid"), + ) + # the y loc is 3 points below the min of y axis + trans, va, ha = self._get_text1_transform() + self.label1.set( + x=0, y=0, + verticalalignment=va, horizontalalignment=ha, transform=trans, + ) + trans, va, ha = self._get_text2_transform() + self.label2.set( + x=1, y=0, + verticalalignment=va, horizontalalignment=ha, transform=trans, + ) + def _get_text1_transform(self): return self.axes.get_yaxis_text1_transform(self._pad) @@ -540,81 +540,6 @@ def apply_tickdir(self, tickdir): self._pad = self._base_pad + self.get_tick_padding() self.stale = True - # how far from the y axis line the right of the ticklabel are - def _get_text1(self): - 'Get the default Text instance' - # x in axes coords, y in data coords - trans, vert, horiz = self._get_text1_transform() - t = mtext.Text( - x=0, y=0, - fontproperties=font_manager.FontProperties(size=self._labelsize), - color=self._labelcolor, - verticalalignment=vert, - horizontalalignment=horiz, - ) - t.set_transform(trans) - self._set_artist_props(t) - return t - - def _get_text2(self): - 'Get the default Text instance' - # x in axes coords, y in data coords - trans, vert, horiz = self._get_text2_transform() - t = mtext.Text( - x=1, y=0, - fontproperties=font_manager.FontProperties(size=self._labelsize), - color=self._labelcolor, - verticalalignment=vert, - horizontalalignment=horiz, - ) - t.set_transform(trans) - self._set_artist_props(t) - return t - - def _get_tick1line(self): - 'Get the default line2D instance' - # x in axes coords, y in data coords - - l = mlines.Line2D((0,), (0,), - color=self._color, - marker=self._tickmarkers[0], - linestyle='None', - markersize=self._size, - markeredgewidth=self._width, - zorder=self._zorder) - l.set_transform(self.axes.get_yaxis_transform(which='tick1')) - self._set_artist_props(l) - return l - - def _get_tick2line(self): - 'Get the default line2D instance' - # x in axes coords, y in data coords - l = mlines.Line2D((1,), (0,), - color=self._color, - marker=self._tickmarkers[1], - linestyle='None', - markersize=self._size, - markeredgewidth=self._width, - zorder=self._zorder) - l.set_transform(self.axes.get_yaxis_transform(which='tick2')) - self._set_artist_props(l) - return l - - def _get_gridline(self): - 'Get the default line2D instance' - # x in axes coords, y in data coords - l = mlines.Line2D(xdata=(0, 1), ydata=(0, 0), - color=self._grid_color, - linestyle=self._grid_linestyle, - linewidth=self._grid_linewidth, - alpha=self._grid_alpha, - markersize=0, - **self._grid_kw) - l.set_transform(self.axes.get_yaxis_transform(which='grid')) - l.get_path()._interpolation_steps = GRIDLINE_INTERPOLATION_STEPS - self._set_artist_props(l) - return l - def update_position(self, loc): """Set the location of tick in data coords with scalar *loc*.""" self.tick1line.set_ydata((loc,)) @@ -766,9 +691,18 @@ def __init__(self, axes, pickradius=15): self._autolabelpos = True self._smart_bounds = False # Deprecated in 3.2 - self.label = self._get_label() + self.label = mtext.Text( + np.nan, np.nan, + fontproperties=font_manager.FontProperties( + size=rcParams['axes.labelsize'], + weight=rcParams['axes.labelweight']), + color=rcParams['axes.labelcolor'], + ) + self._set_artist_props(self.label) + self.offsetText = mtext.Text(np.nan, np.nan) + self._set_artist_props(self.offsetText) + self.labelpad = rcParams['axes.labelpad'] - self.offsetText = self._get_offset_text() self.pickradius = pickradius @@ -1244,12 +1178,6 @@ def draw(self, renderer, *args, **kwargs): renderer.close_group(__name__) self.stale = False - def _get_label(self): - raise NotImplementedError('Derived must override') - - def _get_offset_text(self): - raise NotImplementedError('Derived must override') - def get_gridlines(self): 'Return the grid lines as a list of Line2D instance' ticks = self.get_major_ticks() @@ -1919,6 +1847,28 @@ class XAxis(Axis): __name__ = 'xaxis' axis_name = 'x' + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + # x in axes coords, y in display coords (to be updated at draw time by + # _update_label_positions and _update_offset_text_position). + self.label.set( + x=0.5, y=0, + verticalalignment='top', horizontalalignment='center', + transform=mtransforms.blended_transform_factory( + self.axes.transAxes, mtransforms.IdentityTransform()), + ) + self.label_position = 'bottom' + self.offsetText.set( + x=1, y=0, + verticalalignment='top', horizontalalignment='right', + transform=mtransforms.blended_transform_factory( + self.axes.transAxes, mtransforms.IdentityTransform()), + fontproperties=font_manager.FontProperties( + size=rcParams['xtick.labelsize']), + color=rcParams['xtick.color'], + ) + self.offset_text_position = 'bottom' + def contains(self, mouseevent): """Test whether the mouse event occurred in the x axis. """ @@ -1945,39 +1895,6 @@ def _get_tick(self, major): tick_kw = self._minor_tick_kw return XTick(self.axes, 0, '', major=major, **tick_kw) - def _get_label(self): - # x in axes coords, y in display coords (to be updated at draw - # time by _update_label_positions) - label = mtext.Text(x=0.5, y=0, - fontproperties=font_manager.FontProperties( - size=rcParams['axes.labelsize'], - weight=rcParams['axes.labelweight']), - color=rcParams['axes.labelcolor'], - verticalalignment='top', - horizontalalignment='center') - - label.set_transform(mtransforms.blended_transform_factory( - self.axes.transAxes, mtransforms.IdentityTransform())) - - self._set_artist_props(label) - self.label_position = 'bottom' - return label - - def _get_offset_text(self): - # x in axes coords, y in display coords (to be updated at draw time) - offsetText = mtext.Text(x=1, y=0, - fontproperties=font_manager.FontProperties( - size=rcParams['xtick.labelsize']), - color=rcParams['xtick.color'], - verticalalignment='top', - horizontalalignment='right') - offsetText.set_transform(mtransforms.blended_transform_factory( - self.axes.transAxes, mtransforms.IdentityTransform()) - ) - self._set_artist_props(offsetText) - self.offset_text_position = 'bottom' - return offsetText - def set_label_position(self, position): """ Set the label position (top or bottom) @@ -2204,6 +2121,30 @@ class YAxis(Axis): __name__ = 'yaxis' axis_name = 'y' + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + # x in display coords, y in axes coords (to be updated at draw time by + # _update_label_positions and _update_offset_text_position). + self.label.set( + x=0, y=0.5, + verticalalignment='bottom', horizontalalignment='center', + rotation='vertical', rotation_mode='anchor', + transform=mtransforms.blended_transform_factory( + mtransforms.IdentityTransform(), self.axes.transAxes), + ) + self.label_position = 'left' + # x in axes coords, y in display coords(!). + self.offsetText.set( + x=0, y=0.5, + verticalalignment='baseline', horizontalalignment='left', + transform=mtransforms.blended_transform_factory( + self.axes.transAxes, mtransforms.IdentityTransform()), + fontproperties=font_manager.FontProperties( + size=rcParams['ytick.labelsize']), + color=rcParams['ytick.color'], + ) + self.offset_text_position = 'left' + def contains(self, mouseevent): """Test whether the mouse event occurred in the y axis. @@ -2232,42 +2173,6 @@ def _get_tick(self, major): tick_kw = self._minor_tick_kw return YTick(self.axes, 0, '', major=major, **tick_kw) - def _get_label(self): - # x in display coords (updated by _update_label_position) - # y in axes coords - label = mtext.Text(x=0, y=0.5, - # todo: get the label position - fontproperties=font_manager.FontProperties( - size=rcParams['axes.labelsize'], - weight=rcParams['axes.labelweight']), - color=rcParams['axes.labelcolor'], - verticalalignment='bottom', - horizontalalignment='center', - rotation='vertical', - rotation_mode='anchor') - label.set_transform(mtransforms.blended_transform_factory( - mtransforms.IdentityTransform(), self.axes.transAxes)) - - self._set_artist_props(label) - self.label_position = 'left' - return label - - def _get_offset_text(self): - # x in display coords, y in axes coords (to be updated at draw time) - offsetText = mtext.Text(x=0, y=0.5, - fontproperties=font_manager.FontProperties( - size=rcParams['ytick.labelsize'] - ), - color=rcParams['ytick.color'], - verticalalignment='baseline', - horizontalalignment='left') - offsetText.set_transform(mtransforms.blended_transform_factory( - self.axes.transAxes, mtransforms.IdentityTransform()) - ) - self._set_artist_props(offsetText) - self.offset_text_position = 'left' - return offsetText - def set_label_position(self, position): """ Set the label position (left or right) diff --git a/lib/matplotlib/projections/polar.py b/lib/matplotlib/projections/polar.py index c33fa0560f0b..9b70f160d4a5 100644 --- a/lib/matplotlib/projections/polar.py +++ b/lib/matplotlib/projections/polar.py @@ -257,26 +257,19 @@ class ThetaTick(maxis.XTick): the spine. The label padding is also applied here since it's not possible to use a generic axes transform to produce tick-specific padding. """ + def __init__(self, axes, *args, **kwargs): self._text1_translate = mtransforms.ScaledTranslation( - 0, 0, - axes.figure.dpi_scale_trans) + 0, 0, axes.figure.dpi_scale_trans) self._text2_translate = mtransforms.ScaledTranslation( - 0, 0, - axes.figure.dpi_scale_trans) + 0, 0, axes.figure.dpi_scale_trans) super().__init__(axes, *args, **kwargs) - - def _get_text1(self): - t = super()._get_text1() - t.set_rotation_mode('anchor') - t.set_transform(t.get_transform() + self._text1_translate) - return t - - def _get_text2(self): - t = super()._get_text2() - t.set_rotation_mode('anchor') - t.set_transform(t.get_transform() + self._text2_translate) - return t + self.label1.set( + rotation_mode='anchor', + transform=self.label1.get_transform() + self._text1_translate) + self.label2.set( + rotation_mode='anchor', + transform=self.label2.get_transform() + self._text2_translate) def _apply_params(self, **kw): super()._apply_params(**kw) @@ -506,15 +499,11 @@ class RadialTick(maxis.YTick): are also rotated to be perpendicular to the spine, when 'auto' rotation is enabled. """ - def _get_text1(self): - t = super()._get_text1() - t.set_rotation_mode('anchor') - return t - - def _get_text2(self): - t = super()._get_text2() - t.set_rotation_mode('anchor') - return t + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.label1.set_rotation_mode('anchor') + self.label2.set_rotation_mode('anchor') def _determine_anchor(self, mode, angle, start): # Note: angle is the (spine angle - 90) because it's used for the tick diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index 16f495c04375..0b92b04c1b94 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -5656,9 +5656,9 @@ def test_axis_set_tick_params_labelsize_labelcolor(): # Expected values after setting the ticks assert axis_1.yaxis.majorTicks[0]._size == 4.0 - assert axis_1.yaxis.majorTicks[0]._color == 'k' - assert axis_1.yaxis.majorTicks[0]._labelsize == 30.0 - assert axis_1.yaxis.majorTicks[0]._labelcolor == 'red' + assert axis_1.yaxis.majorTicks[0].tick1line.get_color() == 'k' + assert axis_1.yaxis.majorTicks[0].label1.get_size() == 30.0 + assert axis_1.yaxis.majorTicks[0].label1.get_color() == 'red' def test_axes_tick_params_gridlines(): @@ -5667,10 +5667,10 @@ def test_axes_tick_params_gridlines(): ax.tick_params(grid_color='b', grid_linewidth=5, grid_alpha=0.5, grid_linestyle='dashdot') for axis in ax.xaxis, ax.yaxis: - assert axis.majorTicks[0]._grid_color == 'b' - assert axis.majorTicks[0]._grid_linewidth == 5 - assert axis.majorTicks[0]._grid_alpha == 0.5 - assert axis.majorTicks[0]._grid_linestyle == 'dashdot' + assert axis.majorTicks[0].gridline.get_color() == 'b' + assert axis.majorTicks[0].gridline.get_linewidth() == 5 + assert axis.majorTicks[0].gridline.get_alpha() == 0.5 + assert axis.majorTicks[0].gridline.get_linestyle() == '-.' def test_axes_tick_params_ylabelside():