From 6d3dcf2e55f910897032456d4fc85f0b4110e7ca Mon Sep 17 00:00:00 2001 From: cvanelteren Date: Tue, 14 Jan 2025 17:15:49 +0100 Subject: [PATCH 01/17] fixed axis sharing not working properly --- ultraplot/axes/cartesian.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ultraplot/axes/cartesian.py b/ultraplot/axes/cartesian.py index 7ce4f3b6c..9091adcd7 100644 --- a/ultraplot/axes/cartesian.py +++ b/ultraplot/axes/cartesian.py @@ -576,7 +576,7 @@ def _sharex_limits(self, sharex): if ax1.get_autoscalex_on() and not ax2.get_autoscalex_on(): ax1.set_xlim(ax2.get_xlim()) # non-default limits # Copy non-default locators and formatters - self.get_shared_x_axes().joined(self, sharex) # share limit/scale changes + self.sharex(sharex) # get_shared_x_axes now returns an immutable object if sharex.xaxis.isDefault_majloc and not self.xaxis.isDefault_majloc: sharex.xaxis.set_major_locator(self.xaxis.get_major_locator()) if sharex.xaxis.isDefault_minloc and not self.xaxis.isDefault_minloc: @@ -598,7 +598,7 @@ def _sharey_limits(self, sharey): ax1.set_yscale(ax2.get_yscale()) if ax1.get_autoscaley_on() and not ax2.get_autoscaley_on(): ax1.set_ylim(ax2.get_ylim()) - self.get_shared_y_axes().joined(self, sharey) # share limit/scale changes + self.sharey(sharey) # get_shared_y_axes now returns an immutable object if sharey.yaxis.isDefault_majloc and not self.yaxis.isDefault_majloc: sharey.yaxis.set_major_locator(self.yaxis.get_major_locator()) if sharey.yaxis.isDefault_minloc and not self.yaxis.isDefault_minloc: From 5cff0de43166bbce4a26fd36727451e0ef48efac Mon Sep 17 00:00:00 2001 From: cvanelteren Date: Tue, 14 Jan 2025 17:52:04 +0100 Subject: [PATCH 02/17] draft unittest --- ultraplot/tests/test_1dplots.py | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/ultraplot/tests/test_1dplots.py b/ultraplot/tests/test_1dplots.py index 60eed6d04..a9955b3d4 100644 --- a/ultraplot/tests/test_1dplots.py +++ b/ultraplot/tests/test_1dplots.py @@ -378,7 +378,6 @@ def test_scatter_sizes(): from matplotlib import tri -@pytest.mark.mpl_image_compare @pytest.mark.mpl_image_compare @pytest.mark.parametrize( "x, y, z, triangles, use_triangulation, use_datadict", @@ -427,3 +426,32 @@ def test_triplot_variants(x, y, z, triangles, use_triangulation, use_datadict): ax.triplot(x, y, "ko-") # Without specific triangles return fig + +@pytest.mark.mpl_image_compare +@pytest.mark.parametrize("share", ["limits", "labels"]) +def test_axis_sharing(share): + fig, ax = uplt.subplots(ncols = 2, nrows = 2, share = share) + labels = ["A", "B", "C", "D"] + for idx, axi in enumerate(ax): + axi.scatter(idx, idx) + axi.set_xlabel(labels[idx]) + axi.set_ylabel(labels[idx]) + + # TODO: the labels are handled in a funky way. The plot looks fine but the label are not "shared" that is the labels still exist but they are not visible and instead there are new labels created. Need to figure this out + # test left hand side + if share != "labels": + assert all([i == j for i, j in zip(ax[0].get_xlim(), ax[2].get_xlim())]) + assert all([i == j for i, j in zip(ax[0].get_ylim(), ax[1].get_ylim())]) + assert all([i == j for i, j in zip(ax[1].get_xlim(), ax[3].get_xlim())]) + #elif share == "labels": + # print("--" * 32) + # print(repr(ax.get_xlabel())) + # print(repr(ax.get_ylabel())) + # print("--" * 32) + # # columns shares x label + # assert ax[0].get_xlabel() == ax[2].get_xlabel().strip() + # assert ax[1].get_xlabel() == ax[3].get_xlabel().strip() + # # rows share ylabel + # assert ax[0].get_ylabel() == ax[1].get_ylabel().strip() + # assert ax[2].get_ylabel().strip() == ax[3].get_ylabel().strip() + return fig From 7e59f8167e0d40adc4ab15c267191bc0847a1179 Mon Sep 17 00:00:00 2001 From: cvanelteren Date: Tue, 14 Jan 2025 18:05:13 +0100 Subject: [PATCH 03/17] multi share allowed to circumvent error raised by mpl --- ultraplot/axes/cartesian.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ultraplot/axes/cartesian.py b/ultraplot/axes/cartesian.py index 9091adcd7..13cae09b8 100644 --- a/ultraplot/axes/cartesian.py +++ b/ultraplot/axes/cartesian.py @@ -576,7 +576,10 @@ def _sharex_limits(self, sharex): if ax1.get_autoscalex_on() and not ax2.get_autoscalex_on(): ax1.set_xlim(ax2.get_xlim()) # non-default limits # Copy non-default locators and formatters - self.sharex(sharex) # get_shared_x_axes now returns an immutable object + # self.sharex(sharex) + self._shared_axes["x"].join(self, sharex) + + # self.get_shared_x_axes().joined(self, sharex) # share limit/scale changes if sharex.xaxis.isDefault_majloc and not self.xaxis.isDefault_majloc: sharex.xaxis.set_major_locator(self.xaxis.get_major_locator()) if sharex.xaxis.isDefault_minloc and not self.xaxis.isDefault_minloc: @@ -598,7 +601,8 @@ def _sharey_limits(self, sharey): ax1.set_yscale(ax2.get_yscale()) if ax1.get_autoscaley_on() and not ax2.get_autoscaley_on(): ax1.set_ylim(ax2.get_ylim()) - self.sharey(sharey) # get_shared_y_axes now returns an immutable object + # self.sharey(sharey) # get_shared_y_axes now returns an immutable object + self._shared_axes["y"].join(self, sharey) if sharey.yaxis.isDefault_majloc and not self.yaxis.isDefault_majloc: sharey.yaxis.set_major_locator(self.yaxis.get_major_locator()) if sharey.yaxis.isDefault_minloc and not self.yaxis.isDefault_minloc: From 79a44f4ac1ad324b6e5f9ede6e9bb8d8c2b4f88c Mon Sep 17 00:00:00 2001 From: cvanelteren Date: Tue, 14 Jan 2025 18:06:09 +0100 Subject: [PATCH 04/17] removed comments --- ultraplot/axes/cartesian.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ultraplot/axes/cartesian.py b/ultraplot/axes/cartesian.py index 13cae09b8..b0ff0c62a 100644 --- a/ultraplot/axes/cartesian.py +++ b/ultraplot/axes/cartesian.py @@ -576,10 +576,7 @@ def _sharex_limits(self, sharex): if ax1.get_autoscalex_on() and not ax2.get_autoscalex_on(): ax1.set_xlim(ax2.get_xlim()) # non-default limits # Copy non-default locators and formatters - # self.sharex(sharex) self._shared_axes["x"].join(self, sharex) - - # self.get_shared_x_axes().joined(self, sharex) # share limit/scale changes if sharex.xaxis.isDefault_majloc and not self.xaxis.isDefault_majloc: sharex.xaxis.set_major_locator(self.xaxis.get_major_locator()) if sharex.xaxis.isDefault_minloc and not self.xaxis.isDefault_minloc: @@ -601,7 +598,6 @@ def _sharey_limits(self, sharey): ax1.set_yscale(ax2.get_yscale()) if ax1.get_autoscaley_on() and not ax2.get_autoscaley_on(): ax1.set_ylim(ax2.get_ylim()) - # self.sharey(sharey) # get_shared_y_axes now returns an immutable object self._shared_axes["y"].join(self, sharey) if sharey.yaxis.isDefault_majloc and not self.yaxis.isDefault_majloc: sharey.yaxis.set_major_locator(self.yaxis.get_major_locator()) From 0011f37a20586eb75b70b63d73596fa02799bcc9 Mon Sep 17 00:00:00 2001 From: cvanelteren Date: Tue, 14 Jan 2025 18:08:04 +0100 Subject: [PATCH 05/17] black formatting --- ultraplot/tests/test_1dplots.py | 29 ----------------------------- ultraplot/tests/test_subplots.py | 30 ++++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 29 deletions(-) diff --git a/ultraplot/tests/test_1dplots.py b/ultraplot/tests/test_1dplots.py index a9955b3d4..338d631e2 100644 --- a/ultraplot/tests/test_1dplots.py +++ b/ultraplot/tests/test_1dplots.py @@ -426,32 +426,3 @@ def test_triplot_variants(x, y, z, triangles, use_triangulation, use_datadict): ax.triplot(x, y, "ko-") # Without specific triangles return fig - -@pytest.mark.mpl_image_compare -@pytest.mark.parametrize("share", ["limits", "labels"]) -def test_axis_sharing(share): - fig, ax = uplt.subplots(ncols = 2, nrows = 2, share = share) - labels = ["A", "B", "C", "D"] - for idx, axi in enumerate(ax): - axi.scatter(idx, idx) - axi.set_xlabel(labels[idx]) - axi.set_ylabel(labels[idx]) - - # TODO: the labels are handled in a funky way. The plot looks fine but the label are not "shared" that is the labels still exist but they are not visible and instead there are new labels created. Need to figure this out - # test left hand side - if share != "labels": - assert all([i == j for i, j in zip(ax[0].get_xlim(), ax[2].get_xlim())]) - assert all([i == j for i, j in zip(ax[0].get_ylim(), ax[1].get_ylim())]) - assert all([i == j for i, j in zip(ax[1].get_xlim(), ax[3].get_xlim())]) - #elif share == "labels": - # print("--" * 32) - # print(repr(ax.get_xlabel())) - # print(repr(ax.get_ylabel())) - # print("--" * 32) - # # columns shares x label - # assert ax[0].get_xlabel() == ax[2].get_xlabel().strip() - # assert ax[1].get_xlabel() == ax[3].get_xlabel().strip() - # # rows share ylabel - # assert ax[0].get_ylabel() == ax[1].get_ylabel().strip() - # assert ax[2].get_ylabel().strip() == ax[3].get_ylabel().strip() - return fig diff --git a/ultraplot/tests/test_subplots.py b/ultraplot/tests/test_subplots.py index 487920ce8..150689189 100644 --- a/ultraplot/tests/test_subplots.py +++ b/ultraplot/tests/test_subplots.py @@ -172,3 +172,33 @@ def test_reference_aspect(): fig.auto_layout() assert np.isclose(refwidth, axs[fig._refnum - 1]._get_size_inches()[0]) return fig + + +@pytest.mark.mpl_image_compare +@pytest.mark.parametrize("share", ["limits", "labels"]) +def test_axis_sharing(share): + fig, ax = uplt.subplots(ncols=2, nrows=2, share=share) + labels = ["A", "B", "C", "D"] + for idx, axi in enumerate(ax): + axi.scatter(idx, idx) + axi.set_xlabel(labels[idx]) + axi.set_ylabel(labels[idx]) + + # TODO: the labels are handled in a funky way. The plot looks fine but the label are not "shared" that is the labels still exist but they are not visible and instead there are new labels created. Need to figure this out. + # test left hand side + if share != "labels": + assert all([i == j for i, j in zip(ax[0].get_xlim(), ax[2].get_xlim())]) + assert all([i == j for i, j in zip(ax[0].get_ylim(), ax[1].get_ylim())]) + assert all([i == j for i, j in zip(ax[1].get_xlim(), ax[3].get_xlim())]) + # elif share == "labels": + # print("--" * 32) + # print(repr(ax.get_xlabel())) + # print(repr(ax.get_ylabel())) + # print("--" * 32) + # # columns shares x label + # assert ax[0].get_xlabel() == ax[2].get_xlabel().strip() + # assert ax[1].get_xlabel() == ax[3].get_xlabel().strip() + # # rows share ylabel + # assert ax[0].get_ylabel() == ax[1].get_ylabel().strip() + # assert ax[2].get_ylabel().strip() == ax[3].get_ylabel().strip() + return fig From efc4b7b95a91d47605debe7a0e6de3e87913dc8a Mon Sep 17 00:00:00 2001 From: cvanelteren Date: Thu, 16 Jan 2025 10:47:22 +0100 Subject: [PATCH 06/17] finalizing tests --- ultraplot/tests/test_subplots.py | 33 ++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/ultraplot/tests/test_subplots.py b/ultraplot/tests/test_subplots.py index 150689189..fb303070d 100644 --- a/ultraplot/tests/test_subplots.py +++ b/ultraplot/tests/test_subplots.py @@ -177,7 +177,7 @@ def test_reference_aspect(): @pytest.mark.mpl_image_compare @pytest.mark.parametrize("share", ["limits", "labels"]) def test_axis_sharing(share): - fig, ax = uplt.subplots(ncols=2, nrows=2, share=share) + fig, ax = uplt.subplots(ncols=2, nrows=2, share=share, span = False) labels = ["A", "B", "C", "D"] for idx, axi in enumerate(ax): axi.scatter(idx, idx) @@ -190,15 +190,24 @@ def test_axis_sharing(share): assert all([i == j for i, j in zip(ax[0].get_xlim(), ax[2].get_xlim())]) assert all([i == j for i, j in zip(ax[0].get_ylim(), ax[1].get_ylim())]) assert all([i == j for i, j in zip(ax[1].get_xlim(), ax[3].get_xlim())]) - # elif share == "labels": - # print("--" * 32) - # print(repr(ax.get_xlabel())) - # print(repr(ax.get_ylabel())) - # print("--" * 32) - # # columns shares x label - # assert ax[0].get_xlabel() == ax[2].get_xlabel().strip() - # assert ax[1].get_xlabel() == ax[3].get_xlabel().strip() - # # rows share ylabel - # assert ax[0].get_ylabel() == ax[1].get_ylabel().strip() - # assert ax[2].get_ylabel().strip() == ax[3].get_ylabel().strip() + elif share == "labels": + ax.draw(fig.canvas.get_renderer()) # forcing a draw to ensure the labels are shared + # columns shares x label; top row should be empty + assert ax[0].xaxis.get_label().get_visible() == False + assert ax[1].xaxis.get_label().get_visible() == False + + assert ax[2].xaxis.get_label().get_visible() == True + assert ax[2].get_xlabel() == 'A' + assert ax[3].xaxis.get_label().get_visible() == True + assert ax[3].get_xlabel() == 'B' + + # rows share ylabel + assert ax[3].yaxis.get_label().get_visible() == False + assert ax[1].yaxis.get_label().get_visible() == False + + assert ax[0].yaxis.get_label().get_visible() == True + assert ax[2].yaxis.get_label().get_visible() == True + assert ax[0].get_ylabel() == 'B' + assert ax[2].get_ylabel() == 'D' + return fig From bfb08e42f6de12c4eff7b10f15c3d14152f457a2 Mon Sep 17 00:00:00 2001 From: cvanelteren Date: Thu, 16 Jan 2025 10:47:34 +0100 Subject: [PATCH 07/17] black formatting --- ultraplot/tests/test_subplots.py | 40 +++++++++++++++++--------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/ultraplot/tests/test_subplots.py b/ultraplot/tests/test_subplots.py index fb303070d..3d45bed66 100644 --- a/ultraplot/tests/test_subplots.py +++ b/ultraplot/tests/test_subplots.py @@ -177,7 +177,7 @@ def test_reference_aspect(): @pytest.mark.mpl_image_compare @pytest.mark.parametrize("share", ["limits", "labels"]) def test_axis_sharing(share): - fig, ax = uplt.subplots(ncols=2, nrows=2, share=share, span = False) + fig, ax = uplt.subplots(ncols=2, nrows=2, share=share, span=False) labels = ["A", "B", "C", "D"] for idx, axi in enumerate(ax): axi.scatter(idx, idx) @@ -191,23 +191,25 @@ def test_axis_sharing(share): assert all([i == j for i, j in zip(ax[0].get_ylim(), ax[1].get_ylim())]) assert all([i == j for i, j in zip(ax[1].get_xlim(), ax[3].get_xlim())]) elif share == "labels": - ax.draw(fig.canvas.get_renderer()) # forcing a draw to ensure the labels are shared - # columns shares x label; top row should be empty - assert ax[0].xaxis.get_label().get_visible() == False - assert ax[1].xaxis.get_label().get_visible() == False - - assert ax[2].xaxis.get_label().get_visible() == True - assert ax[2].get_xlabel() == 'A' - assert ax[3].xaxis.get_label().get_visible() == True - assert ax[3].get_xlabel() == 'B' - - # rows share ylabel - assert ax[3].yaxis.get_label().get_visible() == False - assert ax[1].yaxis.get_label().get_visible() == False - - assert ax[0].yaxis.get_label().get_visible() == True - assert ax[2].yaxis.get_label().get_visible() == True - assert ax[0].get_ylabel() == 'B' - assert ax[2].get_ylabel() == 'D' + ax.draw( + fig.canvas.get_renderer() + ) # forcing a draw to ensure the labels are shared + # columns shares x label; top row should be empty + assert ax[0].xaxis.get_label().get_visible() == False + assert ax[1].xaxis.get_label().get_visible() == False + + assert ax[2].xaxis.get_label().get_visible() == True + assert ax[2].get_xlabel() == "A" + assert ax[3].xaxis.get_label().get_visible() == True + assert ax[3].get_xlabel() == "B" + + # rows share ylabel + assert ax[3].yaxis.get_label().get_visible() == False + assert ax[1].yaxis.get_label().get_visible() == False + + assert ax[0].yaxis.get_label().get_visible() == True + assert ax[2].yaxis.get_label().get_visible() == True + assert ax[0].get_ylabel() == "B" + assert ax[2].get_ylabel() == "D" return fig From 2ac32cf128c963bab5ec6e0442ec3c5f04329e09 Mon Sep 17 00:00:00 2001 From: cvanelteren Date: Thu, 16 Jan 2025 16:17:51 +0100 Subject: [PATCH 08/17] override sharex sharey to handle multiple axis sharing --- ultraplot/axes/base.py | 37 +++++++++++++++++++++++++++++++++++++ ultraplot/axes/cartesian.py | 15 +++++++++++++-- 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/ultraplot/axes/base.py b/ultraplot/axes/base.py index 26bb984f1..17b9d7111 100644 --- a/ultraplot/axes/base.py +++ b/ultraplot/axes/base.py @@ -3224,6 +3224,43 @@ def number(self, num): else: raise ValueError(f"Invalid number {num!r}. Must be integer >=1.") + # Override matplotlib defaults ot handle multiple axis sharing + def sharex(self, other): + return self.share_axis(which="x", other=other) + + def sharey(self, other): + self.share_axis(which="y", other=other) + + # Internal function to share axes + def share_axis(self, which, other): + if not isinstance(other, Axes): + return TypeError( + f"Cannot share axes with {type(other).__name__}.\n" + f"Expected: matplotlib.axes.Axes instance\n" + f"Received: {type(other).__name__}\n" + "Please provide a valid Axes instance to share with." + ) + + self._shared_axes[which].join(self, other) + + # Get axis objects + this_axis = getattr(self, f"{which}axis") + other_axis = getattr(other, f"{which}axis") + + # Set minor ticker + this_axis.minor = other_axis.minor + + # Get and set limits + limits = getattr(other, f"get_{which}lim")() + set_lim = getattr(self, f"set_{which}lim") + get_autoscale = getattr(other, f"get_autoscale{which}_on") + + lim0, lim1 = limits + set_lim(lim0, lim1, emit=False, auto=get_autoscale()) + + # Set scale + this_axis._scale = other_axis._scale + # Apply signature obfuscation after storing previous signature # NOTE: This is needed for __init__ diff --git a/ultraplot/axes/cartesian.py b/ultraplot/axes/cartesian.py index b0ff0c62a..524adc325 100644 --- a/ultraplot/axes/cartesian.py +++ b/ultraplot/axes/cartesian.py @@ -563,6 +563,17 @@ def _is_panel_group_member(self, other): and other._panel_parent is self._panel_parent # other is sibling panel ) + def _safe_share(self, axis_name, other_axis): + if hasattr(self, f"_share{axis_name}"): + setattr(self, f"_share{axis_name}", None) + getattr(self, f"share{axis_name}")(other_axis) + + def share_axis(self, which, other): + if not isinstance(other, plot.PlotAxes): + raise ValueError("other must be an Axes instance") + shared = getattr(self, f"get_shared_{which}_axes")() + self._shared_axes[which].join(self, other) + def _sharex_limits(self, sharex): """ Safely share limits and tickers without resetting things. @@ -576,7 +587,7 @@ def _sharex_limits(self, sharex): if ax1.get_autoscalex_on() and not ax2.get_autoscalex_on(): ax1.set_xlim(ax2.get_xlim()) # non-default limits # Copy non-default locators and formatters - self._shared_axes["x"].join(self, sharex) + self.sharex(sharex) if sharex.xaxis.isDefault_majloc and not self.xaxis.isDefault_majloc: sharex.xaxis.set_major_locator(self.xaxis.get_major_locator()) if sharex.xaxis.isDefault_minloc and not self.xaxis.isDefault_minloc: @@ -598,7 +609,7 @@ def _sharey_limits(self, sharey): ax1.set_yscale(ax2.get_yscale()) if ax1.get_autoscaley_on() and not ax2.get_autoscaley_on(): ax1.set_ylim(ax2.get_ylim()) - self._shared_axes["y"].join(self, sharey) + self.sharey(sharey) if sharey.yaxis.isDefault_majloc and not self.yaxis.isDefault_majloc: sharey.yaxis.set_major_locator(self.yaxis.get_major_locator()) if sharey.yaxis.isDefault_minloc and not self.yaxis.isDefault_minloc: From 5f3338ecd87a167c315f11bb13690b00e85452ba Mon Sep 17 00:00:00 2001 From: cvanelteren Date: Thu, 16 Jan 2025 16:19:31 +0100 Subject: [PATCH 09/17] move sharing to shared --- ultraplot/axes/base.py | 36 ------------------------------------ ultraplot/axes/shared.py | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 36 deletions(-) diff --git a/ultraplot/axes/base.py b/ultraplot/axes/base.py index 17b9d7111..fd5f55191 100644 --- a/ultraplot/axes/base.py +++ b/ultraplot/axes/base.py @@ -3224,42 +3224,6 @@ def number(self, num): else: raise ValueError(f"Invalid number {num!r}. Must be integer >=1.") - # Override matplotlib defaults ot handle multiple axis sharing - def sharex(self, other): - return self.share_axis(which="x", other=other) - - def sharey(self, other): - self.share_axis(which="y", other=other) - - # Internal function to share axes - def share_axis(self, which, other): - if not isinstance(other, Axes): - return TypeError( - f"Cannot share axes with {type(other).__name__}.\n" - f"Expected: matplotlib.axes.Axes instance\n" - f"Received: {type(other).__name__}\n" - "Please provide a valid Axes instance to share with." - ) - - self._shared_axes[which].join(self, other) - - # Get axis objects - this_axis = getattr(self, f"{which}axis") - other_axis = getattr(other, f"{which}axis") - - # Set minor ticker - this_axis.minor = other_axis.minor - - # Get and set limits - limits = getattr(other, f"get_{which}lim")() - set_lim = getattr(self, f"set_{which}lim") - get_autoscale = getattr(other, f"get_autoscale{which}_on") - - lim0, lim1 = limits - set_lim(lim0, lim1, emit=False, auto=get_autoscale()) - - # Set scale - this_axis._scale = other_axis._scale # Apply signature obfuscation after storing previous signature diff --git a/ultraplot/axes/shared.py b/ultraplot/axes/shared.py index 95b685744..97a1de288 100644 --- a/ultraplot/axes/shared.py +++ b/ultraplot/axes/shared.py @@ -184,3 +184,41 @@ def _update_ticks( if kwtext_extra: for lab in obj.get_ticklabels(): lab.update(kwtext_extra) + + + # Override matplotlib defaults ot handle multiple axis sharing + def sharex(self, other): + return self.share_axis(which="x", other=other) + + def sharey(self, other): + self.share_axis(which="y", other=other) + + # Internal function to share axes + def share_axis(self, which, other): + if not isinstance(other, Axes): + return TypeError( + f"Cannot share axes with {type(other).__name__}.\n" + f"Expected: matplotlib.axes.Axes instance\n" + f"Received: {type(other).__name__}\n" + "Please provide a valid Axes instance to share with." + ) + + self._shared_axes[which].join(self, other) + + # Get axis objects + this_axis = getattr(self, f"{which}axis") + other_axis = getattr(other, f"{which}axis") + + # Set minor ticker + this_axis.minor = other_axis.minor + + # Get and set limits + limits = getattr(other, f"get_{which}lim")() + set_lim = getattr(self, f"set_{which}lim") + get_autoscale = getattr(other, f"get_autoscale{which}_on") + + lim0, lim1 = limits + set_lim(lim0, lim1, emit=False, auto=get_autoscale()) + + # Set scale + this_axis._scale = other_axis._scale From e370daa6fd7eaf9d08b41608fa521e43c8b65ed3 Mon Sep 17 00:00:00 2001 From: cvanelteren Date: Thu, 16 Jan 2025 16:20:46 +0100 Subject: [PATCH 10/17] black formatting --- ultraplot/axes/base.py | 1 - ultraplot/axes/shared.py | 1 - 2 files changed, 2 deletions(-) diff --git a/ultraplot/axes/base.py b/ultraplot/axes/base.py index fd5f55191..26bb984f1 100644 --- a/ultraplot/axes/base.py +++ b/ultraplot/axes/base.py @@ -3225,7 +3225,6 @@ def number(self, num): raise ValueError(f"Invalid number {num!r}. Must be integer >=1.") - # Apply signature obfuscation after storing previous signature # NOTE: This is needed for __init__ Axes._format_signatures = {Axes: inspect.signature(Axes.format)} diff --git a/ultraplot/axes/shared.py b/ultraplot/axes/shared.py index 97a1de288..14f7974fb 100644 --- a/ultraplot/axes/shared.py +++ b/ultraplot/axes/shared.py @@ -185,7 +185,6 @@ def _update_ticks( for lab in obj.get_ticklabels(): lab.update(kwtext_extra) - # Override matplotlib defaults ot handle multiple axis sharing def sharex(self, other): return self.share_axis(which="x", other=other) From 8fe3215dfb56ccf0534c21820b531ddef5ed783d Mon Sep 17 00:00:00 2001 From: cvanelteren Date: Thu, 16 Jan 2025 16:25:52 +0100 Subject: [PATCH 11/17] typo in raised error --- ultraplot/axes/shared.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ultraplot/axes/shared.py b/ultraplot/axes/shared.py index 14f7974fb..c4a56a8f6 100644 --- a/ultraplot/axes/shared.py +++ b/ultraplot/axes/shared.py @@ -197,7 +197,7 @@ def share_axis(self, which, other): if not isinstance(other, Axes): return TypeError( f"Cannot share axes with {type(other).__name__}.\n" - f"Expected: matplotlib.axes.Axes instance\n" + f"Expected: ultraplot.base.Axes instance\n" f"Received: {type(other).__name__}\n" "Please provide a valid Axes instance to share with." ) From 84e53bae52215660fa02bd752cdacbb4f42ae399 Mon Sep 17 00:00:00 2001 From: Casper van Elteren Date: Thu, 16 Jan 2025 16:59:05 +0100 Subject: [PATCH 12/17] Update ultraplot/axes/shared.py Co-authored-by: Matthew R. Becker --- ultraplot/axes/shared.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ultraplot/axes/shared.py b/ultraplot/axes/shared.py index c4a56a8f6..a70f2dd4d 100644 --- a/ultraplot/axes/shared.py +++ b/ultraplot/axes/shared.py @@ -185,7 +185,7 @@ def _update_ticks( for lab in obj.get_ticklabels(): lab.update(kwtext_extra) - # Override matplotlib defaults ot handle multiple axis sharing + # Override matplotlib defaults to handle multiple axis sharing def sharex(self, other): return self.share_axis(which="x", other=other) From 37e55c3b79a127b05605b5bdb2558a251f25c154 Mon Sep 17 00:00:00 2001 From: cvanelteren Date: Thu, 16 Jan 2025 17:00:52 +0100 Subject: [PATCH 13/17] removed _safe_share --- ultraplot/axes/cartesian.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/ultraplot/axes/cartesian.py b/ultraplot/axes/cartesian.py index 524adc325..b8f27d9a2 100644 --- a/ultraplot/axes/cartesian.py +++ b/ultraplot/axes/cartesian.py @@ -563,11 +563,6 @@ def _is_panel_group_member(self, other): and other._panel_parent is self._panel_parent # other is sibling panel ) - def _safe_share(self, axis_name, other_axis): - if hasattr(self, f"_share{axis_name}"): - setattr(self, f"_share{axis_name}", None) - getattr(self, f"share{axis_name}")(other_axis) - def share_axis(self, which, other): if not isinstance(other, plot.PlotAxes): raise ValueError("other must be an Axes instance") From bec6700861bcfc4949dc37256040d1ff08140508 Mon Sep 17 00:00:00 2001 From: cvanelteren Date: Thu, 16 Jan 2025 17:28:49 +0100 Subject: [PATCH 14/17] internalize _share_axis --- ultraplot/axes/cartesian.py | 5 ----- ultraplot/axes/shared.py | 5 +++-- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/ultraplot/axes/cartesian.py b/ultraplot/axes/cartesian.py index b8f27d9a2..0bb70dd22 100644 --- a/ultraplot/axes/cartesian.py +++ b/ultraplot/axes/cartesian.py @@ -563,11 +563,6 @@ def _is_panel_group_member(self, other): and other._panel_parent is self._panel_parent # other is sibling panel ) - def share_axis(self, which, other): - if not isinstance(other, plot.PlotAxes): - raise ValueError("other must be an Axes instance") - shared = getattr(self, f"get_shared_{which}_axes")() - self._shared_axes[which].join(self, other) def _sharex_limits(self, sharex): """ diff --git a/ultraplot/axes/shared.py b/ultraplot/axes/shared.py index a70f2dd4d..155decfec 100644 --- a/ultraplot/axes/shared.py +++ b/ultraplot/axes/shared.py @@ -10,6 +10,7 @@ from ..internals import ic # noqa: F401 from ..internals import _pop_kwargs from ..utils import _fontsize_to_pt, _not_none, units +from ..axes import Axes class _SharedAxes(object): @@ -192,8 +193,8 @@ def sharex(self, other): def sharey(self, other): self.share_axis(which="y", other=other) - # Internal function to share axes - def share_axis(self, which, other): + # Ultraplot internal function to share axes + def _share_axis(self, which, other): if not isinstance(other, Axes): return TypeError( f"Cannot share axes with {type(other).__name__}.\n" From bb93b043b80b221249bdf6a220655fb96255affc Mon Sep 17 00:00:00 2001 From: cvanelteren Date: Thu, 16 Jan 2025 17:30:01 +0100 Subject: [PATCH 15/17] black formatting --- ultraplot/axes/cartesian.py | 1 - 1 file changed, 1 deletion(-) diff --git a/ultraplot/axes/cartesian.py b/ultraplot/axes/cartesian.py index 0bb70dd22..2cf34bdc3 100644 --- a/ultraplot/axes/cartesian.py +++ b/ultraplot/axes/cartesian.py @@ -563,7 +563,6 @@ def _is_panel_group_member(self, other): and other._panel_parent is self._panel_parent # other is sibling panel ) - def _sharex_limits(self, sharex): """ Safely share limits and tickers without resetting things. From 7c5dc7b4ebe4bc5ea9de7ee4e7132044489f4457 Mon Sep 17 00:00:00 2001 From: cvanelteren Date: Thu, 16 Jan 2025 17:43:31 +0100 Subject: [PATCH 16/17] fix typo --- ultraplot/axes/shared.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ultraplot/axes/shared.py b/ultraplot/axes/shared.py index 155decfec..d589e5c6e 100644 --- a/ultraplot/axes/shared.py +++ b/ultraplot/axes/shared.py @@ -188,10 +188,10 @@ def _update_ticks( # Override matplotlib defaults to handle multiple axis sharing def sharex(self, other): - return self.share_axis(which="x", other=other) + return self._share_axis(which="x", other=other) def sharey(self, other): - self.share_axis(which="y", other=other) + self._share_axis(which="y", other=other) # Ultraplot internal function to share axes def _share_axis(self, which, other): From ad254bea5015dcd9fd95bd9e62b28f24cfbbaca0 Mon Sep 17 00:00:00 2001 From: "Matthew R. Becker" Date: Thu, 16 Jan 2025 12:08:18 -0600 Subject: [PATCH 17/17] Update ultraplot/tests/test_subplots.py --- ultraplot/tests/test_subplots.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ultraplot/tests/test_subplots.py b/ultraplot/tests/test_subplots.py index 3d45bed66..797068d3a 100644 --- a/ultraplot/tests/test_subplots.py +++ b/ultraplot/tests/test_subplots.py @@ -184,7 +184,10 @@ def test_axis_sharing(share): axi.set_xlabel(labels[idx]) axi.set_ylabel(labels[idx]) - # TODO: the labels are handled in a funky way. The plot looks fine but the label are not "shared" that is the labels still exist but they are not visible and instead there are new labels created. Need to figure this out. + # TODO: the labels are handled in a funky way. The plot looks fine but + # the label are not "shared" that is the labels still exist but they + # are not visible and instead there are new labels created. Need to + # figure this out. # test left hand side if share != "labels": assert all([i == j for i, j in zip(ax[0].get_xlim(), ax[2].get_xlim())])