FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

Correctly treat pan/zoom events of overlapping axes. by raphaelquast · Pull Request #27148 · matplotlib/matplotlib · GitHub

Repository navigation

Correctly treat pan/zoom events of overlapping axes. - #27148

Merged
ksunden merged 7 commits into
matplotlib:mainfrom
raphaelquast:nested_ax_zoom
Mar 27, 2024
Merged

ksunden merged 7 commits into
matplotlib:mainfrom
raphaelquast:nested_ax_zoom

Conversation

Copy link
Copy Markdown
Contributor

Followup for #22347

Copy link
Copy Markdown
Contributor

It may not make sense to roll into this PR, but while you’re looking at this could you check the behavior of 3D axes as well? They have different pan/rotate/zoom handling.

raphaelquast reopened this Oct 19, 2023

Copy link
Copy Markdown
Contributor Author

@scottshambaugh

Sorry to disappoint you, but this PR is already going on for a very long time and I really like to get this merged in a reasonble time. Extending it with additional functionalities is unfortunately out of scope for me at the moment (also my experience with mpl3d is limited).

ksunden commented Oct 20, 2023

Copy link
Copy Markdown
Member

The newly added methods/__init__ arguments should be added to the pyi stub file (with type hints, which I think are mostly just bool | Literal["auto"] here)

Copy link
Copy Markdown
Contributor Author

@ksunden Stub-files are updated now!

ksunden commented Oct 30, 2023

Copy link
Copy Markdown
Member

I think this largely works as desired/documented. My concern is that I think it is confusing that ax.patch.set_facecolor("none") behaves differently to ax.patch.set_visible(False) here... visually they are identical, but they differ in interactivity because the "auto" treatment checks patch.is_visible().

I'm not sure that that should be blocking, necessarily, but I do think that transparent axes are one of the most sensible cases for overlapping axes (along with things like insets, etc) and I'm not certain that people will have used set_visible rather than set_facecolor("none")

And of course if we do decide to key off of the alpha value, we would need to decide what cutoff to use:

  • alpha=0 means pass events, any nonzero value absorbs
  • alpha=1 absorbs events, any non-one value passes
  • something in between? 0.5 as a cutoff?

