diff --git a/galleries/examples/subplots_axes_and_figures/broken_axis_inset.py b/galleries/examples/subplots_axes_and_figures/broken_axis_inset.py new file mode 100644 index 000000000000..0797c065a813 --- /dev/null +++ b/galleries/examples/subplots_axes_and_figures/broken_axis_inset.py @@ -0,0 +1,55 @@ +""" +================= +Broken axis inset +================= + +Example of a broken axis with an inset spanning across the break. +""" + +import matplotlib.pyplot as plt + +# initialize figure +fig, axes = plt.subplots(ncols=2, sharey=True, gridspec_kw={'top': 0.6}) +inset = fig.add_axes([0.2, 0.7, 0.6, 0.2]) + +# set axes limits +axes[0].set_xlim(0, 0.5) +axes[1].set_xlim(0.5, 1) +axes[1].set_ylim(0, 1) +inset.set_xlim(0.3, 0.7) +inset.set_ylim(0.4, 0.6) + +# hide the spines between the axes +axes[0].spines.right.set_visible(False) +axes[1].spines.left.set_visible(False) +axes[1].yaxis.tick_right() + +# indicate inset on the left axes +indicator = axes[0].indicate_inset(inset_ax=inset, edgecolor='tab:red') +indicator.rectangle.set_clip_box(axes[0].bbox) +indicator.rectangle.set_clip_on(True) +indicator.connectors[2].set_visible(False) + +# indicate inset on the right axes +indicator = axes[1].indicate_inset(inset_ax=inset, edgecolor='tab:blue') +indicator.rectangle.set_clip_box(axes[1].bbox) +indicator.rectangle.set_clip_on(True) +indicator.connectors[0].set_visible(False) + +# show +plt.show() + +# %% +# +# .. admonition:: References +# +# The use of the following functions, methods, classes and modules is shown +# in this example: +# +# - `matplotlib.axes.Axes.indicate_inset_zoom` +# - `matplotlib.inset.InsetIndicator` +# +# .. tags:: +# +# component: axes +# level: advances diff --git a/galleries/examples/subplots_axes_and_figures/gallery_order.txt b/galleries/examples/subplots_axes_and_figures/gallery_order.txt index 5ec9bf1a0d00..0da8ca319efd 100644 --- a/galleries/examples/subplots_axes_and_figures/gallery_order.txt +++ b/galleries/examples/subplots_axes_and_figures/gallery_order.txt @@ -44,5 +44,6 @@ figure_title axes_zoom_effect zoom_inset_axes broken_axis +broken_axis_inset geo_demo axhspan_demo diff --git a/lib/matplotlib/inset.py b/lib/matplotlib/inset.py index aae640db6f81..6e8d3195e5e1 100644 --- a/lib/matplotlib/inset.py +++ b/lib/matplotlib/inset.py @@ -8,7 +8,8 @@ from matplotlib.path import Path -_shared_properties = ('alpha', 'edgecolor', 'linestyle', 'linewidth') +_shared_properties = ( + 'alpha', 'clip_on', 'edgecolor', 'linestyle', 'linewidth') class InsetIndicator(artist.Artist): @@ -69,13 +70,14 @@ def __init__(self, bounds=None, inset_ax=None, zorder=None, **kwargs): # Initial style properties for the artist should match the rectangle. for prop in _shared_properties: - setattr(self, f'_{prop}', artist.getp(self._rectangle, prop)) + setattr( + self, f'_{prop.replace('_', '')}', artist.getp(self._rectangle, prop)) def _shared_setter(self, prop, val): """ Helper function to set the same style property on the artist and its children. """ - setattr(self, f'_{prop}', val) + setattr(self, f'_{prop.replace('_', '')}', val) artist.setp([self._rectangle, *self._connectors], prop, val) @@ -90,6 +92,10 @@ def set_alpha(self, alpha): # docstring inherited self._shared_setter('alpha', alpha) + def set_clip_on(self, clip_on): + # docstring inherited + self._shared_setter('clip_on', clip_on) + def set_edgecolor(self, color): """ Set the edge color of the rectangle and the connectors. @@ -193,7 +199,7 @@ def _update_connectors(self): p = ConnectionPatch( xyA=xy_inset_ax, coordsA=self._inset_ax.transAxes, xyB=xy_data, coordsB=self.rectangle.get_data_transform(), - arrowstyle="-", + arrowstyle="-", clip_on=self.get_clip_on(), edgecolor=self._edgecolor, alpha=self.get_alpha(), linestyle=self._linestyle, linewidth=self._linewidth) self._connectors.append(p) diff --git a/lib/matplotlib/inset.pyi b/lib/matplotlib/inset.pyi index e895fd7be27c..144dc8eb7305 100644 --- a/lib/matplotlib/inset.pyi +++ b/lib/matplotlib/inset.pyi @@ -15,6 +15,7 @@ class InsetIndicator(artist.Artist): ) -> None: ... def set_alpha(self, alpha: float | None) -> None: ... def set_edgecolor(self, color: ColorType | None) -> None: ... + def set_clip_on(self, clip_on: bool) -> None: ... def set_color(self, c: ColorType | None) -> None: ... def set_linewidth(self, w: float | None) -> None: ... def set_linestyle(self, ls: LineStyleType | None) -> None: ... diff --git a/lib/matplotlib/tests/baseline_images/test_inset/zoom_inset_connector_styles.png b/lib/matplotlib/tests/baseline_images/test_inset/zoom_inset_connector_styles.png index df8b768725d7..15a86195005b 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_inset/zoom_inset_connector_styles.png and b/lib/matplotlib/tests/baseline_images/test_inset/zoom_inset_connector_styles.png differ diff --git a/lib/matplotlib/tests/test_inset.py b/lib/matplotlib/tests/test_inset.py index bbf481c23253..8fc39d4e6151 100644 --- a/lib/matplotlib/tests/test_inset.py +++ b/lib/matplotlib/tests/test_inset.py @@ -46,19 +46,24 @@ def test_inset_indicator_update_styles(): inset, edgecolor='red', alpha=0.5, linewidth=2, linestyle='solid') # Changing the rectangle styles should not affect the connectors. - indicator.rectangle.set(color='blue', linestyle='dashed', linewidth=42, alpha=0.2) + indicator.rectangle.set( + clip_on=True, color='blue', linestyle='dashed', linewidth=42, alpha=0.2) for conn in indicator.connectors: assert mcolors.same_color(conn.get_edgecolor()[:3], 'red') assert conn.get_alpha() == 0.5 + assert conn.get_clip_on() is False assert conn.get_linestyle() == 'solid' assert conn.get_linewidth() == 2 # Changing the indicator styles should affect both rectangle and connectors. - indicator.set(color='green', linestyle='dotted', linewidth=7, alpha=0.8) + indicator.rectangle.set(clip_on=False) + indicator.set( + clip_on=True, color='green', linestyle='dotted', linewidth=7, alpha=0.8) assert mcolors.same_color(indicator.rectangle.get_facecolor()[:3], 'green') for patch in (*indicator.connectors, indicator.rectangle): assert mcolors.same_color(patch.get_edgecolor()[:3], 'green') assert patch.get_alpha() == 0.8 + assert patch.get_clip_on() is True assert patch.get_linestyle() == 'dotted' assert patch.get_linewidth() == 7 @@ -67,12 +72,15 @@ def test_inset_indicator_update_styles(): assert mcolors.same_color(patch.get_edgecolor()[:3], 'purple') # This should also be true if connectors weren't created yet. + indicator.rectangle.set(clip_on=False) indicator._connectors = [] - indicator.set(color='burlywood', linestyle='dashdot', linewidth=4, alpha=0.4) + indicator.set( + clip_on=True, color='burlywood', linestyle='dashdot', linewidth=4, alpha=0.4) assert mcolors.same_color(indicator.rectangle.get_facecolor()[:3], 'burlywood') for patch in (*indicator.connectors, indicator.rectangle): assert mcolors.same_color(patch.get_edgecolor()[:3], 'burlywood') assert patch.get_alpha() == 0.4 + assert patch.get_clip_on() is True assert patch.get_linestyle() == 'dashdot' assert patch.get_linewidth() == 4 @@ -107,6 +115,8 @@ def test_zoom_inset_connector_styles(): # Make one visible connector a different style indicator.connectors[1].set_linestyle('dashed') indicator.connectors[1].set_color('blue') + indicator.connectors[1].set_clip_box(axs[0].bbox) + indicator.connectors[1].set_clip_on(True) @image_comparison(['zoom_inset_transform.png'], remove_text=True, style='mpl20',