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

Skip to content

Make Figure.frameon a thin wrapper for the patch visibility. #11691

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

Merged
merged 3 commits into from
Dec 28, 2018

Conversation

anntzer
Copy link
Contributor

@anntzer anntzer commented Jul 18, 2018

Directly manipulating the visibility of Figure.patch is just as good.

Similar in spirit to #10088, but hopefully less controversial.

PR Summary

PR Checklist

  • Has Pytest style unit tests
  • Code is PEP 8 compliant
  • New features are documented, with examples if plot related
  • Documentation is sphinx and numpydoc compliant
  • Added an entry to doc/users/next_whats_new/ if major new feature (follow instructions in README.rst there)
  • Documented in doc/api/api_changes.rst if API changed in a backward-incompatible way

@tacaswell tacaswell added this to the v3.1 milestone Jul 18, 2018
Copy link
Member

@tacaswell tacaswell left a comment

Choose a reason for hiding this comment

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

Please hold for 3.1

Anyone can dismiss this after 3.0 is branched.

The following API elements are deprecated:

- ``Figure.frameon``, ``Figure.get_frameon``, ``Figure.set_frameon`` (directly
manipulate the visibility of ``Figure.rect`` instead),

Choose a reason for hiding this comment

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

Do you mean Figure.patch here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes, fixed

@ImportanceOfBeingErnest
Copy link
Member

Does this solve #10939?

@anntzer
Copy link
Contributor Author

anntzer commented Jul 23, 2018

No, for that you want to look at #11692.

@anntzer anntzer dismissed tacaswell’s stale review August 27, 2018 20:15

3.0 is branched

@jklymak jklymak self-requested a review September 24, 2018 19:39
@ImportanceOfBeingErnest
Copy link
Member

Are the changes to doc/users/whats_new.rst something that belongs into this PR?


Technically this simplifies things.
One drawback of this is that it makes the option to turn the axes background off hard to find for a user. Any idea how to still make sure people would find that option? I think somehow it needs to become clear that the rcParam rcParam["figure.frameon"]=False is the same as setting fig.patch.set_visible(False) and the same as plt.figure(frameon=False).

@anntzer
Copy link
Contributor Author

anntzer commented Sep 25, 2018

Got rid of the (bookkeeping) change to whatsnew.
I don't think figure.frameon was particularly well documented either unless you were a fan of going through the shipped example matplotlibrc... in any case feel free to push doc changes.

Copy link
Member

@jklymak jklymak left a comment

Choose a reason for hiding this comment

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

Not sure about having users drill down to the patch property. Will most people know about this property of the figure? Right now we have, for instance get_facecolor/set_facecolor (and edgecolor, but not linewidth :eyeroll:) . Are you proposing to do away with those at some point in favour of the patch methods? If not, I'd suggest we still need something equivalent at the figure level: set_frameon/get_frameon.

I'm somewhat -0.5 on making the API less discoverable unless there is a good reason for the user to know the underlying structure of how an object is made. Is there a reason for the user to know that a Figure has a patch to handle its style? If we are going to do this, at the very least the docstring for Figure would need to be expanded to explain that the patch is the object for manipulating the relevant properties.

@anntzer
Copy link
Contributor Author

anntzer commented Oct 4, 2018

Right now we have, for instance get_facecolor/set_facecolor (and edgecolor, but not linewidth :eyeroll:)

That's essentially the problem: we have a top layer (Figure) partially duplicating a bottom layer (Figure.patch), but not completely and some things under different names (set_visible -> set_frameon, although naming it set_visible wouldn't work either as that'd imply not drawing the child artists either). I actually think pointing the user to do everything on the .patch would make the API simpler for the end user, not less.

@jklymak
Copy link
Member

jklymak commented Oct 4, 2018

We should decide if that is the way to go, and do it all then and thoroughly update the docs. I guess Personally I'm 50/50 on going this way, versus just adding figure.set_linewidth and figure.set_frameon.

@timhoffm
Copy link
Member

timhoffm commented Oct 7, 2018

I'm -0.5 on adding additional figure methods to control the patch. It wouldn't end with set_linewidth (which by the way is not a good name. What is the figure line anyway? It may easily be confused with other lines, such as the axes.). You would need Figure.set_edgecolor and others. It's reasonable to control the background via a dedicated object. We should make this more clear in the docs.

