Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Fix cla colorbar#20330

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Closed
jklymak wants to merge6 commits intomatplotlib:masterfromjklymak:fix-cla-colorbar
Closed

Conversation

jklymak
Copy link
Member

PR Summary

Alternative to#20323.

This allows callingcb.ax.cla and reusing the same axes to swap in a new colorbar. If the colorbar is not filled in, an empty axes with the default facecolor is left where the old one was.

Co-authored with@greglucas since I stole his test (though he is welcome to disavow the PR ;-))

PR Checklist

  • Has pytest style unit tests (andpytest passes).
  • IsFlake 8 compliant (runflake8 on changed files to check).
  • New features are documented, with examples if plot related.
  • Documentation is sphinx and numpydoc compliant (the docs shouldbuild without error).
  • Conforms to Matplotlib style conventions (installflake8-docstrings and runflake8 --docstring-convention=all).
  • New features have an entry indoc/users/next_whats_new/ (follow instructions in README.rst there).
  • API changes documented indoc/api/next_api_changes/ (follow instructions in README.rst there).

@jklymakjklymak added this to thev3.5.0 milestoneMay 28, 2021
co-author: Greg Lucas <greg.m.lucas@gmail.com>
Copy link
Contributor

@greglucasgreglucas left a comment

Choose a reason for hiding this comment

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

I think this is a better fix 👍. Could you also add theremove() method on the axes too? I think that is the one I was running into actually.

del self.inner_ax
del self.outer_ax

self.xaxis.set_visible(True)
Copy link
Contributor

Choose a reason for hiding this comment

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

Here self is back to an Axes now, so can you callsuper().cla() to get the defaults for you?

@jklymak
Copy link
MemberAuthor

We can probably make that work. I think it's a little trickier than what you had in the other PR. What is the goal of removing the axes from the axes versus calling cb.remove()?

@jklymak
Copy link
MemberAuthor

Actually, I'm a little confused what you wantcb.ax.remove to do.cb.remove removes the colorbar , and resizes the mappables axes to refill the space. So far, what I have forcb.ax.remove it just removes the colorbar, but it doesn't resize the mappables axes. Is that what you want?

@greglucas
Copy link
Contributor

The issue I was running into was thatparent.remove() was failing when I was trying to replicate the axes_grid test with a normal colorbar axis.


I assumed that was because theremove() method wasn't on CbarAxes, but now that I think about it I'm not sure why that method wouldn't be inherited from the base Axes in the first place for something to be there already. It may be that thecla() method here is enough to clean up that use case, which I think is shown by your test not failing...

@greglucas
Copy link
Contributor

Here is my reproducing example on master currently.

importmatplotlib.pyplotaspltimportnumpyasnpX,Y=np.meshgrid(np.linspace(0,6,30),np.linspace(0,6,30))arr=np.sin(X)*np.cos(Y)+1j*(np.sin(3*Y)*np.cos(Y/2.))fig,axarr=plt.subplots(figsize=(12,9),nrows=2,ncols=2)im1=axarr[0,0].imshow(arr.real,cmap='nipy_spectral')im2=axarr[1,0].imshow(arr.imag,cmap='hot')im3=axarr[0,1].imshow(np.abs(arr),cmap='jet')im4=axarr[1,1].imshow(np.arctan2(arr.imag,arr.real),cmap='hsv')cb1=plt.colorbar(im1,ax=axarr[:,0])cb2=plt.colorbar(im2,ax=axarr[:,1])cb1.ax.cla()cb3=plt.colorbar(im3,cax=cb1.ax)plt.show()

@jklymak
Copy link
MemberAuthor

Sure, that works fine on this PR.

try:
self.draw_all()
except AttributeError:
# update_normal sometimes is called when it shouldn't be..
Copy link
Contributor

Choose a reason for hiding this comment

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

Why doesupdate_normal get called when it shouldn't be? Presumably it has something to do with the newcla() addition you added. I wonder if you need to remove thecallbacks_cid fromcallbacksSM?

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

Oh more lovely callbacks ;-)

greglucas reacted with laugh emoji
Copy link
MemberAuthor

Choose a reason for hiding this comment

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

The fundamental problem is that the colorbar axes iscla, but thecolorbar object is not removed. ThenColorbar__init__ callsmappable.autoscale_None() which triggers a draw of the orphaned colorbar. The orphaned colorbar tries to draw itself, but of course all its axes have been destroyed and we get anAttributeError.

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

The problem is that for the callbacks, each mappable only maps to one colorbar, but in the following the mappable is used for two colorbars:

importmatplotlib.pyplotaspltimportnumpyasnpfig,ax=plt.subplots()pc=ax.imshow(np.arange(100).reshape(10,10))cb=fig.colorbar(pc,extend='both')cb2=fig.colorbar(pc)# Clear and re-use the same colorbar axesprint('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.colorbar(pc,cax=cb.ax)cb2=fig.colorbar(pc,cax=cb2.ax)

which results in:

HERE1: AxesImage(80,52.8;317.44x369.6) {'changed': {0: <weakref at 0x1085d69e0; to 'Colorbar' at 0x1096758e0>, 1: <weakref at 0x109709120; to 'Colorbar' at 0x109748130>}}id 1HERE2: AxesImage(80,52.8;317.44x369.6) {'changed': {0: <weakref at 0x1085d69e0; to 'Colorbar' at 0x1096758e0>}}id 1HERE3: AxesImage(80,52.8;317.44x369.6) {'changed': {0: <weakref at 0x1085d69e0; to 'Colorbar' at 0x1096758e0>}}

ForHERE3 there should be no callback, but it never gets dicsonnected when we docb2.ax.cla() becausepc doesn't know aboutcb2.

The culprit is here:

mappable.colorbar=self
mappable.colorbar_cid=mappable.callbacksSM.connect(
'changed',self.update_normal)

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

Of course this doesn't work at all on master? So maybe this test is just too stringent.

Copy link
Contributor

Choose a reason for hiding this comment

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

Here is an idea that would help this situation (and the pan/zoom on colorbars too which would help me). What about making theColorbar object itself inherit fromCbarAxes/become an Axes object? Then you would only call methods on the Colorbar object, notcbar.ax/cax...

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

I don't know if that helps in particular.

The issue here is that the scalar mappable is only storing a reference to a single colorbar, whereas it stores two callbacks. So the lineself.colorbar.mappable.callbacksSM.disconnect(self.colorbar.mappable.colorbar_cid) only knows about one colorbar id, and loses track of the other colorbar. That leads to the stray draw.

The newest commit promotescolorbar_cid to a dictionary if there is more than one and.colorbar to a list. Thats a bit of a breaking change for what is stored on this attribute, but makes sure we don't lose track of what colorbars are on the scalar mappable.

Copy link
Contributor

Choose a reason for hiding this comment

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

Oof, this opened a can of worms ;)

I don't think changingcolorbar_cid to a dictionary is a great idea here, that seems like a lot more logic/work going on under the hood to take care of a niche case. As it so happens, it looks like there is some discussion about multiple callbacks in#20210 as well.

For this specific use-case, the reason you're getting multiple callbacks is because when you callcbar.ax.cla() thatax.cla() has no idea it should also clear a callback on thecbar. But, if we move that up a level to make it justcbar.cla() (i.e. Colorbar is now an Axes object), that clear call would know to get rid of the callback (and also clear the inner/outer axes as you already have implemented).

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

I'm not at all fundamentally against that, but it seems a pretty hefty change. I think what you would do is lock out cb.ax and then just reproduce all the axes methods (which has been done already, but only partially). But it'd take some work to do it and not have a bunch of breaking API.

Copy link
Contributor

Choose a reason for hiding this comment

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

Surprisingly, it doesn't seem that bad... See this commit:
greglucas@569fbc1
Passes alltest_colorbar.py tests locally. Just pointingself.ax = self after initialization would help and we could probably stick in a deprecation warning pass-through on that too. Maybe this is getting a little side-tracked for this thread and I should open up a separate issue for discussion?

@jklymakjklymak marked this pull request as draftJune 10, 2021 14:19
@jklymak
Copy link
MemberAuthor

Pretty sure this is obsoleted by#20501

Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment
Reviewers

@greglucasgreglucasgreglucas left review comments

Assignees
No one assigned
Projects
None yet
Milestone
v3.5.0
Development

Successfully merging this pull request may close these issues.

2 participants
@jklymak@greglucas

[8]ページ先頭

©2009-2025 Movatter.jp