I would lean towards one of the first two, I think though not sure which one. (Under the assumption we want to key off of alpha in the first palace, which I'm not totally convinced of)

Copy link
Copy Markdown
Contributor Author

@ksunden Thanks for taking a close look on the PR!

Let me add my 2 cents to your concerns:

In my opinion, the current implementation still seems the most intuitive... to clarify:

  • Axes only forward events by default if the background is explicitly hidden (e.g. via ax.patch.set_visible(False))
    • I think this is quite intuitive because even if the patch is set to transparent, it is "still present in the figure" (e.g. changing the color would make it immediately visible) while a hidden patch only becomes visible if it is explicitly shown (e.g. via ax.patch.set_visible(True)) independent of all other patch-properties.
    • In short, this means that the presence of a patch directly triggers capturing of events.

Considering the effects of transparency as an additional trigger for capturing behavior, I think it would add more confusion than it solves.

  • using an alpha-value other than 0 or 1 would be quite arbitrary
  • in my opinion, changing the color of a patch should not interfere with any type of low-level behavior of an axes.

In 99% of the case, people will want axes that capture events. So even though less people use set_visible, the ones who want to temper with the capturing behavior will find it in the docs and all other users that only use set_facecolor("none") (or set_alpha(0)) do not interfere with the capturing behavior at all.

tacaswell added this to the v3.9.0 milestone Nov 9, 2023
- ``True``: Forward navigation events to axes below.
- ``False``: Execute navigation events only on this axes.
- ``"auto"``: Use the default behaviour
(``True`` for axes with an invisible patch and ``False`` otherwise).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

be explicit about what "invisible" means here (obj.set_visible(False))

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

added a note on what is considered a "visible patch"

Comment thread lib/matplotlib/backend_bases.py Outdated
Comment thread lib/matplotlib/backend_bases.py Outdated
Comment thread lib/matplotlib/backend_bases.py Outdated

Copy link
Copy Markdown
Contributor Author

@tacaswell thanks for having a look at the PR!
I addressed all comments so this should be ready for a re-review!

Copy link
Copy Markdown
Member

Do we have a theory as to why coverage looks wrong? Are we sure that the test is actually running on CI?

Copy link
Copy Markdown
Contributor Author

@tacaswell thanks for the follow-up!

Good point... I assumed that pytest grabs it just like any other test... however looking at codecov it seems that something strange is going on...

The test itself is fully covered so I guess it was executed, but I can't really wrap my head around how it is even possible that the resulting coverage looks like this (in backend_bases.py here)...

I'll try to look into this as soon as I find some time!

Copy link
Copy Markdown
Member

At least locally for me they are being discovered, but I can reproduce the lack of coverage. It looks like it only ever runs until the if len(axes): return [] fast-exit.

Copy link
Copy Markdown
Member

Any update on this?

Copy link
Copy Markdown
Contributor Author

Hey, sorry that I've disappeared after pushing for more speed on this... There were some things that kept me busy.
I'll try to make some time and have a look in the next few days!

Copy link
Copy Markdown
Member

No worries! For volunteers basically everything else in your life should take priority and we understand stuff comes up.

ksunden commented Mar 6, 2024

Copy link
Copy Markdown
Member

@raphaelquast gentle ping as we are rounding up things for 3.9

raphaelquast commented Mar 6, 2024 •
edited
Loading

Copy link
Copy Markdown
Contributor Author

Hey @ksunden, thanks for pinging.
It's really a pity but I'm all fed up with other things at the moment so I can't promise a timely update here.

As I see it the only thing that holds this back is the mysterious unittests that somehow don't run as expected.
Everyting else is done (api-change-notice, stub-files, etc.).


I quickly did another check if I can reproduce this but unfortunately I really don't get why the tests are not showing the expected coverage.

To clarify, I just did the following crude test to be absolutely sure the lines are covered locally on my side:

  • I add print("Found axes to trigger:", axes_to_trigger) at the bottom of the _start_event_axes_interaction(..) function of backend_bases.py (e.g. at a position that is supposedly not covered by the tests)
  • I run python -m pytest tests -k test_interactive_pan_zoom_events -s to run my test (-s flag to see print outputs)
... and this is what pytest gives me: [click to show]
(mpl-dev) C:\Users\Admin\Projects\matplotlib\lib\matplotlib>python -m pytest tests -k test_interactive_pan_zoom_events -s
============================================================= test session starts ==============================================================
platform win32 -- Python 3.11.6, pytest-7.4.2, pluggy-1.3.0
rootdir: C:\Users\Admin\Projects\matplotlib
configfile: pytest.ini
plugins: anyio-4.0.0, cov-4.1.0, rerunfailures-12.0, timeout-2.2.0, xdist-3.3.1
collected 9344 items / 9296 deselected / 2 skipped / 48 selected

tests\test_backend_bases.py Found axes to trigger: [<Axes: label='ax_t_twin'>, <Axes: label='ax_t'>, <Axes: label='ax_b_twin'>, <Axes: label='ax_b'>]
.Found axes to trigger: [<Axes: label='ax_t_twin'>, <Axes: label='ax_t'>, <Axes: label='ax_b_twin'>, <Axes: label='ax_b'>]
.Found axes to trigger: [<Axes: label='ax_t_twin'>, <Axes: label='ax_t'>, <Axes: label='ax_b_twin'>, <Axes: label='ax_b'>]
.Found axes to trigger: [<Axes: label='ax_t_twin'>, <Axes: label='ax_t'>, <Axes: label='ax_b_twin'>, <Axes: label='ax_b'>]
.Found axes to trigger: [<Axes: label='ax_t_twin'>, <Axes: label='ax_t'>, <Axes: label='ax_b_twin'>, <Axes: label='ax_b'>]
.Found axes to trigger: [<Axes: label='ax_t_twin'>, <Axes: label='ax_t'>, <Axes: label='ax_b_twin'>, <Axes: label='ax_b'>]
.Found axes to trigger: [<Axes: label='ax_t_twin'>, <Axes: label='ax_t'>, <Axes: label='ax_b_twin'>, <Axes: label='ax_b'>]
.Found axes to trigger: [<Axes: label='ax_t_twin'>, <Axes: label='ax_t'>, <Axes: label='ax_b_twin'>, <Axes: label='ax_b'>]
.Found axes to trigger: [<Axes: label='ax_t_twin'>, <Axes: label='ax_t'>]
.Found axes to trigger: [<Axes: label='ax_t_twin'>, <Axes: label='ax_t'>]
.Found axes to trigger: [<Axes: label='ax_t_twin'>, <Axes: label='ax_t'>]
.Found axes to trigger: [<Axes: label='ax_t_twin'>, <Axes: label='ax_t'>]
.Found axes to trigger: [<Axes: label='ax_t_twin'>, <Axes: label='ax_t'>]
.Found axes to trigger: [<Axes: label='ax_t_twin'>, <Axes: label='ax_t'>]
.Found axes to trigger: [<Axes: label='ax_t_twin'>, <Axes: label='ax_t'>]
.Found axes to trigger: [<Axes: label='ax_t_twin'>, <Axes: label='ax_t'>]
.Found axes to trigger: [<Axes: label='ax_t_twin'>, <Axes: label='ax_t'>]
.Found axes to trigger: [<Axes: label='ax_t_twin'>, <Axes: label='ax_t'>]
.Found axes to trigger: [<Axes: label='ax_t_twin'>, <Axes: label='ax_t'>]
.Found axes to trigger: [<Axes: label='ax_t_twin'>, <Axes: label='ax_t'>]
.Found axes to trigger: [<Axes: label='ax_t_twin'>, <Axes: label='ax_t'>, <Axes: label='ax_b_twin'>, <Axes: label='ax_b'>]
.Found axes to trigger: [<Axes: label='ax_t_twin'>, <Axes: label='ax_t'>, <Axes: label='ax_b_twin'>, <Axes: label='ax_b'>]
.Found axes to trigger: [<Axes: label='ax_t_twin'>, <Axes: label='ax_t'>, <Axes: label='ax_b_twin'>, <Axes: label='ax_b'>]
.Found axes to trigger: [<Axes: label='ax_t_twin'>, <Axes: label='ax_t'>, <Axes: label='ax_b_twin'>, <Axes: label='ax_b'>]
.Found axes to trigger: [<Axes: label='ax_t'>, <Axes: label='ax_b'>]
.Found axes to trigger: [<Axes: label='ax_t'>, <Axes: label='ax_b'>]
.Found axes to trigger: [<Axes: label='ax_t'>, <Axes: label='ax_b'>]
.Found axes to trigger: [<Axes: label='ax_t'>, <Axes: label='ax_b'>]
.Found axes to trigger: [<Axes: label='ax_t'>, <Axes: label='ax_b'>]
.Found axes to trigger: [<Axes: label='ax_t'>, <Axes: label='ax_b'>]
.Found axes to trigger: [<Axes: label='ax_t'>, <Axes: label='ax_b'>]
.Found axes to trigger: [<Axes: label='ax_t'>, <Axes: label='ax_b'>]
.Found axes to trigger: [<Axes: label='ax_t'>]
.Found axes to trigger: [<Axes: label='ax_t'>]
.Found axes to trigger: [<Axes: label='ax_t'>]
.Found axes to trigger: [<Axes: label='ax_t'>]
.Found axes to trigger: [<Axes: label='ax_t'>]
.Found axes to trigger: [<Axes: label='ax_t'>]
.Found axes to trigger: [<Axes: label='ax_t'>]
.Found axes to trigger: [<Axes: label='ax_t'>]
.Found axes to trigger: [<Axes: label='ax_t'>]
.Found axes to trigger: [<Axes: label='ax_t'>]
.Found axes to trigger: [<Axes: label='ax_t'>]
.Found axes to trigger: [<Axes: label='ax_t'>]
.Found axes to trigger: [<Axes: label='ax_t'>, <Axes: label='ax_b'>]
.Found axes to trigger: [<Axes: label='ax_t'>, <Axes: label='ax_b'>]
.Found axes to trigger: [<Axes: label='ax_t'>, <Axes: label='ax_b'>]
.Found axes to trigger: [<Axes: label='ax_t'>, <Axes: label='ax_b'>]
.

=============================================== 48 passed, 2 skipped, 9296 deselected in 26.49s ================================================

... which is what I was expecting to get if the function was executed properly

So long story short, I unfortunately cannot reproduce the lack of coverage so I am quite stuck.
Since my time is very limited at the moment I don't know how to proceed here as I don't have the time to dig into reasons why the lines are apparently executed but not reported to codecov.

Any help here is highly appreciated! (I can try my best to finalize this for 3.9 but I'd need a way to reproduce it)
My last guess is that it might somehow be OS-related?? (I'm on windows)

QuLogic commented Mar 12, 2024

Copy link
Copy Markdown
Member

At least locally for me they are being discovered, but I can reproduce the lack of coverage. It looks like it only ever runs until the if len(axes): return [] fast-exit.

I can't reproduce locally after a rebase to latest main (though I didn't try before the rebase); maybe that will help here?

Copy link
Copy Markdown
Contributor Author

@QuLogic Thanks a lot for taking a look! I updated the branch to the latest main... let's see if that helps!

Copy link
Copy Markdown
Contributor Author

@QuLogic I rebased to the latest main but now the macOS_py311 test on azure is failing with the (I guess unrelated) error:
Path does not exist: /Users/runner/work/1/s/result_images

QuLogic commented Mar 13, 2024

Copy link
Copy Markdown
Member

It appears that someone re-ran that, as it seems to be passing now. The code coverage looks pretty good now; only the early returns are uncovered.

Copy link
Copy Markdown
Contributor Author

OK perfect! But codecov/project/tests is still failing (even though all added/changed lines are covered)?

Co-authored-by: Elliott Sales de Andrade <quantum.analyst@gmail.com>

ksunden commented Mar 27, 2024

Copy link
Copy Markdown
Member

GTK dll loading failures on appveyor appears to be unrelated, reproducible in other recent PRs.

ksunden merged commit ae73ddb into matplotlib:main Mar 27, 2024

ksunden commented Mar 27, 2024

Copy link
Copy Markdown
Member

Thanks for your perseverance on this one, it was a long journey, but glad we got there!

Copy link
Copy Markdown
Contributor Author

Awesome to see that this is finally merged!
I learned a lot on the road and I'm really happy that this journey has come to a successful ending.
Thanks also from my side for all the help along the way!

Copy link
Copy Markdown
Member

I hope we hear from you again @raphaelquast !

Copy link
Copy Markdown
Contributor Author

@tacaswell definitely! I do have a few things that I'd like to see implemented and I hope that I find some time to contribute!

This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants


Back | FazBrowse Home | New Git URL