diff --git a/doc/api/next_api_changes/deprecations/30369-AL.rst b/doc/api/next_api_changes/deprecations/30369-AL.rst new file mode 100644 index 000000000000..8b82c80054d9 --- /dev/null +++ b/doc/api/next_api_changes/deprecations/30369-AL.rst @@ -0,0 +1,14 @@ +:mod:`.axisartist` now uses more standard tick direction controls +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Previously, the position of :mod:`.axisartist` ticks (inside or outside +the axes) were set using ``set_tick_out(bool)``. They are now set +using ``set_tick_direction("in")`` (or "out", or "inout"), and respect +:rc:`xtick.direction` and :rc:`ytick.direction`. In particular, they default +to pointing outwards, consistently with the rest of the library. + +The *tick_out* parameter of `.Ticks` has been deprecated (use *tick_direction* +instead). The ``Ticks.get_tick_out`` method is deprecated (use +`.Ticks.get_tick_direction` instead). + +The unused ``locs_angles_labels`` attribute of `.Ticks` and `.LabelBase` has +also been deprecated. diff --git a/galleries/examples/axisartist/simple_axis_pad.py b/galleries/examples/axisartist/simple_axis_pad.py index fcd58885a57a..39dc4e7a1642 100644 --- a/galleries/examples/axisartist/simple_axis_pad.py +++ b/galleries/examples/axisartist/simple_axis_pad.py @@ -81,23 +81,33 @@ def ann(ax1, d): va="top", ha="center") -ax1 = setup_axes(fig, rect=141) +ax1 = setup_axes(fig, rect=231) axis = add_floating_axis1(ax1) -ann(ax1, r"default") +ann(ax1, "default") -ax1 = setup_axes(fig, rect=142) +ax1 = setup_axes(fig, rect=232) axis = add_floating_axis1(ax1) axis.major_ticklabels.set_pad(10) -ann(ax1, r"ticklabels.set_pad(10)") +ann(ax1, "ticklabels.set_pad(10)") -ax1 = setup_axes(fig, rect=143) +ax1 = setup_axes(fig, rect=233) axis = add_floating_axis1(ax1) axis.label.set_pad(20) -ann(ax1, r"label.set_pad(20)") +ann(ax1, "label.set_pad(20)") -ax1 = setup_axes(fig, rect=144) +ax1 = setup_axes(fig, rect=234) axis = add_floating_axis1(ax1) -axis.major_ticks.set_tick_out(True) -ann(ax1, "ticks.set_tick_out(True)") +axis.major_ticks.set_tick_direction("in") +ann(ax1, 'ticks.set_tick_direction("in")') + +ax1 = setup_axes(fig, rect=235) +axis = add_floating_axis1(ax1) +axis.major_ticks.set_tick_direction("out") +ann(ax1, 'ticks.set_tick_direction("out")') + +ax1 = setup_axes(fig, rect=236) +axis = add_floating_axis1(ax1) +axis.major_ticks.set_tick_direction("inout") +ann(ax1, 'ticks.set_tick_direction("inout")') plt.show() diff --git a/galleries/users_explain/toolkits/axisartist.rst b/galleries/users_explain/toolkits/axisartist.rst index 7ff0897f23d8..7229d1735d0c 100644 --- a/galleries/users_explain/toolkits/axisartist.rst +++ b/galleries/users_explain/toolkits/axisartist.rst @@ -287,16 +287,16 @@ HowTo ax.axis[:].major_ticklabels.set_color("r") -4. To change the tick size (length), you need to use - axis.major_ticks.set_ticksize method. To change the direction of - the ticks (ticks are in opposite direction of ticklabels by - default), use axis.major_ticks.set_tick_out method. +4. To change the tick size (length), use ``axis.major_ticks.set_ticksize``. + + To change the direction of the ticks, use + ``axis.major_ticks.set_tick_direction``. To change the pad between ticks and ticklabels, use - axis.major_ticklabels.set_pad method. + ``axis.major_ticklabels.set_pad``. - To change the pad between ticklabels and axis label, - axis.label.set_pad method. + To change the pad between ticklabels and axis label, use + ``axis.label.set_pad``. Rotation and alignment of TickLabels ==================================== @@ -398,15 +398,14 @@ axis_direction of ticks, ticklabels, and axis-label does not affect them. If you want to make ticks outward and ticklabels inside the axes, -use invert_ticklabel_direction method. :: +use `.AxisArtist.invert_ticklabel_direction`:: ax.axis[:].invert_ticklabel_direction() -A related method is "set_tick_out". It makes ticks outward (as a -matter of fact, it makes ticks toward the opposite direction of the -default direction). :: +A related method is `.Ticks.set_tick_direction`. It can make ticks point "in", +"out", or "inout" (crossing the axis halfway):: - ax.axis[:].major_ticks.set_tick_out(True) + ax.axis[:].major_ticks.set_tick_direction("inout") .. figure:: /gallery/axisartist/images/sphx_glr_simple_axis_direction03_001.png :target: /gallery/axisartist/simple_axis_direction03.html @@ -416,27 +415,28 @@ So, in summary, * AxisArtist's methods - - set_axis_direction: "left", "right", "bottom", or "top" - - set_ticklabel_direction: "+" or "-" - - set_axislabel_direction: "+" or "-" - - invert_ticklabel_direction + - `~.AxisArtist.set_axis_direction`: "left", "right", "bottom", or "top" + - `~.AxisArtist.set_ticklabel_direction`: "+" or "-" + - `~.AxisArtist.set_axislabel_direction`: "+" or "-" + - `~.AxisArtist.invert_ticklabel_direction` * Ticks' methods (major_ticks and minor_ticks) - - set_tick_out: True or False - - set_ticksize: size in points + - `~.Ticks.set_tick_direction`: "in", "out", or "inout" + - `~.Ticks.set_ticksize`: size in points * TickLabels' methods (major_ticklabels and minor_ticklabels) - - set_axis_direction: "left", "right", "bottom", or "top" - - set_rotation: angle with respect to the reference direction - - set_ha and set_va: see below + - `~.TickLabels.set_axis_direction`: "left", "right", "bottom", or "top" + - `~.Text.set_rotation`: angle with respect to the reference direction + - `~.Text.set_horizontalalignment` and `~.Text.set_verticalalignment`: see below -* AxisLabels' methods (label) +* AxisLabel' methods (label) - - set_axis_direction: "left", "right", "bottom", or "top" - - set_rotation: angle with respect to the reference direction - - set_ha and set_va + - `~.AxisLabel.set_axis_direction`: "left", "right", "bottom", or "top" + - `~.Text.set_rotation`: angle with respect to the reference direction + - `~.Text.set_horizontalalignment` and + `~.Text.set_verticalalignment` Adjusting ticklabels alignment ------------------------------ diff --git a/lib/mpl_toolkits/axisartist/axis_artist.py b/lib/mpl_toolkits/axisartist/axis_artist.py index 9ba6f6075844..caffb71f9ca4 100644 --- a/lib/mpl_toolkits/axisartist/axis_artist.py +++ b/lib/mpl_toolkits/axisartist/axis_artist.py @@ -55,14 +55,14 @@ axislabel ha right center right center =================== ====== ======== ====== ======== -Ticks are by default direct opposite side of the ticklabels. To make ticks to -the same side of the ticklabels, :: +Tick orientation is controlled by :rc:`xtick.direction` and +:rc:`ytick.direction`; they can be manually adjusted using :: - ax.axis["bottom"].major_ticks.set_tick_out(True) + ax.axis["bottom"].major_ticks.set_tick_direction("in") # or "out", "inout" The following attributes can be customized (use the ``set_xxx`` methods): -* `Ticks`: ticksize, tick_out +* `Ticks`: ticksize, tick_direction * `TickLabels`: pad * `AxisLabel`: pad """ @@ -109,16 +109,16 @@ class Ticks(AttributeCopier, Line2D): Ticks are derived from `.Line2D`, and note that ticks themselves are markers. Thus, you should use set_mec, set_mew, etc. - To change the tick size (length), you need to use - `set_ticksize`. To change the direction of the ticks (ticks are - in opposite direction of ticklabels by default), use - ``set_tick_out(False)`` + To change the tick size (length), use `set_ticksize`. + To change the direction of the ticks, use ``set_tick_direction("in")`` (or + "out", or "inout"). """ + locs_angles_labels = _api.deprecated("3.11")(property(lambda self: [])) + + @_api.delete_parameter("3.11", "tick_out", alternative="tick_direction") def __init__(self, ticksize, tick_out=False, *, axis=None, **kwargs): self._ticksize = ticksize - self.locs_angles_labels = [] - self.set_tick_out(tick_out) self._axis = axis @@ -152,13 +152,33 @@ def get_markeredgecolor(self): def get_markeredgewidth(self): return self.get_attribute_from_ref_artist("markeredgewidth") + def set_tick_direction(self, direction): + _api.check_in_list(["in", "out", "inout"], direction=direction) + self._tick_dir = direction + + def get_tick_direction(self): + return self._tick_dir + def set_tick_out(self, b): - """Set whether ticks are drawn inside or outside the axes.""" - self._tick_out = b + """ + Set whether ticks are drawn inside or outside the axes. + .. admonition:: Discouraged + Consider using the more general method `.set_tick_direction` instead. + """ + self._tick_dir = "out" if b else "in" + + @_api.deprecated("3.11", alternative="get_tick_direction") def get_tick_out(self): """Return whether ticks are drawn inside or outside the axes.""" - return self._tick_out + if self._tick_dir == "in": + return False + elif self._tick_dir == "out": + return True + else: + raise ValueError( + f"Tick direction ({self._tick_dir!r}) not supported by get_tick_out, " + f"use get_tick_direction instead") def set_ticksize(self, ticksize): """Set length of the ticks in points.""" @@ -171,8 +191,6 @@ def get_ticksize(self): def set_locs_angles(self, locs_angles): self.locs_angles = locs_angles - _tickvert_path = Path([[0., 0.], [1., 0.]]) - def draw(self, renderer): if not self.get_visible(): return @@ -182,18 +200,20 @@ def draw(self, renderer): gc.set_linewidth(self.get_markeredgewidth()) gc.set_alpha(self._alpha) + tickvert_path = ( + Path([[0, 0], [1, 0]]) if self._tick_dir == "in" else + Path([[-1, 0], [0, 0]]) if self._tick_dir == "out" else + Path([[-.5, 0.], [.5, 0]])) # if self._tick_dir == "inout" path_trans = self.get_transform() marker_transform = (Affine2D() .scale(renderer.points_to_pixels(self._ticksize))) - if self.get_tick_out(): - marker_transform.rotate_deg(180) for loc, angle in self.locs_angles: locs = path_trans.transform_non_affine(np.array([loc])) if self.axes and not self.axes.viewLim.contains(*locs[0]): continue renderer.draw_markers( - gc, self._tickvert_path, + gc, tickvert_path, marker_transform + Affine2D().rotate_deg(angle), Path(locs), path_trans.get_affine()) @@ -207,8 +227,9 @@ class LabelBase(mtext.Text): text_ref_angle, and offset_radius attributes. """ + locs_angles_labels = _api.deprecated("3.11")(property(lambda self: [])) + def __init__(self, *args, **kwargs): - self.locs_angles_labels = [] self._ref_angle = 0 self._offset_radius = 0. @@ -866,14 +887,16 @@ def _init_ticks(self, **kwargs): + self.offset_transform) self.major_ticks = Ticks( - kwargs.get( + ticksize=kwargs.get( "major_tick_size", mpl.rcParams[f"{axis_name}tick.major.size"]), + tick_direction=mpl.rcParams[f"{axis_name}tick.direction"], axis=self.axis, transform=trans) self.minor_ticks = Ticks( - kwargs.get( + ticksize=kwargs.get( "minor_tick_size", mpl.rcParams[f"{axis_name}tick.minor.size"]), + tick_direction=mpl.rcParams[f"{axis_name}tick.direction"], axis=self.axis, transform=trans) size = mpl.rcParams[f"{axis_name}tick.labelsize"] @@ -925,14 +948,13 @@ def _update_ticks(self, renderer=None): if renderer is None: renderer = self.get_figure(root=True)._get_renderer() - dpi_cor = renderer.points_to_pixels(1.) - if self.major_ticks.get_visible() and self.major_ticks.get_tick_out(): - ticklabel_pad = self.major_ticks._ticksize * dpi_cor - self.major_ticklabels._external_pad = ticklabel_pad - self.minor_ticklabels._external_pad = ticklabel_pad - else: - self.major_ticklabels._external_pad = 0 - self.minor_ticklabels._external_pad = 0 + self.major_ticklabels._external_pad = \ + self.minor_ticklabels._external_pad = ( + renderer.points_to_pixels(self.major_ticks._ticksize) + * {"in": 0, "inout": 1/2, "out": 1}[ + self.major_ticks.get_tick_direction()] + * self.major_ticks.get_visible() # 0 if invisible. + ) majortick_iter, minortick_iter = \ self._axis_artist_helper.get_tick_iterators(self.axes) @@ -1007,13 +1029,18 @@ def _update_label(self, renderer): return if self._ticklabel_add_angle != self._axislabel_add_angle: - if ((self.major_ticks.get_visible() - and not self.major_ticks.get_tick_out()) - or (self.minor_ticks.get_visible() - and not self.major_ticks.get_tick_out())): - axislabel_pad = self.major_ticks._ticksize - else: - axislabel_pad = 0 + axislabel_pad = max( + # major pad: + self.major_ticks._ticksize + * {"in": 1, "inout": .5, "out": 0}[ + self.major_ticks.get_tick_direction()] + * self.major_ticks.get_visible(), # 0 if invisible. + # minor pad: + self.minor_ticks._ticksize + * {"in": 1, "inout": .5, "out": 0}[ + self.minor_ticks.get_tick_direction()] + * self.minor_ticks.get_visible(), # 0 if invisible. + ) else: axislabel_pad = max(self.major_ticklabels._axislabel_pad, self.minor_ticklabels._axislabel_pad) diff --git a/lib/mpl_toolkits/axisartist/tests/test_axis_artist.py b/lib/mpl_toolkits/axisartist/tests/test_axis_artist.py index d44a61b6dd4a..e93d0608e212 100644 --- a/lib/mpl_toolkits/axisartist/tests/test_axis_artist.py +++ b/lib/mpl_toolkits/axisartist/tests/test_axis_artist.py @@ -19,7 +19,7 @@ def test_ticks(): ticks_in.set_locs_angles(locs_angles) ax.add_artist(ticks_in) - ticks_out = Ticks(ticksize=10, tick_out=True, color='C3', axis=ax.xaxis) + ticks_out = Ticks(ticksize=10, tick_direction="out", color='C3', axis=ax.xaxis) ticks_out.set_locs_angles(locs_angles) ax.add_artist(ticks_out) @@ -89,11 +89,11 @@ def test_axis_artist(): for loc in ('left', 'right', 'bottom'): helper = AxisArtistHelperRectlinear.Fixed(ax, loc=loc) axisline = AxisArtist(ax, helper, offset=None, axis_direction=loc) + axisline.major_ticks.set_tick_direction("in") ax.add_artist(axisline) # Settings for bottom AxisArtist. axisline.set_label("TTT") - axisline.major_ticks.set_tick_out(False) axisline.label.set_pad(5) ax.set_ylabel("Test") diff --git a/lib/mpl_toolkits/axisartist/tests/test_axislines.py b/lib/mpl_toolkits/axisartist/tests/test_axislines.py index a1485d4f436b..97b4d3a267e7 100644 --- a/lib/mpl_toolkits/axisartist/tests/test_axislines.py +++ b/lib/mpl_toolkits/axisartist/tests/test_axislines.py @@ -10,7 +10,8 @@ @image_comparison(['SubplotZero.png'], style='default') def test_SubplotZero(): # Remove this line when this test image is regenerated. - plt.rcParams['text.kerning_factor'] = 6 + plt.rcParams.update({ + "text.kerning_factor": 6, "xtick.direction": "in", "ytick.direction": "in"}) fig = plt.figure() @@ -31,7 +32,8 @@ def test_SubplotZero(): @image_comparison(['Subplot.png'], style='default') def test_Subplot(): # Remove this line when this test image is regenerated. - plt.rcParams['text.kerning_factor'] = 6 + plt.rcParams.update({ + "text.kerning_factor": 6, "xtick.direction": "in", "ytick.direction": "in"}) fig = plt.figure() @@ -60,6 +62,8 @@ def test_Axes(): @image_comparison(['ParasiteAxesAuxTrans_meshplot.png'], remove_text=True, style='default', tol=0.075) def test_ParasiteAxesAuxTrans(): + # Remove this line when this test image is regenerated. + plt.rcParams.update({"xtick.direction": "in", "ytick.direction": "in"}) data = np.ones((6, 6)) data[2, 2] = 2 data[0, :] = 0 @@ -91,6 +95,8 @@ def test_ParasiteAxesAuxTrans(): @image_comparison(['axisline_style.png'], remove_text=True, style='mpl20') def test_axisline_style(): + # Remove this line when this test image is regenerated. + plt.rcParams.update({"xtick.direction": "in", "ytick.direction": "in"}) fig = plt.figure(figsize=(2, 2)) ax = fig.add_subplot(axes_class=AxesZero) ax.axis["xzero"].set_axisline_style("-|>") @@ -105,6 +111,8 @@ def test_axisline_style(): @image_comparison(['axisline_style_size_color.png'], remove_text=True, style='mpl20') def test_axisline_style_size_color(): + # Remove this line when this test image is regenerated. + plt.rcParams.update({"xtick.direction": "in", "ytick.direction": "in"}) fig = plt.figure(figsize=(2, 2)) ax = fig.add_subplot(axes_class=AxesZero) ax.axis["xzero"].set_axisline_style("-|>", size=2.0, facecolor='r') @@ -119,6 +127,8 @@ def test_axisline_style_size_color(): @image_comparison(['axisline_style_tight.png'], remove_text=True, style='mpl20') def test_axisline_style_tight(): + # Remove this line when this test image is regenerated. + plt.rcParams.update({"xtick.direction": "in", "ytick.direction": "in"}) fig = plt.figure(figsize=(2, 2), layout='tight') ax = fig.add_subplot(axes_class=AxesZero) ax.axis["xzero"].set_axisline_style("-|>", size=5, facecolor='g') @@ -132,6 +142,8 @@ def test_axisline_style_tight(): @image_comparison(['subplotzero_ylabel.png'], style='mpl20') def test_subplotzero_ylabel(): + # Remove this line when this test image is regenerated. + plt.rcParams.update({"xtick.direction": "in", "ytick.direction": "in"}) fig = plt.figure() ax = fig.add_subplot(111, axes_class=SubplotZero) diff --git a/lib/mpl_toolkits/axisartist/tests/test_floating_axes.py b/lib/mpl_toolkits/axisartist/tests/test_floating_axes.py index feb667af013e..98d49dc0cf37 100644 --- a/lib/mpl_toolkits/axisartist/tests/test_floating_axes.py +++ b/lib/mpl_toolkits/axisartist/tests/test_floating_axes.py @@ -23,6 +23,8 @@ def test_subplot(): # remove when image is regenerated. @image_comparison(['curvelinear3.png'], style='default', tol=5) def test_curvelinear3(): + # Remove this line when this test image is regenerated. + plt.rcParams.update({"xtick.direction": "in", "ytick.direction": "in"}) fig = plt.figure(figsize=(5, 5)) tr = (mtransforms.Affine2D().scale(np.pi / 180, 1) + @@ -70,7 +72,8 @@ def test_curvelinear3(): @image_comparison(['curvelinear4.png'], style='default', tol=0.9) def test_curvelinear4(): # Remove this line when this test image is regenerated. - plt.rcParams['text.kerning_factor'] = 6 + plt.rcParams.update({ + "text.kerning_factor": 6, "xtick.direction": "in", "ytick.direction": "in"}) fig = plt.figure(figsize=(5, 5)) diff --git a/lib/mpl_toolkits/axisartist/tests/test_grid_helper_curvelinear.py b/lib/mpl_toolkits/axisartist/tests/test_grid_helper_curvelinear.py index 7d6554782fe6..988a2775ef3e 100644 --- a/lib/mpl_toolkits/axisartist/tests/test_grid_helper_curvelinear.py +++ b/lib/mpl_toolkits/axisartist/tests/test_grid_helper_curvelinear.py @@ -17,6 +17,9 @@ @image_comparison(['custom_transform.png'], style='default', tol=0.2) def test_custom_transform(): + # Remove this line when this test image is regenerated. + plt.rcParams.update({"xtick.direction": "in", "ytick.direction": "in"}) + class MyTransform(Transform): input_dims = output_dims = 2 @@ -78,6 +81,8 @@ def inverted(self): @image_comparison(['polar_box.png'], style='default', tol=0.04) def test_polar_box(): + # Remove this line when this test image is regenerated. + plt.rcParams.update({"xtick.direction": "in", "ytick.direction": "in"}) fig = plt.figure(figsize=(5, 5)) # PolarAxes.PolarTransform takes radian. However, we want our coordinate @@ -138,7 +143,9 @@ def test_polar_box(): # Remove tol & kerning_factor when this test image is regenerated. @image_comparison(['axis_direction.png'], style='default', tol=0.13) def test_axis_direction(): - plt.rcParams['text.kerning_factor'] = 6 + # Remove this line when this test image is regenerated. + plt.rcParams.update({ + "text.kerning_factor": 6, "xtick.direction": "in", "ytick.direction": "in"}) fig = plt.figure(figsize=(5, 5))