Once this is done, there is no need for Figure.frameon anymore. We don't need two ways here.

I'm not sure about the name patch. It's a bit unspecific. frame or background might be better. Though I'm not 100% sure which one I like. Essentially, we need to check which code looks most reasonable:

figure.patch.set_linewidth()
figure.patch.set_facecolor()
figure.patch.set_edgecolor()
figure.patch.set_visible()

or

figure.frame.set_linewidth()
figure.frame.set_facecolor()
figure.frame.set_edgecolor()
figure.frame.set_visible()

or

figure.background.set_linewidth()
figure.background.set_facecolor()
figure.background.set_edgecolor()
figure.background.set_visible()

Another aspect is the frameon kwarg and the figure.frameon rcparam. They get more obscure if there is no corresponding property. Might still be ok, if we would rename patch to frame. Otherwise, we should do something about them as well.

@jklymak
Copy link
Member

jklymak commented Oct 7, 2018

We already have Figure.set_facecolor and Figure.set_edgecolor, so adding Figure.set_linewidth isn't such a stretch. If we are keeping those, I don't see why we are making the user drill down to the patch to manipulate visibility. So either

  1. deprecate facecolor, egdecolor, frameon in favour of manipulating the patch (I'm not so worried about the name), or:
  2. add set_linewidth and keep frameon.

@ImportanceOfBeingErnest
Copy link
Member

I don't think it makes sense to deprecate existing/widespread API (frameon) unless there is a true problem which needs to be solved.

plt.rcParams["figure.frameon"] = False
fig = plt.figure(frameon=True)
fig.frameon = False

is a perfectly valid API. Anything that requires the user to think more about this, cannot be seen as improvement.

Having two or more layers in the code can be problematic, but it isn't in this case, when frameon is a transparent wrapper for the visibility of the patch. So the improvement is to make frameon a real property here.

Hence:
👍 on making frameon a property with get_/set_frameon available.
👎 on changing any API and deprecating or removing existent functionality.

@anntzer
Copy link
Contributor Author

anntzer commented Oct 8, 2018

Would you consider that by the same argument #10088 was going in the wrong direction?

In my opinion, the problem of such additional layers is that they both make it harder to write correct code (the actual visibility state is figure.frameon and figure.patch.get_visible(), not just either of them), and users tend to write ad-hoc code against them instead of using more general, widely usable APIs (e.g. consider the SkewT implementation change in #10088: yes, the "new" implementation was also possible previously, but whoever implemented it didn't think about it because label1On, etc. were "more apparent"; even without using ExitStack I think it's clear that manipulating the visibility state in draw() is simpler than overriding a bunch of properties).

@ImportanceOfBeingErnest
Copy link
Member

I'm totally in favour of making frameon a completely transparent wrapper to patch.visible. I do see the problem of the different layers, but if introducing frameon as property as you do here gets rid of that problematic completely, hence the state of figure.frameon and figure.patch.get_visible() cannot disparate.

The same could have been done in #10088. There is no benefit of not having this kind of thin-wrapping properties, other than less code lines. But removing them might break people's code. Because they were not implemented as properties, code breaks in an intransparent way now. There is no indication that tick.label2On = False should suddenly not turn the label off any more. This is pretty mean for an inexperienced user.

@anntzer
Copy link
Contributor Author

anntzer commented Oct 8, 2018

There is no benefit of not having this kind of thin-wrapping properties, other than less code lines.

Again, I disagree, although even more strongly for #10088, where the names (label1On, etc.) clearly point to the corresponding attributes (label1, etc.): I want to encourage users to use generic utilities (set_visible()) rather than specific ones (label1On), as that knowledge is more widely applicable. The new implementation of SkewT, for example, demonstrates a general, reusable method of manipulating sub-artist visibility in draw(), rather than being completely specific to SkewT.

Because they were not implemented as properties, code breaks in an intransparent way now. There is no indication that tick.label2On = False should suddenly not turn the label off any more. This is pretty mean for an inexperienced user.

Actually I implemented them as properties for the deprecation period, so that a deprecation warning does get emitted...

@anntzer anntzer mentioned this pull request Oct 10, 2018
6 tasks
@anntzer
Copy link
Contributor Author

anntzer commented Oct 10, 2018

See also discussion surrounding https://github.com/matplotlib/matplotlib/pull/12241/files#r223905198 for code that would become simpler if visibility just meant, well, {get,set}_visible(), instead of having to check on other attributes.

@ImportanceOfBeingErnest
Copy link
Member

ImportanceOfBeingErnest commented Oct 10, 2018

You'd be totally free to use .frameon or .patch.get_visible(), whichever you like more, in a case where you need to check visibility of the patch. If there are then people using constructs like if fig.frameon and fig.pach.get_visible(): that would mostly be because of unclear docstrings.

@anntzer
Copy link
Contributor Author

anntzer commented Oct 10, 2018

I undeprecated frameon to move this PR forward (it is now just an alias for the visibility state of the underlying patch).

Directly manipulating the visibility of Figure.patch is just as good.
@ImportanceOfBeingErnest
Copy link
Member

👍 Do you want to update any docstrings? Something like "This is a convenience wrapper for Figure.patch.set_visible kept for compatibility and convenience." ?

@ImportanceOfBeingErnest ImportanceOfBeingErnest changed the title Deprecate Figure.frameon. Make Figure.frameon a thin wrapper for the patch visibility. Oct 10, 2018
@anntzer
Copy link
Contributor Author

anntzer commented Oct 10, 2018

Please push the changes as desired.

@anntzer
Copy link
Contributor Author

anntzer commented Oct 11, 2018

@ImportanceOfBeingErnest Note, by the way, that having #10088 would have avoided the issue around #10981, namely that we were using one way, internally, to control visibility, whereas the user may be using another knob (although I guess this doesn't argue against having two synonyms for the same setting, but heh).

@ImportanceOfBeingErnest
Copy link
Member

I consider this a major difference. Just leveling up some properties is totally different from having two (or more) separate code paths.
I do share the concerns about the latter, as seen from the discussion you link to. There is also #10882 for which there isn't even a solution as of now.
The main problem is mostly that there seem to be different concepts around and no general strategy being agreed upon, such that everyone just pushes a bit to either side. At the end, the user is the one who's conned.

I will look at the documentation for this latest on Saturday. If someone wants to merge in between, I can also open a new PR.

@ImportanceOfBeingErnest
Copy link
Member

Ok, so how do I push to this PR? Is this the right strategy? It seems that I would need to clone your complete fork to a local folder, which seems a bit strange/overkill.

@QuLogic
Copy link
Member

QuLogic commented Oct 14, 2018

You don't need a new clone; just some remotes pointing to the fork, or use the fork's URL directly.

To pull a PR's contents (if upstream is your remote name for matplotlib):

git fetch upstream pull/11691/head:local-branch-name

You could also pull directly from someone's fork:

git fetch https://github.com/anntzer/matplotlib figureframeon:local-branch-name

(or use the [email protected] SSH address instead of HTTPS.)

To push to @anntzer's fork:

git push https://github.com/anntzer/matplotlib local-branch-name:figureframeon

(or use the [email protected] SSH address instead of HTTPS.)

@ImportanceOfBeingErnest
Copy link
Member

Thanks @QuLogic, I had the feeling that it must be easier.

@anntzer are you ok with those docstrings? Do I now need to remove my approval due to me commiting something here?

@anntzer
Copy link
Contributor Author

anntzer commented Oct 14, 2018

Do I now need to remove my approval due to me commiting something here?

Technically I guess so, although I'd like to suggest a change of policy wrt these "collaborative" PRs (namely that we can just have separate approval by enough devs for each part of the PR).

@timhoffm
Copy link
Member

timhoffm commented Dec 26, 2018

Not sure if this has enough reviewers overall. If @anntzer and @ImportanceOfBeingErnest each approve their collaborative code, that would be 1 plus 1 by me.

IMO, @anntzer if you approve the changes of @ImportanceOfBeingErnest you can self-merge.

@anntzer
Copy link
Contributor Author

anntzer commented Dec 28, 2018

I approve @ImportanceOfBeingErnest's patch.

@anntzer anntzer merged commit feea6da into matplotlib:master Dec 28, 2018
@anntzer anntzer deleted the figureframeon branch December 28, 2018 17:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants