-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Colorbar redo again! #20501
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
Colorbar redo again! #20501
Conversation
4becc5b
to
114e8d3
Compare
import numpy as np
import matplotlib.pyplot as plt
#from matplotlib.backend_bases import MouseButton
def pick_fn(mouseevent):
if mouseevent.inaxes is colorbar.ax:
print('pick\n\nPICKKKKKKK')
print(mouseevent.ydata, mouseevent.button)
def motion_fn(mouseevent):
if mouseevent.inaxes is colorbar.ax:
print(mouseevent.ydata, mouseevent.button)
pass
fig, ax = plt.subplots()
canvas = fig.canvas
arr = np.random.randn(100, 100)
axesimage = plt.imshow(arr)
colorbar = plt.colorbar(axesimage, ax=ax, use_gridspec=True)
# helps you see what value you are about to set the range to
colorbar.ax.set_navigate(True)
canvas.mpl_connect("motion_notify_event", motion_fn)
colorbar.ax.set_picker(True)
canvas.mpl_connect("button_press_event", pick_fn)
plt.show() works fine on this PR. |
0370b32
to
735de0d
Compare
@@ -2369,6 +2369,8 @@ def _update_patch_limits(self, patch): | |||
return | |||
patch_trf = patch.get_transform() | |||
updatex, updatey = patch_trf.contains_branch_seperately(self.transData) | |||
if not (updatex or updatey): | |||
return |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Calling the transform below blows up needlessly because it is called, and then does nothing....
eab1f92
to
32b38d2
Compare
32b38d2
to
561d771
Compare
... interesting wrinkle. If an axes has an |
I have to say, this was actually fewer image changes than I was expecting :) |
np.testing.assert_allclose(cb.ax.get_position().extents, | ||
[0.78375, 0.536364, 0.796147, 0.9], rtol=2e-3) | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Surely this test was there for a reason, especially saying it was a "funny" error. Makes it seem like this should be investigated and updated rather than removed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was testing the extend length being correct (its my test). We could go ahead and keep testing this, but it'd properly require an image test.
lib/matplotlib/colorbar.py
Outdated
except TypeError: | ||
pass | ||
lim = self._cbar._long_axis().get_view_interval() | ||
self._cbar._short_axis().set_view_interval(*lim) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you also want to update the data interval of the short axis here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure what you mean? We need to do this to make the aspect ratio of the colobar correct.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, this has been removed. See comments
lib/matplotlib/colorbar.py
Outdated
if self._cbar.orientation == 'vertical': | ||
if aspect: | ||
ax.set_aspect(aspect*shrink) | ||
offset = offset * pos.height |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
*=
or even just explicitly put offset * pos.height
into the translated call below.
lib/matplotlib/colorbar.py
Outdated
|
||
# make the inner axes smaller to make room for the extend rectangle | ||
top = elower + inner_length | ||
top = eupper + 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For consistency I'd suggest either defining bot = -elower
here and using that below or using 1 + eupper
in the calculations.
self.ax.outer_ax.add_patch(patch) | ||
antialiased=False, transform=self.ax.transAxes, | ||
hatch=hatches[0], clip_on=False) | ||
self.ax.add_patch(patch) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because you're setting clip_on=False
here, do you need to adjust the zorder of the patch at all so that it is behind the main axes?
There was a problem hiding this 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 as it was before?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems reasonable with the layer between the patch, the outline, and then the fill-ins for the extensions.
We do not keep a (named) reference to these patches, what do we do if we update the colormap?
Colorbar(cax, cmap=cmap, norm=norm, | ||
boundaries=boundaries, values=values, | ||
extend=extension_type, extendfrac=extendfrac, | ||
orientation='horizontal', spacing=spacing) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dedent these also.
lib/matplotlib/colorbar.py
Outdated
|
||
def __call__(self, ax, renderer): | ||
|
||
# make sure that lims and scales are the same |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure why this is necessary; I understand from some comments that this is due to aspect ratio handling?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, the aspect of the axes is presently set via the usual ax.set_aspect
, but that requires the limits and scales of the short and long axes to be the same.
However I'm starting to think that is a mistake, and we should perhaps just use something like set_box_aspect
and get around all this awkwardness. Or just do it with the new colorbar axes locator.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See comments, this has all be re-engineered.
182a488
to
b573664
Compare
latest commit changes the aspect ratio handling from a data-aspect ratio to a box aspect ratio. The "short" side of the colorbar now always has a linear scale and limits from 0-1, and only the "long" side gets a scale and variable data limits. This simplifies a lot of code.. Note I've added a |
cax.set_aspect(aspect, anchor=loc_settings["anchor"], adjustable='box') | ||
cax.set_anchor(anchor) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was loc_settings["anchor"]
an error?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I assume that anchor
was just ignored if passed as a kwarg. I'm not sure what happens if it is passed as a kwarg.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Strange, it should just go through to set_anchor
if it's not None
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think what is happening here is correct, and the old code was ignoring the user's specification of anchor
, but maybe I'm not following...
Are the
|
This is the same as
I needed to make this method, and didn't see that it needed to be private, but I don't feel strongly about it. Indeed it will only change the scale and not have anything to do with the norm's scale. |
Your new AxesLocator is the only thing calling self.ax.set_box_aspect(aspect)
self.ax.set_aspect('auto')
This seems like a real reason not to make this public. In your calls to |
Absolutely. I'm just suggesting it may be more-generally useful as well to be able to set this after creating the colorbar, so why not expose it?
Yes, there is no reason for it to be public, other than user convenience. As I said, I don't feel super strongly about exposing this, but we have had requests to change the scale without changing the norm. For instance, it is not completely unreasonable to have a linear scale and a logarithmic norm. |
514192f
to
e391e62
Compare
70621cd
to
5a5d1bb
Compare
cax.set_aspect(aspect, anchor=loc_settings["anchor"], adjustable='box') | ||
cax.set_anchor(anchor) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Strange, it should just go through to set_anchor
if it's not None
.
f0ca1e0
to
914b750
Compare
I assume you'll fix the last thing. |
@QuLogic, sorry, what "last thing"? The anchor passthrough? I think the new code is correct... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#20501 (comment) |
8caf7dd
to
7884314
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Anyone can merge on CI green!
7884314
to
b574177
Compare
Previous overhaul packaged an inner and outer axes in a container "ColorbarAxes" and tried to dispatch methods between them. This overhaul takes the _much_ simpler approach of resizing the image using a custom _axes_locator that a) calls any existing locator b) or just uses the axes default position. The custom _axes_locator then shrinks the axes in the appropriate direction to make room for extend tri/rectangles. As with the previous fix, the extend tri/rectangles are drawn as patches in axes co-ordinates, rather than pcolormesh in "data" co-ordinates.
b574177
to
277b5eb
Compare
Thanks @QuLogic and @greglucas for all your help! |
PR Summary
OK, third time is the charm.
Previous overhaul packaged an inner and outer axes in a container "ColorbarAxes" and tried to dispatch methods between them.
This overhaul takes the much simpler approach of resizing the image using a custom _axes_locator that a) calls any existing locator b) or just uses the axes default position. The custom _axes_locator then shrinks the axes in the appropriate direction to make room for extend tri/rectangles. As with the previous fix, the extend tri/rectangles are drawn as patches in axes co-ordinates, rather than pcolormesh in "data" co-ordinates.
This addresses the concerns and closes #20479
PR Checklist
pytest
passes).flake8
on changed files to check).flake8-docstrings
and runflake8 --docstring-convention=all
).doc/users/next_whats_new/
(follow instructions in README.rst there).doc/api/next_api_changes/
(follow instructions in README.rst there).