Skip to content

Have Colorbar inherit from Axes #20341

Closed
Closed

Description

@greglucas

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.

Activity

  1. jklymak commented on Jun 2, 2021

    @jklymak
    Member

    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...

  2. greglucas commented on Jun 2, 2021

    @greglucas
    ContributorAuthor

    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.

  3. timhoffm commented on Jun 2, 2021

    @timhoffm
    Member

    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.

  4. jklymak commented on Jun 2, 2021

    @jklymak
    Member

    .. 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.

  5. greglucas commented on Jun 3, 2021

    @greglucas
    ContributorAuthor

    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)

  6. jklymak commented on Jun 3, 2021

    @jklymak
    Member

    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.

  7. jklymak commented on Jun 11, 2021

    @jklymak
    Member

    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.

  8. jklymak commented on Jun 11, 2021

    @jklymak
    Member

    ... 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.

  9. greglucas commented on Jun 11, 2021

    @greglucas
    ContributorAuthor

    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.

  10. github-actions commented on Oct 24, 2025

    @github-actions

    This issue has been marked "inactive" because it has been 365 days since the last comment. If this issue is still present in recent Matplotlib releases, or the feature request is still wanted, please leave a comment and this label will be removed. If there are no updates in another 30 days, this issue will be automatically closed, but you are free to re-open or create a new issue if needed. We value issue reports, and this procedure is meant to help us resurface and prioritize issues that have not been addressed yet, not make them disappear. Thanks for your help!

  11. timhoffm commented on Oct 27, 2025

    @timhoffm
    Member

    I propose to close this, I think it's a too broad API going with a full Axes subclass. If we want to rearchitect #20341 (comment) would be the way, but I don't see a strong need here; in particular also because #27167 introduced the long_axis property so that you can handle all the tick-related topics on the Axis level.

  12. greglucas commented on Oct 27, 2025

    @greglucas
    ContributorAuthor

    I agree with closing this 👍 We can bring it back up later if someone has the desire to push it through. The colorbar is in a better state now than when this was proposed.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions