From 7de868bd2fa2ae6e766acc262233a18dfe5a2a5e Mon Sep 17 00:00:00 2001 From: Greg Lucas Date: Tue, 1 Jun 2021 20:04:10 -0600 Subject: [PATCH 1/5] ENH: Make Colorbar inherit from an Axes base class --- lib/matplotlib/colorbar.py | 90 +++++++++++++-------------- lib/matplotlib/tests/test_colorbar.py | 4 +- 2 files changed, 44 insertions(+), 50 deletions(-) diff --git a/lib/matplotlib/colorbar.py b/lib/matplotlib/colorbar.py index 6fa03e994b67..3b8c1082b43d 100644 --- a/lib/matplotlib/colorbar.py +++ b/lib/matplotlib/colorbar.py @@ -310,7 +310,7 @@ def draw(self, renderer): return ret -class ColorbarBase: +class ColorbarBase(ColorbarAxes): r""" Draw a colorbar in an existing axes. @@ -417,9 +417,9 @@ def __init__(self, ax, *, cmap=None, ['uniform', 'proportional'], spacing=spacing) # wrap the axes so that it can be positioned as an inset axes: - ax = ColorbarAxes(ax, userax=userax) - self.ax = ax - ax.set(navigate=False) + super().__init__(ax, userax=userax) + self.ax = self + self.set(navigate=False) if cmap is None: cmap = cm.get_cmap() @@ -448,24 +448,19 @@ def __init__(self, ax, *, cmap=None, self.extendrect = extendrect self.solids = None self.solids_patches = [] - self.lines = [] + # self.lines = [] - for spine in self.ax.spines.values(): + for spine in self.spines.values(): spine.set_visible(False) - for spine in self.ax.outer_ax.spines.values(): + for spine in self.outer_ax.spines.values(): spine.set_visible(False) - self.outline = self.ax.spines['outline'] = _ColorbarSpine(self.ax) - - self.patch = mpatches.Polygon( - np.empty((0, 2)), - color=mpl.rcParams['axes.facecolor'], linewidth=0.01, zorder=-1) - ax.add_artist(self.patch) + self.outline = self.spines['outline'] = _ColorbarSpine(self) self.dividers = collections.LineCollection( [], colors=[mpl.rcParams['axes.edgecolor']], linewidths=[0.5 * mpl.rcParams['axes.linewidth']]) - self.ax.add_collection(self.dividers) + self.add_collection(self.dividers) self.locator = None self.formatter = None @@ -519,8 +514,8 @@ def draw_all(self): # also adds the outline path to self.outline spine: self._do_extends(extendlen) - self.ax.set_xlim(self.vmin, self.vmax) - self.ax.set_ylim(self.vmin, self.vmax) + self.set_xlim(self.vmin, self.vmax) + self.set_ylim(self.vmin, self.vmax) # set up the tick locators and formatters. A bit complicated because # boundary norms + uniform spacing requires a manual locator. @@ -548,7 +543,7 @@ def _add_solids(self, X, Y, C): and any(hatch is not None for hatch in mappable.hatches)): self._add_solids_patches(X, Y, C, mappable) else: - self.solids = self.ax.pcolormesh( + self.solids = self.pcolormesh( X, Y, C, cmap=self.cmap, norm=self.norm, alpha=self.alpha, edgecolors='none', shading='flat') if not self.drawedges: @@ -569,7 +564,7 @@ def _add_solids_patches(self, X, Y, C, mappable): facecolor=self.cmap(self.norm(C[i][0])), hatch=hatches[i], linewidth=0, antialiased=False, alpha=self.alpha) - self.ax.add_patch(patch) + self.add_patch(patch) patches.append(patch) self.solids_patches = patches @@ -605,7 +600,7 @@ def _do_extends(self, extendlen): if self.orientation == 'horizontal': bounds = bounds[[1, 0, 3, 2]] xyout = xyout[:, ::-1] - self.ax._set_inner_bounds(bounds) + self._set_inner_bounds(bounds) # xyout is the path for the spine: self.outline.set_xy(xyout) @@ -634,9 +629,9 @@ def _do_extends(self, extendlen): color = self.cmap(self.norm(self._values[0])) patch = mpatches.PathPatch( mpath.Path(xy), facecolor=color, linewidth=0, - antialiased=False, transform=self.ax.outer_ax.transAxes, + antialiased=False, transform=self.outer_ax.transAxes, hatch=hatches[0]) - self.ax.outer_ax.add_patch(patch) + self.outer_ax.add_patch(patch) if self._extend_upper(): if not self.extendrect: # triangle @@ -651,8 +646,8 @@ def _do_extends(self, extendlen): patch = mpatches.PathPatch( mpath.Path(xy), facecolor=color, linewidth=0, antialiased=False, - transform=self.ax.outer_ax.transAxes, hatch=hatches[-1]) - self.ax.outer_ax.add_patch(patch) + transform=self.outer_ax.transAxes, hatch=hatches[-1]) + self.outer_ax.add_patch(patch) return def add_lines(self, levels, colors, linewidths, erase=True): @@ -699,25 +694,24 @@ def add_lines(self, levels, colors, linewidths, erase=True): # make a clip path that is just a linewidth bigger than the axes... fac = np.max(linewidths) / 72 xy = np.array([[0, 0], [1, 0], [1, 1], [0, 1], [0, 0]]) - inches = self.ax.get_figure().dpi_scale_trans + inches = self.get_figure().dpi_scale_trans # do in inches: - xy = inches.inverted().transform(self.ax.transAxes.transform(xy)) + xy = inches.inverted().transform(self.transAxes.transform(xy)) xy[[0, 1, 4], 1] -= fac xy[[2, 3], 1] += fac # back to axes units... - xy = self.ax.transAxes.inverted().transform(inches.transform(xy)) + xy = self.transAxes.inverted().transform(inches.transform(xy)) if self.orientation == 'horizontal': xy = xy.T col.set_clip_path(mpath.Path(xy, closed=True), - self.ax.transAxes) - self.ax.add_collection(col) + self.transAxes) + self.add_collection(col) self.stale = True def update_ticks(self): """ Setup the ticks and ticklabels. This should not be needed by users. """ - ax = self.ax # Get the locator and formatter; defaults to self.locator if not None. self._get_ticker_locator_formatter() self._long_axis().set_major_locator(self.locator) @@ -832,7 +826,7 @@ def minorticks_on(self): """ Turn on colorbar minor ticks. """ - self.ax.minorticks_on() + super().minorticks_on() self.minorlocator = self._long_axis().get_minor_locator() self._short_axis().set_minor_locator(ticker.NullLocator()) @@ -863,9 +857,9 @@ def set_label(self, label, *, loc=None, **kwargs): Supported keywords are *labelpad* and `.Text` properties. """ if self.orientation == "vertical": - self.ax.set_ylabel(label, loc=loc, **kwargs) + self.set_ylabel(label, loc=loc, **kwargs) else: - self.ax.set_xlabel(label, loc=loc, **kwargs) + self.set_xlabel(label, loc=loc, **kwargs) self.stale = True def set_alpha(self, alpha): @@ -874,8 +868,8 @@ def set_alpha(self, alpha): def remove(self): """Remove this colorbar from the figure.""" - self.ax.inner_ax.remove() - self.ax.outer_ax.remove() + self.inner_ax.remove() + self.outer_ax.remove() def _ticker(self, locator, formatter): """ @@ -1009,29 +1003,29 @@ def _reset_locator_formatter_scale(self): isinstance(self.norm, colors.BoundaryNorm)): if self.spacing == 'uniform': funcs = (self._forward_boundaries, self._inverse_boundaries) - self.ax.set_xscale('function', functions=funcs) - self.ax.set_yscale('function', functions=funcs) + self.set_xscale('function', functions=funcs) + self.set_yscale('function', functions=funcs) self.__scale = 'function' elif self.spacing == 'proportional': self.__scale = 'linear' - self.ax.set_xscale('linear') - self.ax.set_yscale('linear') + self.set_xscale('linear') + self.set_yscale('linear') elif hasattr(self.norm, '_scale') and self.norm._scale is not None: # use the norm's scale: - self.ax.set_xscale(self.norm._scale) - self.ax.set_yscale(self.norm._scale) + self.set_xscale(self.norm._scale) + self.set_yscale(self.norm._scale) self.__scale = self.norm._scale.name elif type(self.norm) is colors.Normalize: # plain Normalize: - self.ax.set_xscale('linear') - self.ax.set_yscale('linear') + self.set_xscale('linear') + self.set_yscale('linear') self.__scale = 'linear' else: # norm._scale is None or not an attr: derive the scale from # the Norm: funcs = (self.norm, self.norm.inverse) - self.ax.set_xscale('function', functions=funcs) - self.ax.set_yscale('function', functions=funcs) + self.set_xscale('function', functions=funcs) + self.set_yscale('function', functions=funcs) self.__scale = 'function' def _locate(self, x): @@ -1140,14 +1134,14 @@ def _extend_upper(self): def _long_axis(self): """Return the long axis""" if self.orientation == 'vertical': - return self.ax.yaxis - return self.ax.xaxis + return self.yaxis + return self.xaxis def _short_axis(self): """Return the short axis""" if self.orientation == 'vertical': - return self.ax.xaxis - return self.ax.yaxis + return self.xaxis + return self.yaxis class Colorbar(ColorbarBase): diff --git a/lib/matplotlib/tests/test_colorbar.py b/lib/matplotlib/tests/test_colorbar.py index 6167618575dd..c6e5ca0dfef6 100644 --- a/lib/matplotlib/tests/test_colorbar.py +++ b/lib/matplotlib/tests/test_colorbar.py @@ -768,9 +768,9 @@ def test_inset_colorbar_layout(): fig.draw_no_output() # make sure this is in the figure. In the colorbar swapping # it was being dropped from the list of children... - np.testing.assert_allclose(cb.ax.get_position().bounds, + np.testing.assert_allclose(cb.get_position().bounds, [0.87, 0.342, 0.0237, 0.315], atol=0.01) - assert cb.ax.outer_ax in ax.child_axes + assert cb.outer_ax in ax.child_axes @image_comparison(['colorbar_twoslope.png'], remove_text=True, From 7cbeb40e8f10010c6ad4630b08994f3211f16e9d Mon Sep 17 00:00:00 2001 From: Greg Lucas Date: Tue, 1 Jun 2021 21:05:39 -0600 Subject: [PATCH 2/5] FIX: Adding a line property to Colorbar to override the axes attribute --- lib/matplotlib/colorbar.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/colorbar.py b/lib/matplotlib/colorbar.py index 3b8c1082b43d..7a6b21a21580 100644 --- a/lib/matplotlib/colorbar.py +++ b/lib/matplotlib/colorbar.py @@ -448,7 +448,7 @@ def __init__(self, ax, *, cmap=None, self.extendrect = extendrect self.solids = None self.solids_patches = [] - # self.lines = [] + self._lines = [] for spine in self.spines.values(): spine.set_visible(False) @@ -484,6 +484,10 @@ def __init__(self, ax, *, cmap=None, self.formatter = format # Assume it is a Formatter or None self.draw_all() + @property + def lines(self): + return self._lines + def draw_all(self): """ Calculate any free parameters based on the current cmap and norm, From 8a3223411d924c7115502ee9bee6ba5b706eaf70 Mon Sep 17 00:00:00 2001 From: Greg Lucas Date: Wed, 2 Jun 2021 06:55:27 -0600 Subject: [PATCH 3/5] MAINT: Updating all cbar.ax calls to just cbar This is for clarity that the colorbar is now an Axes itself. --- examples/axes_grid1/demo_axes_divider.py | 2 +- .../contour_demo.py | 4 +- .../contourf_demo.py | 2 +- .../image_annotated_heatmap.py | 2 +- .../colorbar_tick_labelling_demo.py | 4 +- lib/matplotlib/colorbar.py | 8 +- lib/matplotlib/tests/test_colorbar.py | 76 +++++++++---------- .../tests/test_constrainedlayout.py | 6 +- lib/matplotlib/tests/test_contour.py | 6 +- lib/mpl_toolkits/tests/test_mplot3d.py | 2 +- 10 files changed, 55 insertions(+), 57 deletions(-) diff --git a/examples/axes_grid1/demo_axes_divider.py b/examples/axes_grid1/demo_axes_divider.py index dddc2ed4760c..345bb34ef496 100644 --- a/examples/axes_grid1/demo_axes_divider.py +++ b/examples/axes_grid1/demo_axes_divider.py @@ -22,7 +22,7 @@ def demo_simple_image(ax): im = ax.imshow(Z, extent=extent) cb = plt.colorbar(im) - plt.setp(cb.ax.get_yticklabels(), visible=False) + plt.setp(cb.get_yticklabels(), visible=False) def demo_locatable_axes_hard(fig): diff --git a/examples/images_contours_and_fields/contour_demo.py b/examples/images_contours_and_fields/contour_demo.py index 4c98d4c8bc4c..a43e3de1f6c4 100644 --- a/examples/images_contours_and_fields/contour_demo.py +++ b/examples/images_contours_and_fields/contour_demo.py @@ -103,8 +103,8 @@ # so let's improve its position. l, b, w, h = ax.get_position().bounds -ll, bb, ww, hh = CB.ax.get_position().bounds -CB.ax.set_position([ll, b + 0.1*h, ww, h*0.8]) +ll, bb, ww, hh = CB.get_position().bounds +CB.set_position([ll, b + 0.1*h, ww, h*0.8]) plt.show() diff --git a/examples/images_contours_and_fields/contourf_demo.py b/examples/images_contours_and_fields/contourf_demo.py index 699eccd0ffa3..f75c192f6624 100644 --- a/examples/images_contours_and_fields/contourf_demo.py +++ b/examples/images_contours_and_fields/contourf_demo.py @@ -54,7 +54,7 @@ # Make a colorbar for the ContourSet returned by the contourf call. cbar = fig1.colorbar(CS) -cbar.ax.set_ylabel('verbosity coefficient') +cbar.set_ylabel('verbosity coefficient') # Add the contour line levels to the colorbar cbar.add_lines(CS2) diff --git a/examples/images_contours_and_fields/image_annotated_heatmap.py b/examples/images_contours_and_fields/image_annotated_heatmap.py index 301c180cb783..691d1f761686 100644 --- a/examples/images_contours_and_fields/image_annotated_heatmap.py +++ b/examples/images_contours_and_fields/image_annotated_heatmap.py @@ -131,7 +131,7 @@ def heatmap(data, row_labels, col_labels, ax=None, # Create colorbar cbar = ax.figure.colorbar(im, ax=ax, **cbar_kw) - cbar.ax.set_ylabel(cbarlabel, rotation=-90, va="bottom") + cbar.set_ylabel(cbarlabel, rotation=-90, va="bottom") # We want to show all ticks... ax.set_xticks(np.arange(data.shape[1])) diff --git a/examples/ticks_and_spines/colorbar_tick_labelling_demo.py b/examples/ticks_and_spines/colorbar_tick_labelling_demo.py index b0d56943fe30..28dcb609f768 100644 --- a/examples/ticks_and_spines/colorbar_tick_labelling_demo.py +++ b/examples/ticks_and_spines/colorbar_tick_labelling_demo.py @@ -29,7 +29,7 @@ # Add colorbar, make sure to specify tick locations to match desired ticklabels cbar = fig.colorbar(cax, ticks=[-1, 0, 1]) -cbar.ax.set_yticklabels(['< -1', '0', '> 1']) # vertically oriented colorbar +cbar.set_yticklabels(['< -1', '0', '> 1']) # vertically oriented colorbar ############################################################################### # Make plot with horizontal colorbar @@ -42,6 +42,6 @@ ax.set_title('Gaussian noise with horizontal colorbar') cbar = fig.colorbar(cax, ticks=[-1, 0, 1], orientation='horizontal') -cbar.ax.set_xticklabels(['Low', 'Medium', 'High']) # horizontal colorbar +cbar.set_xticklabels(['Low', 'Medium', 'High']) # horizontal colorbar plt.show() diff --git a/lib/matplotlib/colorbar.py b/lib/matplotlib/colorbar.py index 7a6b21a21580..675ecfc3ae12 100644 --- a/lib/matplotlib/colorbar.py +++ b/lib/matplotlib/colorbar.py @@ -1,6 +1,6 @@ """ Colorbars are a visualization of the mapping from scalar values to colors. -In Matplotlib they are drawn into a dedicated `~.axes.Axes`. +In Matplotlib they are a separate dedicated `~.axes.Axes`. .. note:: Colorbars are typically created through `.Figure.colorbar` or its pyplot @@ -233,7 +233,7 @@ class ColorbarAxes(Axes): over/under colors. Users should not normally instantiate this class, but it is the class - returned by ``cbar = fig.colorbar(im); cax = cbar.ax``. + that the Colorbar inherits from by ``cbar = fig.colorbar(im);``. """ def __init__(self, parent, userax=True): """ @@ -339,8 +339,6 @@ class ColorbarBase(ColorbarAxes): Attributes ---------- - ax : `~matplotlib.axes.Axes` - The `~.axes.Axes` instance in which the colorbar is drawn. lines : list A list of `.LineCollection` (empty if no lines were drawn). dividers : `.LineCollection` @@ -384,7 +382,7 @@ class ColorbarBase(ColorbarAxes): label : str userax : boolean - Whether the user created the axes or not. Default True + Whether the user created the axes or not. Default False """ n_rasterize = 50 # rasterize solids if number of colors >= n_rasterize diff --git a/lib/matplotlib/tests/test_colorbar.py b/lib/matplotlib/tests/test_colorbar.py index c6e5ca0dfef6..6c1a369f0e20 100644 --- a/lib/matplotlib/tests/test_colorbar.py +++ b/lib/matplotlib/tests/test_colorbar.py @@ -284,7 +284,7 @@ def test_colorbar_ticks(): colors = ['r', 'g', 'b', 'c'] cs = ax.contourf(X, Y, Z, clevs, colors=colors, extend='neither') cbar = fig.colorbar(cs, ax=ax, orientation='horizontal', ticks=clevs) - assert len(cbar.ax.xaxis.get_ticklocs()) == len(clevs) + assert len(cbar.xaxis.get_ticklocs()) == len(clevs) def test_colorbar_minorticks_on_off(): @@ -301,17 +301,17 @@ def test_colorbar_minorticks_on_off(): # testing after minorticks_on() cbar.minorticks_on() np.testing.assert_almost_equal( - cbar.ax.yaxis.get_minorticklocs(), + cbar.yaxis.get_minorticklocs(), [-2.2, -1.8, -1.6, -1.4, -1.2, -0.8, -0.6, -0.4, -0.2, 0.2, 0.4, 0.6, 0.8, 1.2, 1.4, 1.6, 1.8, 2.2, 2.4, 2.6, 2.8, 3.2]) # testing after minorticks_off() cbar.minorticks_off() - np.testing.assert_almost_equal(cbar.ax.yaxis.get_minorticklocs(), []) + np.testing.assert_almost_equal(cbar.yaxis.get_minorticklocs(), []) im.set_clim(vmin=-1.2, vmax=1.2) cbar.minorticks_on() np.testing.assert_almost_equal( - cbar.ax.yaxis.get_minorticklocs(), + cbar.yaxis.get_minorticklocs(), [-1.1, -0.9, -0.8, -0.7, -0.6, -0.4, -0.3, -0.2, -0.1, 0.1, 0.2, 0.3, 0.4, 0.6, 0.7, 0.8, 0.9, 1.1, 1.2, 1.3]) @@ -322,20 +322,20 @@ def test_colorbar_minorticks_on_off(): im = ax.pcolormesh(data, norm=LogNorm()) cbar = fig.colorbar(im) fig.canvas.draw() - default_minorticklocks = cbar.ax.yaxis.get_minorticklocs() + default_minorticklocks = cbar.yaxis.get_minorticklocs() # test that minorticks turn off for LogNorm cbar.minorticks_off() - np.testing.assert_equal(cbar.ax.yaxis.get_minorticklocs(), []) + np.testing.assert_equal(cbar.yaxis.get_minorticklocs(), []) # test that minorticks turn back on for LogNorm cbar.minorticks_on() - np.testing.assert_equal(cbar.ax.yaxis.get_minorticklocs(), + np.testing.assert_equal(cbar.yaxis.get_minorticklocs(), default_minorticklocks) # test issue #13339: minorticks for LogNorm should stay off cbar.minorticks_off() cbar.set_ticks([3, 5, 7, 9]) - np.testing.assert_equal(cbar.ax.yaxis.get_minorticklocs(), []) + np.testing.assert_equal(cbar.yaxis.get_minorticklocs(), []) def test_cbar_minorticks_for_rc_xyminortickvisible(): @@ -358,12 +358,12 @@ def test_cbar_minorticks_for_rc_xyminortickvisible(): im = ax.pcolormesh([[1, 2]], vmin=vmin, vmax=vmax) cbar = fig.colorbar(im, extend='both', orientation='vertical') - assert cbar.ax.yaxis.get_minorticklocs()[0] >= vmin - assert cbar.ax.yaxis.get_minorticklocs()[-1] <= vmax + assert cbar.yaxis.get_minorticklocs()[0] >= vmin + assert cbar.yaxis.get_minorticklocs()[-1] <= vmax cbar = fig.colorbar(im, extend='both', orientation='horizontal') - assert cbar.ax.xaxis.get_minorticklocs()[0] >= vmin - assert cbar.ax.xaxis.get_minorticklocs()[-1] <= vmax + assert cbar.xaxis.get_minorticklocs()[0] >= vmin + assert cbar.xaxis.get_minorticklocs()[-1] <= vmax def test_colorbar_autoticks(): @@ -384,7 +384,7 @@ def test_colorbar_autoticks(): cbar2 = fig.colorbar(pcm, ax=ax[1], extend='both', orientation='vertical', shrink=0.4) # note only -10 to 10 are visible, - np.testing.assert_almost_equal(cbar.ax.yaxis.get_ticklocs(), + np.testing.assert_almost_equal(cbar.yaxis.get_ticklocs(), np.arange(-15, 16, 5)) # note only -10 to 10 are visible np.testing.assert_almost_equal(cbar2.ax.yaxis.get_ticklocs(), @@ -408,7 +408,7 @@ def test_colorbar_autotickslog(): cbar2 = fig.colorbar(pcm, ax=ax[1], extend='both', orientation='vertical', shrink=0.4) # note only -12 to +12 are visible - np.testing.assert_almost_equal(cbar.ax.yaxis.get_ticklocs(), + np.testing.assert_almost_equal(cbar.yaxis.get_ticklocs(), 10**np.arange(-16., 16.2, 4.)) # note only -24 to +24 are visible np.testing.assert_almost_equal(cbar2.ax.yaxis.get_ticklocs(), @@ -488,7 +488,7 @@ def test_colorbar_log_minortick_labels(): pcm = ax.imshow([[10000, 50000]], norm=LogNorm()) cb = fig.colorbar(pcm) fig.canvas.draw() - lb = [l.get_text() for l in cb.ax.yaxis.get_ticklabels(which='both')] + lb = [l.get_text() for l in cb.yaxis.get_ticklabels(which='both')] expected = [r'$\mathdefault{10^{4}}$', r'$\mathdefault{2\times10^{4}}$', r'$\mathdefault{3\times10^{4}}$', @@ -504,7 +504,7 @@ def test_colorbar_renorm(): fig, ax = plt.subplots() im = ax.imshow(z) cbar = fig.colorbar(im) - np.testing.assert_allclose(cbar.ax.yaxis.get_majorticklocs(), + np.testing.assert_allclose(cbar.yaxis.get_majorticklocs(), np.arange(0, 120000.1, 20000)) cbar.set_ticks([1, 2, 3]) @@ -512,13 +512,13 @@ def test_colorbar_renorm(): norm = LogNorm(z.min(), z.max()) im.set_norm(norm) - np.testing.assert_allclose(cbar.ax.yaxis.get_majorticklocs(), + np.testing.assert_allclose(cbar.yaxis.get_majorticklocs(), np.logspace(-10, 7, 18)) # note that set_norm removes the FixedLocator... assert np.isclose(cbar.vmin, z.min()) cbar.set_ticks([1, 2, 3]) assert isinstance(cbar.locator, FixedLocator) - np.testing.assert_allclose(cbar.ax.yaxis.get_majorticklocs(), + np.testing.assert_allclose(cbar.yaxis.get_majorticklocs(), [1.0, 2.0, 3.0]) norm = LogNorm(z.min() * 1000, z.max() * 1000) @@ -536,18 +536,18 @@ def test_colorbar_format(): im = ax.imshow(z) cbar = fig.colorbar(im, format='%4.2e') fig.canvas.draw() - assert cbar.ax.yaxis.get_ticklabels()[4].get_text() == '8.00e+04' + assert cbar.yaxis.get_ticklabels()[4].get_text() == '8.00e+04' # make sure that if we change the clim of the mappable that the # formatting is *not* lost: im.set_clim([4, 200]) fig.canvas.draw() - assert cbar.ax.yaxis.get_ticklabels()[4].get_text() == '2.00e+02' + assert cbar.yaxis.get_ticklabels()[4].get_text() == '2.00e+02' # but if we change the norm: im.set_norm(LogNorm(vmin=0.1, vmax=10)) fig.canvas.draw() - assert (cbar.ax.yaxis.get_ticklabels()[0].get_text() == + assert (cbar.yaxis.get_ticklabels()[0].get_text() == r'$\mathdefault{10^{-2}}$') @@ -559,12 +559,12 @@ def test_colorbar_scale_reset(): pcm = ax.pcolormesh(z, cmap='RdBu_r', rasterized=True) cbar = fig.colorbar(pcm, ax=ax) cbar.outline.set_edgecolor('red') - assert cbar.ax.yaxis.get_scale() == 'linear' + assert cbar.yaxis.get_scale() == 'linear' pcm.set_norm(LogNorm(vmin=1, vmax=100)) - assert cbar.ax.yaxis.get_scale() == 'log' + assert cbar.yaxis.get_scale() == 'log' pcm.set_norm(Normalize(vmin=-20, vmax=20)) - assert cbar.ax.yaxis.get_scale() == 'linear' + assert cbar.yaxis.get_scale() == 'linear' assert cbar.outline.get_edgecolor() == mcolors.to_rgba('red') @@ -583,7 +583,7 @@ def test_colorbar_inverted_ticks(): pc = ax.pcolormesh(10**np.arange(1, 5).reshape(2, 2), norm=LogNorm()) cbar = fig.colorbar(pc, ax=ax, extend='both') ticks = cbar.get_ticks() - cbar.ax.invert_yaxis() + cbar.invert_yaxis() np.testing.assert_allclose(ticks, cbar.get_ticks()) ax = axs[1] @@ -592,7 +592,7 @@ def test_colorbar_inverted_ticks(): cbar.minorticks_on() ticks = cbar.get_ticks() minorticks = cbar.get_ticks(minor=True) - cbar.ax.invert_yaxis() + cbar.invert_yaxis() np.testing.assert_allclose(ticks, cbar.get_ticks()) np.testing.assert_allclose(minorticks, cbar.get_ticks(minor=True)) @@ -603,7 +603,7 @@ def test_extend_colorbar_customnorm(): fig, (ax0, ax1) = plt.subplots(2, 1) pcm = ax0.pcolormesh([[0]], norm=TwoSlopeNorm(vcenter=0., vmin=-2, vmax=1)) cb = fig.colorbar(pcm, ax=ax0, extend='both') - np.testing.assert_allclose(cb.ax.get_position().extents, + np.testing.assert_allclose(cb.get_position().extents, [0.78375, 0.536364, 0.796147, 0.9], rtol=2e-3) @@ -623,11 +623,11 @@ def test_colorbar_label(): fig, ax = plt.subplots() im = ax.imshow([[1, 2], [3, 4]]) cbar = fig.colorbar(im, label='cbar') - assert cbar.ax.get_ylabel() == 'cbar' + assert cbar.get_ylabel() == 'cbar' cbar.set_label(None) - assert cbar.ax.get_ylabel() == '' + assert cbar.get_ylabel() == '' cbar.set_label('cbar 2') - assert cbar.ax.get_ylabel() == 'cbar 2' + assert cbar.get_ylabel() == 'cbar 2' cbar2 = fig.colorbar(im, label=None) assert cbar2.ax.get_ylabel() == '' @@ -663,7 +663,7 @@ def test_anchored_cbar_position_using_specgrid(): # the top right corner of one ax is (x1, y1) # p0: the vertical / horizontal position of anchor x0, y0, x1, y1 = ax.get_position().extents - cx0, cy0, cx1, cy1 = cbar.ax.get_position().extents + cx0, cy0, cx1, cy1 = cbar.get_position().extents p0 = (y1 - y0) * anchor_y + y0 np.testing.assert_allclose( @@ -681,7 +681,7 @@ def test_anchored_cbar_position_using_specgrid(): # the top right corner of one ax is (x1, y1) # p0: the vertical / horizontal position of anchor x0, y0, x1, y1 = ax.get_position().extents - cx0, cy0, cx1, cy1 = cbar.ax.get_position().extents + cx0, cy0, cx1, cy1 = cbar.get_position().extents p0 = (y1 - y0) * anchor_y + y0 np.testing.assert_allclose( @@ -701,7 +701,7 @@ def test_anchored_cbar_position_using_specgrid(): # the top right corner of one ax is (x1, y1) # p0: the vertical / horizontal position of anchor x0, y0, x1, y1 = ax.get_position().extents - cx0, cy0, cx1, cy1 = cbar.ax.get_position().extents + cx0, cy0, cx1, cy1 = cbar.get_position().extents p0 = (x1 - x0) * anchor_x + x0 np.testing.assert_allclose( @@ -721,7 +721,7 @@ def test_anchored_cbar_position_using_specgrid(): # the top right corner of one ax is (x1, y1) # p0: the vertical / horizontal position of anchor x0, y0, x1, y1 = ax.get_position().extents - cx0, cy0, cx1, cy1 = cbar.ax.get_position().extents + cx0, cy0, cx1, cy1 = cbar.get_position().extents p0 = (x1 - x0) * anchor_x + x0 np.testing.assert_allclose( @@ -735,16 +735,16 @@ def test_colorbar_change_lim_scale(): fig, ax = plt.subplots(1, 2, constrained_layout=True) pc = ax[0].pcolormesh(np.arange(100).reshape(10, 10)+1) cb = fig.colorbar(pc, ax=ax[0], extend='both') - cb.ax.set_yscale('log') + cb.set_yscale('log') pc = ax[1].pcolormesh(np.arange(100).reshape(10, 10)+1) cb = fig.colorbar(pc, ax=ax[1], extend='both') - cb.ax.set_ylim([20, 90]) + cb.set_ylim([20, 90]) @check_figures_equal(extensions=["png"]) def test_axes_handles_same_functions(fig_ref, fig_test): - # prove that cax and cb.ax are functionally the same + # prove that cax and cb are functionally the same for nn, fig in enumerate([fig_ref, fig_test]): ax = fig.add_subplot() pc = ax.pcolormesh(np.ones(300).reshape(10, 30)) @@ -753,7 +753,7 @@ def test_axes_handles_same_functions(fig_ref, fig_test): if nn == 0: caxx = cax else: - caxx = cb.ax + caxx = cb caxx.set_yticks(np.arange(20)) caxx.set_yscale('log') caxx.set_position([0.92, 0.1, 0.02, 0.7]) diff --git a/lib/matplotlib/tests/test_constrainedlayout.py b/lib/matplotlib/tests/test_constrainedlayout.py index cfb8ca73980a..da8587bf61e9 100644 --- a/lib/matplotlib/tests/test_constrainedlayout.py +++ b/lib/matplotlib/tests/test_constrainedlayout.py @@ -422,10 +422,10 @@ def test_colorbar_align(): cb = fig.colorbar(pc, ax=ax, location=location, shrink=0.6, pad=0.04) cbs += [cb] - cb.ax.tick_params(direction='in') + cb.tick_params(direction='in') if nn != 1: - cb.ax.xaxis.set_ticks([]) - cb.ax.yaxis.set_ticks([]) + cb.xaxis.set_ticks([]) + cb.yaxis.set_ticks([]) ax.set_xticklabels('') ax.set_yticklabels('') fig.set_constrained_layout_pads(w_pad=4 / 72, h_pad=4 / 72, hspace=0.1, diff --git a/lib/matplotlib/tests/test_contour.py b/lib/matplotlib/tests/test_contour.py index 73a7a3d6ae56..2e3cd2f911b8 100644 --- a/lib/matplotlib/tests/test_contour.py +++ b/lib/matplotlib/tests/test_contour.py @@ -334,9 +334,9 @@ def test_contourf_log_extension(): norm=LogNorm(vmin=levels.min(), vmax=levels.max()), extend='both') cb = plt.colorbar(c1, ax=ax1) - assert cb.ax.get_ylim() == (1e-8, 1e10) + assert cb.get_ylim() == (1e-8, 1e10) cb = plt.colorbar(c2, ax=ax2) - assert cb.ax.get_ylim() == (1e-4, 1e6) + assert cb.get_ylim() == (1e-4, 1e6) cb = plt.colorbar(c3, ax=ax3) @@ -356,7 +356,7 @@ def test_contour_addlines(): cont = ax.contour(X+1000) cb = fig.colorbar(pcm) cb.add_lines(cont) - assert_array_almost_equal(cb.ax.get_ylim(), [114.3091, 9972.30735], 3) + assert_array_almost_equal(cb.get_ylim(), [114.3091, 9972.30735], 3) @image_comparison(baseline_images=['contour_uneven'], diff --git a/lib/mpl_toolkits/tests/test_mplot3d.py b/lib/mpl_toolkits/tests/test_mplot3d.py index edb5c978bb49..9031618233fa 100644 --- a/lib/mpl_toolkits/tests/test_mplot3d.py +++ b/lib/mpl_toolkits/tests/test_mplot3d.py @@ -1346,7 +1346,7 @@ def test_colorbar_pos(): fig.canvas.draw() # check that actually on the bottom - assert cbar.ax.get_position().extents[1] < 0.2 + assert cbar.get_position().extents[1] < 0.2 def test_shared_axes_retick(): From 2f49875f1085ed0cc95bae9f233768f591d5db76 Mon Sep 17 00:00:00 2001 From: Greg Lucas Date: Wed, 2 Jun 2021 07:08:56 -0600 Subject: [PATCH 4/5] DOC: Adding a release note for the Colorbar behavior change --- doc/api/next_api_changes/behavior/20XXX-GL.rst | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 doc/api/next_api_changes/behavior/20XXX-GL.rst diff --git a/doc/api/next_api_changes/behavior/20XXX-GL.rst b/doc/api/next_api_changes/behavior/20XXX-GL.rst new file mode 100644 index 000000000000..e513424dadd8 --- /dev/null +++ b/doc/api/next_api_changes/behavior/20XXX-GL.rst @@ -0,0 +1,16 @@ +Colorbars are now an instance of Axes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The :class:`.colorbar.Colorbar` class now inherits from `.axes.Axes`, +meaning that all of the standard methods of ``Axes`` can be used +directly on the colorbar object itself rather than having to access the +``ax`` attribute. For example, :: + + cbar.set_yticks() + +rather than :: + + cbar.ax.set_yticks() + +We are leaving the ``cbar.ax`` attribute in place as a pass-through for now, +which just maps back to the colorbar object. From 895776b07a239c6af1f3cc2ec4fefd2fb0136e32 Mon Sep 17 00:00:00 2001 From: Greg Lucas Date: Fri, 11 Jun 2021 06:56:48 -0600 Subject: [PATCH 5/5] DOC: Fixing old doc cross-reference --- doc/api/prev_api_changes/api_changes_2.1.0.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/api/prev_api_changes/api_changes_2.1.0.rst b/doc/api/prev_api_changes/api_changes_2.1.0.rst index 9673d24c719f..fa434761163d 100644 --- a/doc/api/prev_api_changes/api_changes_2.1.0.rst +++ b/doc/api/prev_api_changes/api_changes_2.1.0.rst @@ -422,8 +422,8 @@ The ``shading`` kwarg to `~matplotlib.axes.Axes.pcolor` has been removed. Set ``edgecolors`` appropriately instead. -Functions removed from the `.lines` module -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Functions removed from the `matplotlib.lines` module +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The :mod:`matplotlib.lines` module no longer imports the ``pts_to_prestep``, ``pts_to_midstep`` and ``pts_to_poststep``