Qt动画 - 使用QPropertyAnimation同时更改比例和位置

时间:2012-04-30 10:07:30

标签: python qt animation pyqt pyside

我目前正尝试在QAnimation框架的帮助下使用Qt构建幻灯片查看器。我无法改变照片的大小,同时移动照片,因为在幻灯片放映时改变照片的大小似乎会影响其轨迹。

具体来说,我正在尝试为一组照片制作动画,使它们在窗口的水平中心线上从右向左连续移动,同时改变大小。基本上,它是一种“鱼眼”效果,当照片向窗口中心移动时,照片变大,然后在移开时缩小回原来的大小。

在任何给定时间,窗口将只显示3张照片(加上左边窗口的1张照片)。假设从左到右排列的3张照片标记为P1,P2,P3。考虑一张照片,最右边的照片P3。当P3从窗口右侧向中心移动时,其尺寸应逐渐增大并在窗口中心达到最大尺寸。当它继续向左移动并最终退出窗口时,其大小应该减小回原始值。

如果我只是在不调整照片大小的情况下移动照片,那么我当前的实现是有效的。当同时移动和缩放图像时,照片不再水平移动穿过窗口的中心。照片的大小似乎可以正确缩放。

我当前的实现依赖于QPropertyAnimation类来执行动画。我正在使用Python和Qt的PySide绑定编写程序,但C ++或PyQt代码中的解决方案都很好。我将把它们翻译成Python和PySide。

相关代码如下:

def animate(self, items):
'''@params: items The photos to animate'''
    logging.debug('Creating %d animation objects', len(items))
    self.animationGroup.clear()
    for i, item in enumerate(items):
        self.animatePosition(item, i, len(items))
        self.animateScale(item, i)
    QTimer.singleShot(START_DELAY, self.animationGroup.start)    

def animatePosition(self, item, index, numItems):
    isLastItem = (index == numItems - 1)
    isFirstItem = (index == 0)

    posAnimation = QPropertyAnimation(item, 'position')
    posAnimation.setDuration(SLIDE_INTERVAL)
    posAnimation.setStartValue(item.pos())
# I've tried changing dh depending on the scale of the current image with limited success.
dh = 0 
    if isLastItem:
        posAnimation.setEndValue(item.pos() - QPointF(slideshow.RIGHT_MARGIN + slideshow.STANDARD_SLIDE_WIDTH, dh))
    elif isFirstItem:
        posAnimation.setEndValue(item.pos() - QPointF(slideshow.LEFT_MARGIN + slideshow.STANDARD_SLIDE_WIDTH, dh))
    else:
        posAnimation.setEndValue(item.pos() - QPointF(slideshow.IMAGE_GAP + slideshow.STANDARD_SLIDE_WIDTH, dh))
    posAnimation.setEasingCurve(QEasingCurve.OutExpo)
    self.animationGroup.addAnimation(posAnimation)

def animateScale(self, item, index):
    if self.animatedView is not None:
        scaleAnimation = QPropertyAnimation(item, 'slideScale')
        scaleAnimation.setDuration(SLIDE_INTERVAL)
        scaleAnimation.setStartValue(item.scale())
        endScale = self.getSlideScale(index - 1)
        scaleAnimation.setEndValue(endScale)
        scaleAnimation.setEasingCurve(QEasingCurve.OutExpo)
        self.animationGroup.addAnimation(scaleAnimation)

我知道通常在应用缩放变换之前必须将照片移回原点,然后移回到其轨迹上的下一个位置。我怀疑用于缩放照片的QPropertyAnimation正在应用于翻译的照片,从而影响其轨迹,但我可能错了,因为我刚开始使用Qt过去1-2周。

有谁知道如何让QPropertyAnimation知道另一个动画正在发生?或者,任何人都可以建议采用不同的方法(不一定涉及QPropertyAnimation)来解决上述问题吗?

预先感谢任何回复。

1 个答案:

答案 0 :(得分:0)

我假设您正在使用QGraphicsView框架(如果您发布了一个最小的,正在运行的示例程序,那么理解和调试您的问题会更容易。)

我建议使用QGraphicsWidgets并直接为"geometry" property制作动画(编辑:我在这里错了,这不行)。

您需要在不使用两个QPropertyAnimations的情况下同时更改大小和位置。最可能最简单的解决方案是子类一个QVariantAnimation(甚至是QVariantAnimation)并适当地实现updateCurrentValue()。我想我会插入一个QRectF,你可以从中提取位置和大小/比例,否则你当然可以插入一个浮点数(这意味着你可以有更多的插值代码来自己做,但是它需要计算从尺寸)。考虑将所需的比例编组到QRectF的大小(例如,将QSizeF(scale, scale)传递给构造函数)。这有点hackish,因为QRectF已经/不是/所需的项目几何,但是其他方面很简单并且效果很好(也有缓和曲线)。这是一个有用的代码段:

class GeometryAnimation(QtCore.QVariantAnimation):
    def __init__(self, item, parent = None):
        QtCore.QVariantAnimation.__init__(self, parent)
        self._item = item

    def updateCurrentValue(self, value):
        self._item.setPos(value.topLeft())
        self._item.setScale(value.width())

为什么你当前的代码不起作用很难说,取决于“slideScale”属性的实现;如果转换相互作用(我会考虑不受欢迎的行为),那就是原因。