动画 QGraphicsView fitInView 方法

Posted

技术标签:

【中文标题】动画 QGraphicsView fitInView 方法【英文标题】:Animate QGraphicsView fitInView method 【发布时间】:2022-01-18 04:01:33 【问题描述】:

假设我有一个简单的 QGraphicsView 和 QGraphicsScene,我想为视图设置动画以平滑滚动/缩放到场景中的任意 QRecF。我知道您可以调用view.fitInView()view.centerOn() 在矩形上“加框”,但是它们都没有提供太多动画。我可以反复调用view.centerOn() 来很好地为滚动设置动画,但这不能处理缩放。我相信我需要修改视图的视口,但我不太确定该怎么做(调用view.setSceneRect() 似乎很有希望,但这只是设置视图可以管理的区域,而不是它当前“查看”的区域)。

基于一些 C++ 答案,我想出了如何在大多数情况下重写 fitInView 方法,但是我被困在我实际将视图转换为 look at 场景的不同部分的部分,而没有打电话给centerOn。这是我目前所拥有的:

view = QtWidgets.QGraphicsView()
scene = QtWidgets.QGraphicsScene()
targetRect = QtCore.QRectF(500, 500, 500, 500)

viewRect = view.viewport().rect()
sceneRect = view.transform().mapRect(targetRect)

xratio = viewRect.width() / sceneRect.width()
yratio = viewRect.height() / sceneRect.height()

# keep aspect
xratio = yratio = min(xratio, yratio)

# so... now what to do with the ratio? I need to scale the view rect somehow.

# animation
start = view.mapToScene(viewRect).boundingRect()
end = sceneRect # I guess?

animator = QtCore.QVariantAnimation()
animator.setStartValue(start)
animator.setEndValue(end)
animator.valueChanged.connect(view.????) # what am I setting here?
animator.start()

【问题讨论】:

【参考方案1】:

我走了许多曲折的道路,最终得到了一个非常简单的答案,我想我会发布它以防万一有人发现同时为滚动和缩放设置动画很有用:

view = QtWidgets.QGraphicsView()
scene = QtWidgets.QGraphicsScene()
targetRect = QtCore.QRectF(500, 500, 500, 500)


animator = QtCore.QVariantAnimation()
animator.setStartValue(view.mapToScene(view.viewport().rect()).boundingRect())
animator.setEndValue(targetRect)
animator.valueChanged.connect(lambda x: view.fitInView(x, QtCore.Qt.KeepAspectRatio))
animator.start()

【讨论】:

【参考方案2】:

一个简单的基本解决方案是映射目标矩形并将该值用作动画的结束值。

请注意,此解决方案并不是真正的最佳解决方案,原因有两个:

它完全依赖于fitInView(),它必须通过检查当前视口大小在动画的每次迭代中计算整个整个场景的变换;更好(但更复杂)的实现是使用 scale()setTransform() 并在当前映射的变换矩形上调用 centerOn(); 由于场景矩形可能比视口显示的要小,因此缩小可能会有点尴尬;
class SmoothZoomGraphicsView(QtWidgets.QGraphicsView):
    def __init__(self):
        super().__init__()
        scene = QtWidgets.QGraphicsScene()
        self.setScene(scene)
        self.pixmap = scene.addPixmap(QtGui.QPixmap('image.png'))
        self.animation = QtCore.QVariantAnimation()
        self.animation.valueChanged.connect(self.smoothScale)
        self.setTransformationAnchor(self.AnchorUnderMouse)

    def wheelEvent(self, event):
        viewRect = self.mapToScene(self.viewport().rect()).boundingRect()
        # assuming we are using a basic x2 or /2 ratio, we need to remove
        # a quarter of the width/height and translate to half the position
        # betweeen the current rectangle and cursor position, or add half
        # the size and translate to a negative relative position
        if event.angleDelta().y() > 0:
            xRatio = viewRect.width() / 4
            yRatio = viewRect.height() / 4
            translate = .5
        else:
            xRatio = -viewRect.width() / 2
            yRatio = -viewRect.height() / 2
            translate = -1
        finalRect = viewRect.adjusted(xRatio, yRatio, -xRatio, -yRatio)
        cursor = self.viewport().mapFromGlobal(QtGui.QCursor.pos())
        if cursor in self.viewport().rect():
            line = QtCore.QLineF(viewRect.center(), self.mapToScene(cursor))
            finalRect.moveCenter(line.pointAt(translate))
        self.animation.setStartValue(viewRect)
        self.animation.setEndValue(finalRect)
        self.animation.start()

    def smoothScale(self, rect):
        self.fitInView(rect, QtCore.Qt.KeepAspectRatio)

【讨论】:

以上是关于动画 QGraphicsView fitInView 方法的主要内容,如果未能解决你的问题,请参考以下文章

使用 Qt 的 QGraphicsScene/QGraphicsView 进行 2D 游戏

QT:QGraphicsView QGraphicsScene QGraphicsItem理解

QGraphicsProxyWidget 动画不起作用

Qt炫酷动画demo04-仿Android旋转图标的等待对话框动画

Qt炫酷动画demo04-仿Android旋转图标的等待对话框动画

Qt炫酷动画demo04-仿Android旋转图标的等待对话框动画