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

Skip to content

Have Colorbar inherit from Axes #20341

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

Open
greglucas opened this issue Jun 2, 2021 · 9 comments · May be fixed by #20350
Open

Have Colorbar inherit from Axes #20341

greglucas opened this issue Jun 2, 2021 · 9 comments · May be fixed by #20350

Comments

@greglucas
Copy link
Contributor

Describe the issue

A Colorbar currently stores the Axes that is used for drawing in the cbar.ax attribute, which many of the methods are then called internally like self.ax.pcolormesh(). My proposal is to move the Axes up a level and have Colorbar inherit from the new CbarAxes class (to become an Axes itself) and turn the previous call into self.pcolormesh() dropping ax attribute.

Benefits: This would help with interactivity as proposed in #19515, by being able to identify if an axes is a Colorbar or not and then controlling properties based on that. It would also help with #20330, by giving the CbarAxes the ability to remove colorbar callbacks.

Drawbacks: This adds a lot of easy-to-use methods onto a Colorbar now that may not be desired to directly expose to users. i.e. they could do cbar.plot() now, whereas before cbar.ax.plot() hides those capabilities a little bit more.

Proposed fix

Define the class as
class ColorbarBase(ColorbarAxes):

and set
self.ax = self for people to keep using cbar.ax if they prefer. We could deprecate that on pass-through as well.

See this branch for an implementation.

@jklymak
Copy link
Member

jklymak commented Jun 2, 2021

Can you just make a pull of that branch, and put in Draft if you prefer?

As always, I think about this sort of thing from "what would we do if we were starting over " point of view. Would we just call a colorbar a type of Axes? I guess I'm not sure...

@greglucas greglucas linked a pull request Jun 2, 2021 that will close this issue
7 tasks
@greglucas
Copy link
Contributor Author

I wasn't sure whether an Issue was preferred before a PR for discussion purposes or not...

My thought is that making a Colorbar a type of Axes would help with being able to directly override methods of the Axes, rather than having to dispatch to the cbar.ax attribute. The real benefit is that cbar.ax Axes now explicitly has access to the extra Colorbar attributes (callbacks, lines, ...). One downside I see to this is that cbar now has three methods: set_xticks(), set_yticks(), and set_ticks(), which could potentially lead to confusion. But, it doesn't seem that bad to me either.

@timhoffm
Copy link
Member

timhoffm commented Jun 2, 2021

I'm not convinced this is the right design choice.

First, as you mentioned, there's a lot of additional functionality, that doesn't make much sense for colorbars. While one can question whether it's reasonable to have all the plotting methods on the Axes, it certainly doesn't help for a clean colorbar API. This might be addressable by extracting an appropriate common base class, which is backwards-compatible. I haven't checked, but _AxesBase might be a good start.

Second, AFAIR the dance with the inner Axes is done to get extremes-arrows and layout work together. These might be a reason that a colorbar is fundamentally different from a regular Axes.

I'm afraid we haven't clarified exactly what an Axes is. I assume it's multple things. At least:

  • It's a plotting area, in which the directions have individual scales
  • It's a namespace for functions that can add artists to this plotting area. This is mainly what the Axes class itself brings in addtion to _AxesBase (possibly modulo title functionality).
  • It's a container for elements outside the plotting area (ticks/ticklabels, labels, title()
  • It is a top-level building block for the layout engines (unless it's a free or inner Axes).
    The point here is that you need an Axes as a layout element with the same height as your data Axes to be able to put a properly sizing colorbar.

It might be reasonable to separate these functionalities better conceptually. E.g. I think it would help if we would have layout blocks, that do not necessarily come with all the other semantics. Also factoring out the plotting functions would open up a path for others to add their own functions (whether or not we want that could be decided). We would still mix everything together for standard Axes to keep backward-compatibility. But that'd be a larger design project.

@jklymak
Copy link
Member

jklymak commented Jun 2, 2021

.. I mean the PR looks appealing - the question is if we want users to have that functionality directly. I'm also not clear that it is actually a simplification. i.e. I don't see how colorbar.cla will be any easier than colorbar.ax.cla.

@greglucas
Copy link
Contributor Author

That is a good point about defining what an Axes should be and potentially breaking that out into separate subclasses. The reason I even thought that a Colorbar could be an Axes is due to the similarity in the first place. So, is a Colorbar really a completely different animal with a separate API, or is it similar enough to warrant subclassing Axes? (Note: we still need the plotting methods somewhere due to adding QuadMesh and LineCollection for the colors/lines)

The inner/outer dance is for the extends portion, but also so that the inner can leverage the base Axes locator/formatter logic without reimplementing those as separate _ColorbarLocators. Trying to be more consistent with Axes, so my thought in this issue is how far can we take that and can/should we bring it directly into Colorbar?

The reason this makes it easier for something like cla() is that you can access the mappable and other Colorbar attributes from the Axes functions now:

def cla(self):
    self.mappable.callbacksSM.disconnect(self.mappable.colorbar_cid)
    super().cla()

and for the interactive Colorbar it would be nice to say isinstance(ax, Colorbar) over in backend_bases to control which features to turn on/off for Colorbars. We currently can't distinguish if the Axes is a Colorbar or not. (a hack around that may be to put a _colorbar = True private attribute on the axes and do a hasattr() check, but that doesn't seem quite as nice)

@jklymak
Copy link
Member

jklymak commented Jun 3, 2021

OK, so the cla thing is a bit orthogonal... The mappable callback issue for multiple colorbars still stand, and the cla will still have to be as complicated for the inner and outer axes mapping I think.

@jklymak
Copy link
Member

jklymak commented Jun 11, 2021

by being able to identify if an axes is a Colorbar or not and then controlling properties based on that.

I think the concern about directly exposing the axes methods is a valid one, and indeed it will be confusing to have cb.set_ticks and cb.set_xticks and cb.set_yticks.

The desire to know if a given axes is a colorbar is a valid one. But we already do this a couple of ways. For non gridspec axes:

cax = fig.add_axes(pbcb, label="<colorbar>")

and lines following add a cax._colorbar_info attribute to the colorbar axes....

For gridspec colorbar axes we don't add all the information, but the label is still set to <colorbar>. We actually should set all the same information gridspec axes, but that information was needed for constrained_layout, so didn't get passed to the gridspec axes.

@jklymak
Copy link
Member

jklymak commented Jun 11, 2021

... actually, now all colorbar axes should be of type ColorbarAxes so that should be an easy way to tell if an axes is a Colorbar.

@greglucas
Copy link
Contributor Author

I actually think making the API more Axes-like is easier to understand. Someone doesn't have to learn new lingo to interact with a Colorbar. A Colorbar adds a convenience helper method of set_ticks() that dispatches to the long axis, but if someone already knows they want to update the x/y specific axes they can call that directly. The question seems to revolve around whether Colorbar should be a restricted API with only the minimum functions required, or an expanded API from Axes that adds additional convenience methods (what's proposed here).

... actually, now all colorbar axes should be of type ColorbarAxes so that should be an easy way to tell if an axes is a Colorbar.

I think the issue I was running into was that the Zoom/Pan are interacting on the outer_axes, rather than the ColorbarAxes class... I'll have to revisit this, I know I tried that and it didn't work immediately out of the box, but there may be a way to get to it that I'm missing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants