Thanks to visit codestin.com
Credit goes to github.com

Skip to content

FIX: ColorbarAxes properly in the axes stack #20484

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

Closed
wants to merge 4 commits into from
Closed
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
29 changes: 24 additions & 5 deletions lib/matplotlib/colorbar.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,9 +227,9 @@ def __init__(self, parent, userax=True):
True if the user passed `.Figure.colorbar` the axes manually.
"""

fig = parent.figure
if userax:
# copy position:
fig = parent.figure
outer_ax = fig.add_axes(parent.get_position())
# copy the locator if one exists:
outer_ax._axes_locator = parent._axes_locator
Expand All @@ -239,28 +239,48 @@ def __init__(self, parent, userax=True):
parent._axes.add_child_axes(outer_ax)
outer_ax._axes.child_axes.remove(parent)
else:
parent.remove()
try:
parent.remove()
except ValueError:
pass # Already removed
else:
outer_ax = parent

# swap axes in the stack if its in there:
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure how it wouldn't be there if you've created it above or gotten it passed in? But, wouldn't you want to add(self) regardless of whether the outer one is there or not?

Copy link
Member Author

Choose a reason for hiding this comment

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

There was some test where we made a colorbar and it didn't actually ever get added. I can take ti out again to figure out which one...

if outer_ax in fig._localaxes:
fig._localaxes.remove(outer_ax)
fig._axstack.remove(outer_ax)
fig._localaxes.add(self)
fig._axstack.add(self)
inner_ax = outer_ax.inset_axes([0, 0, 1, 1])
self.__dict__.update(inner_ax.__dict__)

self.outer_ax = outer_ax
self.inner_ax = inner_ax

self.outer_ax.xaxis.set_visible(False)
self.outer_ax.yaxis.set_visible(False)
self.outer_ax.set_facecolor('none')
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
for attr in ["get_position", "set_position", "set_aspect"]:
for attr in ["get_position", "set_aspect",
"_remove_method", "_set_position",
"set_position", "cla", "draw"]:
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm still not sure about these only being mapped to the outer_ax... I would have thought it should be something like:

def draw(self, renderer):
    super().draw(renderer)
    self.outer_ax.draw(renderer)

so that you draw both the inner and outer? Same for the cla and remove. The position's I think are appropriate to map straight to the outer though.

Copy link
Member Author

Choose a reason for hiding this comment

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

inner is an artist on outer, so inner gets drawn if outer gets drawn ;-)

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh yeah, it is an inset of the outer! Makes sense now, thanks for clarifying.

setattr(self, attr, getattr(self.outer_ax, attr))
self._colorbar_info = None # used for mpl-created axes
if hasattr(self.outer_ax, "get_subplotspec"):
attr = "get_subplotspec"
setattr(self, attr, getattr(self.outer_ax, attr))

if userax:
self._colorbar_info = 'user'
# point the parent's methods all at this axes...
origdict = parent.__dict__
parent.__dict__ = self.__dict__
Copy link
Contributor

Choose a reason for hiding this comment

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

can you just update it?

Suggested change
parent.__dict__ = self.__dict__
parent.__dict__.update(self.__dict__)

Copy link
Member Author

Choose a reason for hiding this comment

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

Oh, maybe? Thanks!

Copy link
Member Author

Choose a reason for hiding this comment

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

No, sadly not. I'll admit I'm probably not enough of a pythionista to dig around in the guts like this. Its pretty apparent to me that this copies all the attributes over, but if you iterate through __dict__ like a normal dictionary it does not capture everything in __dict__. Its all a little confusing. If someone has a more elegant less tacky way to do this, we can certainly try that approach...

Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe try {**parent.__dict__, **self.__dict__} which would produce a new dict...

for key in origdict.keys():
if key not in parent.__dict__:
parent.__dict__[key] = origdict[key]

def _set_inner_bounds(self, bounds):
"""
Expand Down Expand Up @@ -949,8 +969,7 @@ def remove(self):
If the colorbar was created with ``use_gridspec=True`` the previous
gridspec is restored.
"""
self.ax.inner_ax.remove()
self.ax.outer_ax.remove()
self.ax.remove()

self.mappable.callbacksSM.disconnect(self.mappable.colorbar_cid)
self.mappable.colorbar = None
Expand Down
2 changes: 1 addition & 1 deletion lib/matplotlib/figure.py
Original file line number Diff line number Diff line change
Expand Up @@ -1200,7 +1200,7 @@ def subplots_adjust(self, left=None, bottom=None, right=None, top=None,
"disabling constrained_layout.")
self.subplotpars.update(left, bottom, right, top, wspace, hspace)
for ax in self.axes:
if isinstance(ax, SubplotBase):
if hasattr(ax, 'get_subplotspec'):
ax._set_position(ax.get_subplotspec().get_position(self))
self.stale = True

Expand Down
18 changes: 4 additions & 14 deletions lib/mpl_toolkits/axes_grid1/axes_grid.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

import numpy as np

import matplotlib as mpl
from matplotlib import _api
from matplotlib.gridspec import SubplotSpec

Expand All @@ -29,28 +28,19 @@ def colorbar(self, mappable, *, ticks=None, **kwargs):
orientation = (
"horizontal" if self.orientation in ["top", "bottom"] else
"vertical")
kwargs['userax'] = False
cb = mpl.colorbar.Colorbar(
self, mappable, orientation=orientation, ticks=ticks, **kwargs)
self._config_axes()
cb = self.figure.colorbar(mappable, cax=self, orientation=orientation,
ticks=ticks, **kwargs)
return cb

def _config_axes(self):
"""Make an axes patch and outline."""
ax = self
ax.set_navigate(False)
ax.axis[:].toggle(all=False)
b = self._default_label_on
ax.axis[self.orientation].toggle(all=b)

def toggle_label(self, b):
self._default_label_on = b
axis = self.axis[self.orientation]
axis.toggle(ticklabels=b, label=b)

def cla(self):
orientation = self.orientation
super().cla()
self._config_axes()
self.orientation = orientation


class CbarAxes(CbarAxesBase, Axes):
Expand Down