sceneRect().translated(x,y) 不适用于缩放

sceneRect().translated(x,y) doesn't work properly with zoom

一个月前我在问,如何翻译场景,我得到了很大的帮助并了解了它是如何工作的,在与 it/adding 工作人员一起工作一个月后,我注意到当你放大得非常近时几何或其他地方,翻译越来越有价值。

您必须放大到非常接近几何图形才能看到问题。这是一些代码:

from PyQt5.QtGui import QColor, QPolygonF, QPen, QBrush
from PyQt5.QtCore import Qt, QPointF, QPoint, pyqtSignal
from PyQt5.QtWidgets import QDialog, QVBoxLayout, QGraphicsView, QGraphicsScene, QGraphicsPolygonItem, QApplication, \
    QFrame, QSizePolicy
points_list = [[60.1, 19.6, 0.0], [60.1, 6.5, 0.0], [60.1, -6.5, 0.0], [60.1, -19.6, 0.0], [60.1, -19.6, 0.0],
               [20.0, -19.6, 0.0], [-20, -19.6, 0.0], [-60.1, -19.6, 0.0], [-60.1, -19.6, 0.0], [-60.1, -6.5, 0.0],
               [-60.1, 6.5, 0.0], [-60.1, 19.6, 0.0], [-60.1, 19.6, 0.0], [-20.0, 19.6, 0.0], [20.0, 19.6, 0.0],
               [60.1, 19.6, 0.0]]


class MainWindow(QDialog):
    def __init__(self, parent=None):
        QDialog.__init__(self, parent=parent)
        self.create()

    def create(self, **kwargs):
        main_layout = QVBoxLayout()
        graphics = MainGraphicsWidget()
        main_layout.addWidget(graphics)
        self.setLayout(main_layout)

class MainGraphicsWidget(QGraphicsView):
    zoom_signal = pyqtSignal(bool)

    def __init__(self, parent=None):
        super(MainGraphicsWidget, self).__init__(parent)
        self._scene = QGraphicsScene(backgroundBrush=Qt.gray)
        self.__zoom = 0
        self.setScene(self._scene)
        self.setTransformationAnchor(QGraphicsView.NoAnchor)
        #self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse)
        self.setResizeAnchor(QGraphicsView.AnchorUnderMouse)
        self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setBackgroundBrush(QBrush(QColor(30, 30, 30)))
        self.setFrameShape(QFrame.NoFrame)
        self.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding))
        # self.sceneRect = self._scene.sceneRect()
        self.testButton = GraphicsButton()
        self._scene.addItem(self.testButton)
        self.startPos = None


    def mousePressEvent(self, event):
        if event.modifiers() & Qt.AltModifier and event.button() == Qt.LeftButton:
            self.startPos = event.pos()
        else:
            super(MainGraphicsWidget, self).mousePressEvent(event)

    def mouseMoveEvent(self, event):
        modifierPressed = QApplication.keyboardModifiers()
        if self.startPos is not None:
            delta = self.startPos - event.pos()
            transform = self.transform()
            deltaX = delta.x() / transform.m11()
            deltaY = delta.y() / transform.m22() 
            self.setSceneRect(self.sceneRect().translated(deltaX, deltaY))
            self.startPos = event.pos()
        else:
            super(MainGraphicsWidget, self).mouseMoveEvent(event)

    def mouseReleaseEvent(self, event):
        self.startPos = None
        super(MainGraphicsWidget, self).mouseReleaseEvent(event)


    def wheelEvent(self, event):
        if event.angleDelta().y() > 0:
            factor = 1.25
            self.__zoom += 1
        else:
            factor = 0.8
            self.__zoom -= 1
        self.scale(factor, factor)
        self.zoom_signal.emit(self.__zoom < 10)


class GraphicsButton(QGraphicsPolygonItem):
    def __init__(self, parent=None):
        super(GraphicsButton, self).__init__(parent)
        self.myPolygon = QPolygonF([QPointF(v1, v2) for v1, v2, v3 in points_list])
        self.setPen(QPen(QColor(0, 0, 0), 0, Qt.SolidLine, Qt.FlatCap, Qt.MiterJoin))
        self.setPolygon(self.myPolygon)
        self.setBrush(QColor(220, 40, 30))


if __name__ == '__main__':
    import sys
    app = QApplication(sys.argv)
    window = MainWindow()
    window.setGeometry(500, 100, 500, 900)
    window.show()
    sys.exit(app.exec_())

