⚡ Sangsakala Proxy 🏠 Home
Skip to content

Fix twin axes ignoring navigation opt-outs (#32528) - #32544

Open
NaumanRafay wants to merge 1 commit into
matplotlib:mainfrom
NaumanRafay:fix-twin-axes-navigation-32528
Open

NaumanRafay wants to merge 1 commit into
matplotlib:mainfrom
NaumanRafay:fix-twin-axes-navigation-32528

Conversation

@NaumanRafay

Copy link
Copy Markdown

Summary

Fixes #32528

Twin axes (created via twinx()/twiny()) ignored navigation opt-outs
(set_navigate(False)) during pan/zoom. The toolbar would add twin
siblings to the navigation trigger list unconditionally, so an axes
explicitly opted out of navigation would still zoom/pan.

Root cause

In NavigationToolbar2._start_event_axes_interaction
(lib/matplotlib/backend_bases.py), the primary axes list is filtered
through _ax_filter, which checks in_axes, get_navigate(), and
can_<method>() together. However, twin siblings pulled via
ax._twinned_axes.get_siblings(ax) were added to axes_to_trigger
directly, without being passed through that same filter:

axes_to_trigger.extend(ax._twinned_axes.get_siblings(ax))

This meant a twin could still be triggered even if it had
set_navigate(False), didn't support the navigation method
(can_pan()/can_zoom()), or fell outside the event location.

Fix

Pass twin siblings through the existing _ax_filter predicate before
adding them, so they're held to the same standard as the primary axes:

axes_to_trigger.extend(
    filter(_ax_filter, ax._twinned_axes.get_siblings(ax)))

This is a minimal, single-line change, no other logic in the function
is affected.

Testing

  • Added test_interactive_twin_navigation, which verifies that the
    shared axis (inherent to twins) still moves together, while the
    independent axis correctly respects set_navigate(False).
  • Added test_twin_navigation_axes_filter, which verifies a twin is
    excluded from axes_to_trigger for all three reasons _ax_filter
    checks: set_navigate(False), an unsupported can_<method>(), and
    an out-of-bounds event location.
  • Full test_backend_bases.py suite passes (219 passed, 1 skipped —
    pre-existing pgf skip unrelated to this change).
  • Verified both new tests fail against unmodified main and pass with
    this fix applied.

Reproduction (from the original issue)

import matplotlib.pyplot as plt
import numpy as np

t = np.linspace(0.0, 10.0, 600)
y = np.linspace(0.0, 1.0, 200)
z = np.exp(-(y[:, None] - 0.5 - 0.3 * np.sin(t)) ** 2 / (2 * 0.04 ** 2))

fig, ax = plt.subplots()
ax2 = ax.twinx()
ax2.imshow(z, extent=[t[0], t[-1], 0.0, 1.0], aspect="auto")
ax2.patch.set_visible(False)
ax.set_zorder(ax2.get_zorder() + 1)
ax.patch.set_visible(False)
ax.plot(t, np.exp(-t / 6.0) - np.exp(-t), color="C0")

ax2.set_navigate(False)
ax.set_forward_navigation_events(False)
plt.show()

Before this fix, dragging a zoom rectangle would zoom both the image
(ax2) and the line plot (ax), despite ax2.set_navigate(False).
After this fix, only ax zooms.

Twin-axes siblings were added to the navigation trigger list via
get_siblings() without being checked against the same predicate
(_ax_filter) already applied to the primary axes. This caused twins
with set_navigate(False), an unsupported can_<method>(), or an
out-of-bounds event to still be triggered during pan/zoom.

Fixes matplotlib#32528
@github-actions

Copy link
Copy Markdown

Thank you for opening your first PR into Matplotlib!

If you have not heard from us in a week or so, please leave a new comment below and that should bring it to our attention. Most of our reviewers are volunteers and sometimes things fall through the cracks. We also ask that you please finish addressing any review comments on this PR and wait for it to be merged (or closed) before opening a new one, as it can be a valuable learning experience to go through the review process.

You can also join us on discourse chat for real-time discussion.

For details on testing, writing docs, and our review process, please see the developer guide.
Please let us know if (and how) you use AI, it will help us give you better feedback on your PR.

We strive to be a welcoming and open project. Please follow our Code of Conduct.

@lucaznch

Copy link
Copy Markdown
Contributor

Hi! Please fill out the AI disclosure section (of the pull request template) with details of your AI use. Thank you!

@NaumanRafay

Copy link
Copy Markdown
Author

AI Disclosure

AI assistance (Claude AI) was used at different points, but the core fix and tests are my own independent work.
My own work: I found the issue, commented claiming it, and debugged my local dev environment (Python version conflicts, pybind11/meson build failures) myself. I wrote the actual fix in this PR : filter(_ax_filter, ax._twinned_axes.get_siblings(ax))) ,independently in over 2 days of my own testing. An earlier, simpler get_navigate()-only approach was discussed with Claude, but I didn’t use it, I arrived at reusing the existing ax_filter predicate on my own, which is more correct since it also covers can() and in_axes, not just the navigate flag. I also wrote both test functions myself and ran all local verification.

Where Claude helped: early codebase orientation (locating _start_event_axes_interaction and _ax_filter), git/GitHub workflow guidance, and reviewing my final diff/tests after I’d already written and run them. At one point a code patch that i wrote with unnecessary, overly broad changes was applied to my working copy, claude identified it, and guided me to look for other solutions.

PR quality check
Use an expressive title, e.g. “Fix title font property precedence” DONE
New and changed code is tested DONE
Plotting related features are demonstrated in an example NOT APPLICABLE
New features and API changes have release notes NOT APPLICABLE
Documentation complies with general and docstring guidelines NOT APPLICABLE

@NaumanRafay

Copy link
Copy Markdown
Author

Hi! Just following up since it’s been about a week since I opened this, happy to make any changes needed. Let me know if there’s anything I can clarify about the fix or the tests.

On AI usage, as requested: I used Claude for early codebase orientation (locating _start_event_axes_interaction and _ax_filter) and general git/GitHub workflow guidance, but the fix itself and both test functions are my own work, written and verified independently. Full disclosure is in the PR description above if useful.

Thanks for your time!

This branch has not been deployed

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

[Bug]: twin axes ignore the navigation opt-outs

2 participants