qgis / QGIS

QGIS is a free, open source, cross platform (lin/win/mac) geographical information system (GIS)
https://qgis.org
GNU General Public License v2.0
10.39k stars 2.98k forks source link

mouse-scroll: zoom and recenter #52633

Closed sdikiy closed 1 year ago

sdikiy commented 1 year ago

Feature description

Hi, in last QGIS I can`t find option

2. zoom and recenter

it make me unhappy :-) https://github.com/qgis/QGIS/issues/10474

Regards

Additional context

No response

YoannQDQ commented 1 year ago

This was removed from QGIS 2.18, when QgsMapCanvas::setWheelAction was deprecated. Not sure exactly why, but it was definitely intentional, so I don't see it making a comeback anytime soon.

I'd argue that zooming and recentering with the mouse wheel is really cumbersome though, as it makes the canvas drift on each tick of the wheel.

sdikiy commented 1 year ago

Yes, unfortunately it will most likely never come back...

But about the difficulty of use, I have to disagree with you here, once you get used to this behavior, you will never give it up. https://forum.kicad.info/t/scroll-with-mouse-wheel-action-zoom-in-and-out/20586/5

Here's another usage video, panning and zooming only with the mouse wheel. https://youtu.be/vyUrjnFaZcM?t=131

YoannQDQ commented 1 year ago

Interesting viewpoint. Still, you can also navigate with only the wheel with the current behavior (and most people familiar with Google maps on desktop do it). Seems like the recenter on wheel is great when moving away from something while zooming out, but no so much when zooming in, as show at the end of the video where the user fumbles around their target.

I'm closing this issue as "won't fix" now, but feel free to reopen it if you'd like to further make your point.

sdikiy commented 1 year ago

Are you closing? but it's not a "Bug" it's a "Feature Request"

nyalldawson commented 1 year ago

Feature requests can still be closed as a "won't fix".

nyalldawson commented 1 year ago

Or for a more helpful answer -- this won't get fixed in QGIS itself. But it's entirely possible to write a small plugin which overrides the default behavior for this tool and restores a zoom and recenter mode. That's the way forward here.

sdikiy commented 1 year ago

I haven't looked into QGIS code in a while, but a few years ago I thought it was a fairly non-trivial task. I’m sorry for taking up your time.

YoannQDQ commented 1 year ago

Quick and dirty fix: save the following snippet to a file named startup.py in your QGIS home folder On Windows it would be something like 'C:/Users/sdikiy/AppData/Roaming/QGIS/QGIS3/startup.py'

To check the correct location, type QStandardPaths.writableLocation(QStandardPaths.AppDataLocation) in the QGIS python console.

This script will be run everytime QGIS starts. It installs an event filter on the QgsMapCanvas to intercept wheel events.

from qgis.utils import iface
from qgis.core import QgsPointXY, QgsSettings
from qgis.PyQt.QtCore import QObject, QEvent, Qt

class MyFilter(QObject):

    def eventFilter(self, obj, e):
        if e.type() != QEvent.Wheel:
            return False
        canvas = iface.mapCanvas()
        tool = canvas.mapTool()
        if tool:
            tool.wheelEvent(e)
            if e.isAccepted():
                return True

        if e.angleDelta().y() == 0:
            e.accept()
            return True

        settings = QgsSettings()
        reverseZoom = settings.value("qgis/reverse_wheel_zoom", False, bool)
        zoomIn = e.angleDelta().y() < 0 if reverseZoom else e.angleDelta().y() > 0
        zoomFactor = 1.0 / canvas.zoomInFactor() if zoomIn else canvas.zoomOutFactor()

        # "Normal" mouse have an angle delta of 120, precision mouses provide data faster, in smaller steps
        zoomFactor = 1.0 + (zoomFactor - 1.0) / 120.0 * abs(e.angleDelta().y())

        if e.modifiers() & Qt.ControlModifier:
            # holding ctrl while wheel zooming results in a finer zoom
            zoomFactor = 1.0 + (zoomFactor - 1.0) / 20.0

        signedWheelFactor = 1 / zoomFactor if zoomIn else zoomFactor

        oldCenter = canvas.center()
        mousePos = QgsPointXY(canvas.getCoordinateTransform().toMapCoordinates(e.position().x(), e.position().y()))
        newCenter = QgsPointXY(
            mousePos.x() + ((oldCenter.x() - mousePos.x()) * signedWheelFactor),
            mousePos.y() + ((oldCenter.y() - mousePos.y()) * signedWheelFactor),
        )

        canvas.zoomByFactor(signedWheelFactor, mousePos)  # WheelZoomAndRecenter
        # canvas.zoomByFactor(signedWheelFactor, oldCenter)  # WheelZoom
        # canvas.zoomByFactor(signedWheelFactor, newCenter)  # WheelZoomToMouseCursor
        e.accept()
        return True

filter = MyFilter()
iface.mapCanvas().viewport().installEventFilter(filter)
sdikiy commented 1 year ago

Thank you!