Skip to content

ENH: Scroll to zoom #30405

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
wants to merge 4 commits into
base: main
Choose a base branch
from
Open

ENH: Scroll to zoom #30405

wants to merge 4 commits into from

Conversation

timhoffm
Copy link
Member

@timhoffm timhoffm commented Aug 7, 2025

Implements a minimal version of #20317, in particular #20317 (comment):

When any of the axes manipulation tools is active (pan or zoom tool), a mouse scroll results in a zoom towards the cursor, keeping aspect ratio.

I've decided to require an active manipulation tool, so that without any active tool the plot cannot be changed (accidentally) - as before. For convenience, scroll-to-zoom is allowed with both the zoom and pan tools. Limiting further feels unnecessarily restrictive.

Zooming is also limited to not having a modifier key pressed. This is because we might later want to add scroll+modifiers for other operations . It's better for now not to react to these at all to not introduce behaviors we later want to change.

Closes #28412.

@timhoffm timhoffm force-pushed the scroll-to-zoom branch 2 times, most recently from c66942e to 838930d Compare August 7, 2025 21:22
@timhoffm timhoffm added this to the v3.11.0 milestone Aug 7, 2025
Implements a minimal version of matplotlib#20317, in particular https://github
.com/matplotlib/pull/20317#issuecomment-2233156558:

When any of the axes manipulation tools is active (pan or zoom tool), a
mouse scroll results in a zoom towards the cursor, keeping aspect ratio.

I've decided to require an active manipulation tool, so that without any
active tool the plot cannot be changed (accidentally) - as before.
For convenience, scroll-to-zoom is allowed with both the zoom and pan
tools. Limiting further feels unnecessarily restrictive.

Zooming is also limited to not having a modifier key pressed. This is
because we might later want to add scroll+modifiers for other operations
. It's better for now not to react to these at all to not introduce
behaviors we later want to change.
Copy link
Member

@QuLogic QuLogic left a comment

Choose a reason for hiding this comment

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

This works pretty well as I would expect.

The only minor annoyance is that every scroll event is pushed to the view history, when it might be nicer to group them a bit, but I'm not sure we have the architecture for that, and could wait for a followup.

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

ianhi commented Aug 8, 2025

An optional feature I really like that I have in mpl-pan-zoom is to autocenter on the plot if you zoom out by a lot. This is what programs like gimp do, it makes it so you don't completely lose track of where the data extents are: https://github.com/mpl-extensions/mpl-pan-zoom/blob/158c68bc3ba5bab785b0a7cb4719774af0a632ac/mpl_pan_zoom/_zoom.py#L76

A second note is that for the web/notebook backends this basically has to capture scrolls if you want it to work properly. Would it be possible to add support for ipympl by adding something like this: https://github.com/mpl-extensions/mpl-pan-zoom/blob/158c68bc3ba5bab785b0a7cb4719774af0a632ac/mpl_pan_zoom/_zoom.py#L32-L33 otherwise this will result in behavior like this: matplotlib/ipympl#222

@ianhi
Copy link
Contributor

ianhi commented Aug 8, 2025

A final thought. I haven't tested this but it may be important to test this on non-euclidean projections. For example see this issue: mpl-extensions/mpl-pan-zoom#10

@timhoffm
Copy link
Member Author

timhoffm commented Aug 8, 2025

A final thought. I haven't tested this but it may be important to test this on non-euclidean projections. For example see this issue: mpl-extensions/mpl-pan-zoom#10

Indeed. In non-rectilinear cases it is not correct to just reduce the data limits. I've limited this functionality to rectilinear Axes for now. I suspect, every projection needs its own recipe. - For example polar plots should not change the theta limits at all.

@anntzer
Copy link
Contributor

anntzer commented Aug 8, 2025

I think this doesn't support scales other than linear for now (well, I guess it depends on the exact behavior one expects); I suspect the proper behavior would be to perform the zooming in pixel coordinates rather than in data coordinates?

re: view history; I guess a "relatively" simple fix would be to record whether the last history entry pushed into the stack was from a scroll-zoom, and if so and if the current entry being pushed is also a scroll-zoom (optionally: a scroll zoom from the same point), then pop the last entry before pushing the new one?

@timhoffm
Copy link
Member Author

I have no time to work on this further in the near future.

I believe the current state is good enough for a start, given that a vast of majority of plots are rectilinear Axes with linear scales:

  • limiting to rectilinear Axes prevents really awkward behavior.
  • zooming on nonlinear scales may not be ideal, but is bearable.
  • batching zoom in the history would be nice but is not essential

I propose to merge the PR as. Improvements by others are welcome but could also be done in follow-up PRs.

@ianhi
Copy link
Contributor

ianhi commented Aug 17, 2025

I propose to merge the PR as. Improvements by others are welcome but could also be done in follow-up PRs.

A second note is that for the web/notebook backends this basically has to capture scrolls if you want it to work properly. Would it be possible to add support for ipympl by adding something like this:

If this is merged I can commit to figuring what, if any, follow ups are needed here and/or in ipympl to make this feature work for webbackends

This PR adds a good and extremely useful thing, and feels to me to be standard behavior among plotting libraries now.

@QuLogic
Copy link
Member

QuLogic commented Aug 19, 2025

I wrote this to try and handle non-linear scales:

diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py
index 815c61a0ee..10634d68a1 100644
--- a/lib/matplotlib/backend_bases.py
+++ b/lib/matplotlib/backend_bases.py
@@ -2593,10 +2593,11 @@ def scroll_handler(event, canvas=None, toolbar=None):
 
         xmin, xmax = ax.get_xlim()
         ymin, ymax = ax.get_ylim()
+        xmin, ymin = ax.transScale.transform((xmin, ymin))
+        xmax, ymax = ax.transScale.transform((xmax, ymax))
 
-        # mouse position in data coordinates
-        x = event.xdata
-        y = event.ydata
+        # mouse position in scaled (e.g., log) data coordinates
+        x, y = ax.transScale.transform((event.xdata, event.ydata))
 
         scale_factor = 1.0 - 0.05 * event.step
         new_xmin = x - (x - xmin) * scale_factor
@@ -2604,6 +2605,10 @@ def scroll_handler(event, canvas=None, toolbar=None):
         new_ymin = y - (y - ymin) * scale_factor
         new_ymax = y + (ymax - y) * scale_factor
 
+        inv_scale = ax.transScale.inverted()
+        new_xmin, new_ymin = inv_scale.transform((new_xmin, new_ymin))
+        new_xmax, new_ymax = inv_scale.transform((new_xmax, new_ymax))
+
         ax.set_xlim(new_xmin, new_xmax)
         ax.set_ylim(new_ymin, new_ymax)
 

If that makes sense, should I push that here?

@timhoffm
Copy link
Member Author

timhoffm commented Aug 19, 2025

Looks good, but I can’t test right now. If somebody has verified that this is the „canonically expected“ behavior e.g. on a log scale, you are welcome to push to this branch.

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.

[ENH]: Zoom in/out on rolling the mouse wheel
4 participants
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy