Skip to content

Correctly set formatters and locators on removed shared axis #14997

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 19, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 23 additions & 11 deletions lib/matplotlib/figure.py
Original file line number Diff line number Diff line change
Expand Up @@ -1596,25 +1596,37 @@ def subplots(self, nrows=1, ncols=1, sharex=False, sharey=False,
return axarr

def _remove_ax(self, ax):
def _reset_loc_form(axis):
def _reset_locators_and_formatters(axis):
# Set the formatters and locators to be associated with axis
# (where previously they may have been associated with another
# Axis isntance)
#
# Because set_major_formatter() etc. force isDefault_* to be False,
# we have to manually check if the original formatter was a
# default and manually set isDefault_* if that was the case.
majfmt = axis.get_major_formatter()
if not majfmt.axis.isDefault_majfmt:
axis.set_major_formatter(majfmt)
isDefault = majfmt.axis.isDefault_majfmt
axis.set_major_formatter(majfmt)
if isDefault:
majfmt.axis.isDefault_majfmt = True

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't make any sense to me. You get the formatter for axis and then you set the formatter for axis to that formatter, and then you set the isDefault to the value axis had before. I don't see how this code is doing anything.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the confusion here stems from the mappings of formatters to axis.

  • Axis > Formatter is a one to one mapping
  • Formatter > Axis is a one to many mapping

So a single Formatter is linked to a single Axis, but many other Axis can make use of that Formatter. I agree this is a nightmare to understand, but without re-doing the internals of how this works it is how it is.

So this code is getting the formatter that axis is using, (that it might be borrowing) and then explicitly associating that formatter with axis (so it is no longer borrowing it).

majloc = axis.get_major_locator()
if not majloc.axis.isDefault_majloc:
axis.set_major_locator(majloc)
isDefault = majloc.axis.isDefault_majloc
axis.set_major_locator(majloc)
if isDefault:
majloc.axis.isDefault_majloc = True

minfmt = axis.get_minor_formatter()
if not minfmt.axis.isDefault_minfmt:
axis.set_minor_formatter(minfmt)
isDefault = majloc.axis.isDefault_minfmt
axis.set_minor_formatter(minfmt)
if isDefault:
minfmt.axis.isDefault_minfmt = True

minloc = axis.get_minor_locator()
if not minfmt.axis.isDefault_minloc:
axis.set_minor_locator(minloc)
isDefault = majloc.axis.isDefault_minloc
axis.set_minor_locator(minloc)
if isDefault:
minloc.axis.isDefault_minloc = True

def _break_share_link(ax, grouper):
siblings = grouper.get_siblings(ax)
Expand All @@ -1628,11 +1640,11 @@ def _break_share_link(ax, grouper):
self.delaxes(ax)
last_ax = _break_share_link(ax, ax._shared_y_axes)
if last_ax is not None:
_reset_loc_form(last_ax.yaxis)
_reset_locators_and_formatters(last_ax.yaxis)

last_ax = _break_share_link(ax, ax._shared_x_axes)
if last_ax is not None:
_reset_loc_form(last_ax.xaxis)
_reset_locators_and_formatters(last_ax.xaxis)

def clf(self, keep_observers=False):
"""
Expand Down
7 changes: 7 additions & 0 deletions lib/matplotlib/tests/test_figure.py
Original file line number Diff line number Diff line change
Expand Up @@ -480,3 +480,10 @@ def test_axes_removal():
axs[0].plot([datetime(2000, 1, 1), datetime(2000, 2, 1)], [0, 1])
assert isinstance(axs[0].xaxis.get_major_formatter(),
ScalarFormatter)


def test_removed_axis():
# Simple smoke test to make sure removing a shared axis works
fig, axs = plt.subplots(2, sharex=True)
axs[0].remove()
fig.canvas.draw()