(翻译开始使用 Alt + 左键单击

我想问题是当你放大得太近时,你的光标仍然沿着相同的坐标移动(它们不会变小)并且它开始平移得更慢,我不考虑计算我的代码。但为什么有时我的 sceneRect().translated() 会完全停止移动?我是否放大到至少我的光标(1x,1y)没有穿过的点?

所以我的问题是,如果我的理论正确,有人可以告诉我如何计算 delta、放大和缩小吗?谢谢你,对不起我的英语

编辑:这是您需要放大多少倍才能看到问题的屏幕截图。

sceneRect 位于场景坐标中,而不是视图坐标中,因此对于您的计算,您必须使用 mapToScene 方法将鼠标点转换为场景坐标。此修改也应应用于场景中投影的视口矩形,因为它不等于 sceneRect

class MainGraphicsWidget(QGraphicsView):
    zoom_signal = pyqtSignal(bool)

    def __init__(self, parent=None):
        # ...
        self.startPos = QPointF()

    def mousePressEvent(self, event):
        if event.modifiers() & Qt.AltModifier and event.button() == Qt.LeftButton:
            self.startPos = self.mapToScene(event.pos())
        else:
            super(MainGraphicsWidget, self).mousePressEvent(event)

    def mouseMoveEvent(self, event):
        if not self.startPos.isNull():
            delta = self.startPos - self.mapToScene(event.pos())
            r = self.mapToScene(self.viewport().rect()).boundingRect()
            self.setSceneRect(r.translated(delta))
        else:
            super(MainGraphicsWidget, self).mouseMoveEvent(event)

    def mouseReleaseEvent(self, event):
        self.startPos = QPointF()
        super(MainGraphicsWidget, self).mouseReleaseEvent(event)

    def wheelEvent(self, event):
        # ...

以上答案部分正确。我的看法是,您的方法是尝试通过考虑视口比例来平移 sceneRect。 “m11 和 m22”。但是,当您放大时,您的视口比例将小于 1。 平移函数不接受小于 1 的值。尝试在放大时打印出 DeltaX,您会看到只有当 DeltaX > 1 时场景才会移动。

您可以采取的两种方法是:

A) 当 Delta 轴大于 0 时,手动向上舍入到 1.1 之类的值,即大于 1。

transform = self.transform()
deltaX = delta.x() / transform.m11()
deltaY = delta.y() / transform.m22()
            
if 1 > deltaX > 0.1:
   deltaX = 1.1
            
if 1 > deltaY > 0.1:
   deltaY = 1.1

B) 像上面提到的 eyllanesc 一样,将视口投影到场景中,因此所做的任何转换都将在相对于视口 space 的场景 space 中。此外,为了使缩放位置保持一致,您还可以在缩放场景时将鼠标位置转换为场景 space。最后,eyllanesc 添加了一行代码,该代码是多余的并且可以正确翻译。按下鼠标键时不需要重新设置鼠标原点位置。

因此你可以得到这样的东西。

def __init__(self, parent):
    super(GraphicsThingy, self).__init__(parent)

    self.setTransformationAnchor(QGraphicsView.NoAnchor)
    self.setResizeAnchor(QGraphicsView.NoAnchor)

    self.installEventFilter(self)

    self.mousePos = None

def wheelEvent(self, event):

    if event.modifiers() & Qt.ControlModifier:
        scaleDelta = event.angleDelta().y() / 35
        factor = pow(self.zoomMultiplier, scaleDelta)

        originalPos = self.mapToScene(event.pos())

        self.scale(factor, factor)
        newPos = self.mapToScene(event.pos())

        deltaPos = newPos - originalPos
        self.translate(deltaPos.x(), deltaPos.y())

    event.accept()

def mousePressEvent(self, event):

    if event.button() == Qt.MiddleButton:
        self.mousePos = self.mapToScene(event.pos())

    event.accept()

def mouseMoveEvent(self, event):

    if self.movePos is not None:
        delta = self.mousePos - self.mapFromScene(event.pos())
        # Keeps scrolling speed consistent with zoom. Max(v,1) Ensures that the scale value will not multiply the scrolling speed. 
        delta.setX(delta.x() / max(self.transform().m11(),1))
        delta.setY(delta.y() / max(self.transform().m22(),1))

        # Change scene scene rect position based on delta
        rect = self.mapToScene(self.viewport().rect()).boundingRect()
        self.setSceneRect(rect.translated(delta))
        
        self.update()

    event.accept()

def mouseReleaseEvent(self, event):

    self.mousePos = None