From c750a34df6f17d25967e0874e6ac19887571c54e Mon Sep 17 00:00:00 2001 From: Jody Klymak Date: Fri, 28 May 2021 12:08:18 -0700 Subject: [PATCH 1/6] FIX: add cla to colorbar co-author: Greg Lucas --- lib/matplotlib/colorbar.py | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/colorbar.py b/lib/matplotlib/colorbar.py index b1cd08d955a7..4741cc3909e1 100644 --- a/lib/matplotlib/colorbar.py +++ b/lib/matplotlib/colorbar.py @@ -260,7 +260,6 @@ def __init__(self, parent, userax=True): parent.remove() else: outer_ax = parent - inner_ax = outer_ax.inset_axes([0, 0, 1, 1]) self.__dict__.update(inner_ax.__dict__) @@ -285,6 +284,32 @@ def _set_inner_bounds(self, bounds): self.inner_ax._axes_locator = _TransformedBoundsLocator( bounds, self.outer_ax.transAxes) + def cla(self): + """ + Reset the colorbar axes to be empty. + """ + # need to low-level manipulate the stacks because we + # are just swapping places here. We don't need to + # set transforms etc... + self.inner_ax.cla() + self.outer_ax.cla() + self.figure._axstack.add(self) + self.figure._axstack.remove(self.outer_ax) + self.figure._localaxes.add(self) + self.figure._localaxes.remove(self.outer_ax) + + self.__dict__.update(self.outer_ax.__dict__) + self.inner_ax.remove() + del self.inner_ax + del self.outer_ax + + self.xaxis.set_visible(True) + self.yaxis.set_visible(True) + for spine in self.spines.values(): + spine.set_visible(True) + + self.set_facecolor(mpl.rcParams['axes.facecolor']) + class _ColorbarSpine(mspines.Spine): def __init__(self, axes): @@ -1250,7 +1275,12 @@ def update_normal(self, mappable): self.norm = mappable.norm self._reset_locator_formatter_scale() - self.draw_all() + try: + self.draw_all() + except AttributeError: + # update_normal sometimes is called when it shouldn't be.. + pass + if isinstance(self.mappable, contour.ContourSet): CS = self.mappable if not CS.filled: From 549e0e7f550bc8614ed9bec3bdbc8c60c55e09ac Mon Sep 17 00:00:00 2001 From: Jody Klymak Date: Fri, 28 May 2021 16:14:03 -0700 Subject: [PATCH 2/6] TST: add test --- lib/matplotlib/colorbar.py | 3 +-- lib/matplotlib/tests/test_colorbar.py | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/colorbar.py b/lib/matplotlib/colorbar.py index 4741cc3909e1..323305ff6c1d 100644 --- a/lib/matplotlib/colorbar.py +++ b/lib/matplotlib/colorbar.py @@ -900,8 +900,7 @@ 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.ax.remove() def _ticker(self, locator, formatter): """ diff --git a/lib/matplotlib/tests/test_colorbar.py b/lib/matplotlib/tests/test_colorbar.py index ac054755c4c1..be84611aca4c 100644 --- a/lib/matplotlib/tests/test_colorbar.py +++ b/lib/matplotlib/tests/test_colorbar.py @@ -759,6 +759,24 @@ def test_axes_handles_same_functions(fig_ref, fig_test): caxx.set_position([0.92, 0.1, 0.02, 0.7]) +@check_figures_equal(extensions=["png"]) +def test_colorbar_reuse_axes(fig_ref, fig_test): + ax = fig_ref.add_subplot() + pc = ax.imshow(np.arange(100).reshape(10, 10)) + cb = fig_ref.colorbar(pc) + cb2 = fig_ref.colorbar(pc, extend='both') + + ax = fig_test.add_subplot() + pc = ax.imshow(np.arange(100).reshape(10, 10)) + cb = fig_test.colorbar(pc, extend='both') + cb2 = fig_test.colorbar(pc) + # Clear and re-use the same colorbar axes + cb.ax.cla() + cb2.ax.cla() + cb = fig_test.colorbar(pc, cax=cb.ax) + cb2 = fig_test.colorbar(pc, cax=cb2.ax, extend='both') + + def test_inset_colorbar_layout(): fig, ax = plt.subplots(constrained_layout=True, figsize=(3, 6)) pc = ax.imshow(np.arange(100).reshape(10, 10)) From a0b05e45dea546895fe471f4976dc0a83bb3dcdb Mon Sep 17 00:00:00 2001 From: Jody Klymak Date: Fri, 28 May 2021 16:50:34 -0700 Subject: [PATCH 3/6] fix --- lib/matplotlib/colorbar.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/colorbar.py b/lib/matplotlib/colorbar.py index 323305ff6c1d..4741cc3909e1 100644 --- a/lib/matplotlib/colorbar.py +++ b/lib/matplotlib/colorbar.py @@ -900,7 +900,8 @@ def set_alpha(self, alpha): def remove(self): """Remove this colorbar from the figure.""" - self.ax.remove() + self.ax.inner_ax.remove() + self.ax.outer_ax.remove() def _ticker(self, locator, formatter): """ From 1675c3aaf49014e19140b9597d764c07db4ef567 Mon Sep 17 00:00:00 2001 From: Jody Klymak Date: Mon, 31 May 2021 18:24:58 -0700 Subject: [PATCH 4/6] ?? --- lib/matplotlib/colorbar.py | 21 ++++++++++++++------- lib/matplotlib/tests/test_colorbar.py | 6 +++++- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/lib/matplotlib/colorbar.py b/lib/matplotlib/colorbar.py index 4741cc3909e1..b67b143375ba 100644 --- a/lib/matplotlib/colorbar.py +++ b/lib/matplotlib/colorbar.py @@ -235,7 +235,7 @@ class ColorbarAxes(Axes): Users should not normally instantiate this class, but it is the class returned by ``cbar = fig.colorbar(im); cax = cbar.ax``. """ - def __init__(self, parent, userax=True): + def __init__(self, parent, userax=True, colorbar=None): """ Parameters ---------- @@ -243,6 +243,8 @@ def __init__(self, parent, userax=True): Axes that specifies the position of the colorbar. userax : boolean True if the user passed `.Figure.colorbar` the axes manually. + colorbar: ColorbarBase + colorbar that this axes is contained in. """ if userax: @@ -271,6 +273,7 @@ def __init__(self, parent, userax=True): self.outer_ax.tick_params = self.inner_ax.tick_params self.outer_ax.set_xticks = self.inner_ax.set_xticks self.outer_ax.set_yticks = self.inner_ax.set_yticks + self.colorbar = colorbar for attr in ["get_position", "set_position", "set_aspect"]: setattr(self, attr, getattr(self.outer_ax, attr)) if userax: @@ -291,6 +294,11 @@ def cla(self): # need to low-level manipulate the stacks because we # are just swapping places here. We don't need to # set transforms etc... + print('id', self.colorbar.mappable.colorbar_cid) + self.colorbar.mappable.callbacksSM.disconnect(self.colorbar.mappable.colorbar_cid) + #self.colorbar.mappable.colorbar = None + #self.colorbar.mappable.colorbar_cid = None + self.inner_ax.cla() self.outer_ax.cla() self.figure._axstack.add(self) @@ -443,7 +451,7 @@ 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) + ax = ColorbarAxes(ax, userax=userax, colorbar=self) self.ax = ax ax.set(navigate=False) @@ -1275,11 +1283,10 @@ def update_normal(self, mappable): self.norm = mappable.norm self._reset_locator_formatter_scale() - try: - self.draw_all() - except AttributeError: - # update_normal sometimes is called when it shouldn't be.. - pass + self.draw_all() +# except AttributeError: +# # update_normal sometimes is called when it shouldn't be.. +# pass if isinstance(self.mappable, contour.ContourSet): CS = self.mappable diff --git a/lib/matplotlib/tests/test_colorbar.py b/lib/matplotlib/tests/test_colorbar.py index be84611aca4c..6cdb03a9d37c 100644 --- a/lib/matplotlib/tests/test_colorbar.py +++ b/lib/matplotlib/tests/test_colorbar.py @@ -771,10 +771,14 @@ def test_colorbar_reuse_axes(fig_ref, fig_test): cb = fig_test.colorbar(pc, extend='both') cb2 = fig_test.colorbar(pc) # Clear and re-use the same colorbar axes + print('HERE1:', pc, pc.callbacksSM.callbacks) cb.ax.cla() + print('HERE2:', pc, pc.callbacksSM.callbacks) cb2.ax.cla() + print('HERE3:', pc, pc.callbacksSM.callbacks) + cb = fig_test.colorbar(pc, cax=cb.ax) - cb2 = fig_test.colorbar(pc, cax=cb2.ax, extend='both') + #cb2 = fig_test.colorbar(pc, cax=cb2.ax, extend='both') def test_inset_colorbar_layout(): From f0b91dc4aadb9c22afc97a1b69eb7e7bbfe5b54f Mon Sep 17 00:00:00 2001 From: Jody Klymak Date: Tue, 1 Jun 2021 07:32:44 -0700 Subject: [PATCH 5/6] FIX: make scalarmappable callbacks know all colorbars --- lib/matplotlib/colorbar.py | 28 ++++++++++++++++++++++----- lib/matplotlib/tests/test_colorbar.py | 5 +---- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/lib/matplotlib/colorbar.py b/lib/matplotlib/colorbar.py index b67b143375ba..21466cd9bdb6 100644 --- a/lib/matplotlib/colorbar.py +++ b/lib/matplotlib/colorbar.py @@ -294,8 +294,14 @@ def cla(self): # need to low-level manipulate the stacks because we # are just swapping places here. We don't need to # set transforms etc... + print('id', self.colorbar.mappable.colorbar_cid) - self.colorbar.mappable.callbacksSM.disconnect(self.colorbar.mappable.colorbar_cid) + + if isinstance(self.colorbar.mappable.colorbar_cid, dict): + cid = self.colorbar.mappable.colorbar_cid[self.colorbar] + else: + cid = self.colorbar.mappable.colorbar_cid + self.colorbar.mappable.callbacksSM.disconnect(cid) #self.colorbar.mappable.colorbar = None #self.colorbar.mappable.colorbar_cid = None @@ -1228,9 +1234,17 @@ def __init__(self, ax, mappable, **kwargs): _add_disjoint_kwargs(kwargs, alpha=mappable.get_alpha()) super().__init__(ax, **kwargs) - mappable.colorbar = self - mappable.colorbar_cid = mappable.callbacksSM.connect( - 'changed', self.update_normal) + cid = mappable.callbacksSM.connect('changed', self.update_normal) + if mappable.colorbar is None: + mappable.colorbar = self + mappable.colorbar_cid = cid + elif not isinstance(mappable.colorbar, list): + old = mappable.colorbar_cid + mappable.colorbar_cid = {mappable.colorbar: old, self: cid} + mappable.colorbar = [mappable.colorbar, self] + else: + mappable.colorbar += [self] + mappable.colorbar_cid[self] = cid @_api.deprecated("3.3", alternative="update_normal") def on_mappable_changed(self, mappable): @@ -1343,7 +1357,11 @@ def remove(self): gridspec is restored. """ super().remove() - self.mappable.callbacksSM.disconnect(self.mappable.colorbar_cid) + if isinstance(self.mappable.colorbar_cid, dict): + cid = self.mappable.colorbar_cid[self] + else: + cid = self.mappable.colorbar_cid + self.mappable.callbacksSM.disconnect(cid) self.mappable.colorbar = None self.mappable.colorbar_cid = None diff --git a/lib/matplotlib/tests/test_colorbar.py b/lib/matplotlib/tests/test_colorbar.py index 6cdb03a9d37c..e4b877eac83a 100644 --- a/lib/matplotlib/tests/test_colorbar.py +++ b/lib/matplotlib/tests/test_colorbar.py @@ -771,14 +771,11 @@ def test_colorbar_reuse_axes(fig_ref, fig_test): cb = fig_test.colorbar(pc, extend='both') cb2 = fig_test.colorbar(pc) # Clear and re-use the same colorbar axes - print('HERE1:', pc, pc.callbacksSM.callbacks) cb.ax.cla() - print('HERE2:', pc, pc.callbacksSM.callbacks) cb2.ax.cla() - print('HERE3:', pc, pc.callbacksSM.callbacks) cb = fig_test.colorbar(pc, cax=cb.ax) - #cb2 = fig_test.colorbar(pc, cax=cb2.ax, extend='both') + cb2 = fig_test.colorbar(pc, cax=cb2.ax, extend='both') def test_inset_colorbar_layout(): From 8ae6b42cf6be5030903e9173de4faa8e7281cf51 Mon Sep 17 00:00:00 2001 From: Jody Klymak Date: Tue, 1 Jun 2021 09:11:16 -0700 Subject: [PATCH 6/6] FL8 --- lib/matplotlib/colorbar.py | 4 +--- lib/matplotlib/tests/test_colorbar.py | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/matplotlib/colorbar.py b/lib/matplotlib/colorbar.py index 21466cd9bdb6..cc5c236f7d29 100644 --- a/lib/matplotlib/colorbar.py +++ b/lib/matplotlib/colorbar.py @@ -244,7 +244,7 @@ def __init__(self, parent, userax=True, colorbar=None): userax : boolean True if the user passed `.Figure.colorbar` the axes manually. colorbar: ColorbarBase - colorbar that this axes is contained in. + colorbar that this axes is contained in. """ if userax: @@ -302,8 +302,6 @@ def cla(self): else: cid = self.colorbar.mappable.colorbar_cid self.colorbar.mappable.callbacksSM.disconnect(cid) - #self.colorbar.mappable.colorbar = None - #self.colorbar.mappable.colorbar_cid = None self.inner_ax.cla() self.outer_ax.cla() diff --git a/lib/matplotlib/tests/test_colorbar.py b/lib/matplotlib/tests/test_colorbar.py index e4b877eac83a..be84611aca4c 100644 --- a/lib/matplotlib/tests/test_colorbar.py +++ b/lib/matplotlib/tests/test_colorbar.py @@ -773,7 +773,6 @@ def test_colorbar_reuse_axes(fig_ref, fig_test): # Clear and re-use the same colorbar axes cb.ax.cla() cb2.ax.cla() - cb = fig_test.colorbar(pc, cax=cb.ax) cb2 = fig_test.colorbar(pc, cax=cb2.ax, extend='both')