缩放到鼠标位置 - pyside6

Zoom to mouse position - pyside6

您好,我正在尝试实现可缩放至鼠标位置的简单图像小部件。我在 Zooming in/out on a mouser point ? and https://doc.qt.io/qt-5/qtwidgets-widgets-imageviewer-example.html 结合了这个例子。然而,图像没有按预期缩放,比例尺也没有适当更新。这是我的代码:

import sys
from PySide6 import QtWidgets
from PySide6.QtCore import Qt
from PIL.ImageQt import ImageQt
from PySide6.QtGui import QPixmap
from PySide6.QtWidgets import QDialog, QVBoxLayout, QLabel, QScrollArea


class MyScrollArea(QScrollArea):
    def __init__(self, imageWidget):
        # initialize widget
        super().__init__()
        self.setWidget(imageWidget)
        self.myImageWidget = imageWidget
        self.oldScale = 1
        self.newScale = 1

    def wheelEvent(self, event) -> None:
        if event.angleDelta().y() < 0:
            # zoom out
            self.newScale = 0.8
        else:
            # zoom in
            self.newScale = 1.25

        # compute scrollbar positions
        scrollBarPosHorizontal = self.horizontalScrollBar().value()
        scrollBarPosVertical = self.verticalScrollBar().value()
        deltaToPos = (event.position() / self.oldScale) - (self.myImageWidget.pos() / self.oldScale)
        delta = deltaToPos * self.newScale - deltaToPos * self.oldScale

        # resize image
        self.myImageWidget.resize(self.myImageWidget.size() * self.newScale)

        # set scrollbars
        self.horizontalScrollBar().setValue(scrollBarPosHorizontal+delta.x())
        self.verticalScrollBar().setValue(scrollBarPosVertical+delta.y())

        # save old scale
        self.oldScale = self.newScale

class ImageViewer(QDialog):
    def __init__(self, img):
        # initialize widget
        super().__init__()
        self.setWindowTitle('Zoom example')
        self.imageWidget = QLabel()
        self.imageWidget.installEventFilter(self)
        self.imageWidget.setAlignment(Qt.AlignCenter)
        self.pixmap = QPixmap.fromImage(img)
        self.imageWidget.setPixmap(self.pixmap)

        # create scroll area
        self.scrollArea = MyScrollArea(self.imageWidget)

        # insert to layout
        self.layout = QVBoxLayout()
        self.layout.addWidget(self.scrollArea)
        self.setLayout(self.layout)


if __name__ == '__main__':
    # prepare app
    app = QtWidgets.QApplication(sys.argv)

    # prepare image
    image = ImageQt("test.png")

    # create viewer widget
    MyWidget = ImageViewer(image)
    MyWidget.show()

    # close app
    sys.exit(app.exec())

图像根本不缩放到鼠标点。我做错了什么?

主要问题是默认情况下,当 QLabel 调整大小时,像素图不会调整大小,因此必须使用 setScaledContents(True)

请注意,用于缩放和平移的算法效果不佳,因为它没有正确考虑滚动条范围的变化。

我提出了一个替代版本,实际上 可以在鼠标上缩放,类似于在普通图像 viewers/editors 和地图查看器中发生的情况。诀窍是将鼠标位置映射到标签,并根据 scaled 位置获得增量:

class MyScrollArea(QScrollArea):
    def __init__(self, imageWidget):
        # ...
        imageWidget.setScaledContents(True)

    # ...

    def wheelEvent(self, event) -> None:
        if event.angleDelta().y() < 0:
            # zoom out
            self.newScale = 0.8
        else:
            # zoom in
            self.newScale = 1.25

        widgetPos = self.myImageWidget.mapFrom(self, event.position())

        # resize image
        self.myImageWidget.resize(self.myImageWidget.size() * self.newScale)

        delta = widgetPos * self.newScale - widgetPos
        self.horizontalScrollBar().setValue(
            self.horizontalScrollBar().value() + delta.x())
        self.verticalScrollBar().setValue(
            self.verticalScrollBar().value() + delta.y())
        
        self.oldScale = self.newScale

请注意,QLabel 不太适合此类用途(尤其是大图像和高缩放值)。我强烈建议您考虑切换到 Graphics View Framework.