2

如何在 pyqt 中使用动画实现平滑滚动的 QScrollArea - 之一Yo

 1 year ago
source link: https://www.cnblogs.com/zhiyiYo/p/17066835.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

在之前的博客《如何在 pyqt 中实现平滑滚动的 QScrollArea》中,我们使用定时器和队列实现了平滑滚动。但是实现代码还是有一点复杂,所以这篇博客将使用 Qt 的动画框架 QPropertyAnimation 来实现相同的功能。

SmoothScrollBar

滚动过程其实就是改变 QScrollBarvalue() 的过程,Qt 自带的 QScrollArea 之所以无法平滑滚动,就是因为滚动时在 QScrollBar 的两个 value() 之间进行跳变。如果我们能在两个滚动值之间进行插值,就能实现平滑滚动了,这里通过重写 setValue() 函数来启动滚动动画。

复制class SmoothScrollBar(QScrollBar):
    """ Smooth scroll bar """

    scrollFinished = pyqtSignal()

    def __init__(self, parent=None):
        QScrollBar.__init__(self, parent)
        self.ani = QPropertyAnimation()
        self.ani.setTargetObject(self)
        self.ani.setPropertyName(b"value")
        self.ani.setEasingCurve(QEasingCurve.OutCubic)
        self.ani.setDuration(500)
        self.ani.finished.connect(self.scrollFinished)

    def setValue(self, value: int):
        if value == self.value():
            return

        # stop running animation
        self.ani.stop()
        self.scrollFinished.emit()

        self.ani.setStartValue(self.value())
        self.ani.setEndValue(value)
        self.ani.start()

    def scrollValue(self, value: int):
        """ scroll the specified distance """
        value += self.value()
        self.scrollTo(value)

    def scrollTo(self, value: int):
        """ scroll to the specified position """
        value = min(self.maximum(), max(self.minimum(), value))
        self.setValue(value)

    def mousePressEvent(self, e):
        self.ani.stop()
        super().mousePressEvent(e)

    def mouseReleaseEvent(self, e):
        self.ani.stop()
        super().mouseReleaseEvent(e)

    def mouseMoveEvent(self, e):
        self.ani.stop()
        super().mouseMoveEvent(e)

SmoothScrollArea

最后需要将 QScrollArea 的默认滚动条替换为平滑滚动的 SmoothScrollBar

复制
class SmoothScrollArea(QScrollArea):
    """ Smooth scroll area """

    def __init__(self, parent=None):
        super().__init__(parent)
        self.hScrollBar = SmoothScrollBar()
        self.vScrollBar = SmoothScrollBar()
        self.hScrollBar.setOrientation(Qt.Horizontal)
        self.vScrollBar.setOrientation(Qt.Vertical)
        self.setVerticalScrollBar(self.vScrollBar)
        self.setHorizontalScrollBar(self.hScrollBar)

    def setScrollAnimation(self, orient, duration, easing=QEasingCurve.OutCubic):
        """ set scroll animation

        Parameters
        ----------
        orient: Orient
            scroll orientation

        duration: int
            scroll duration

        easing: QEasingCurve
            animation type
        """
        bar = self.hScrollBar if orient == Qt.Horizontal else self.vScrollBar
        bar.ani.setDuration(duration)
        bar.ani.setEasingCurve(easing)

    def wheelEvent(self, e):
        if e.modifiers() == Qt.NoModifier:
            self.vScrollBar.scrollValue(-e.angleDelta().y())
        else:
            self.hScrollBar.scrollValue(-e.angleDelta().x())

下面是一个简单的图片查看器测试程序:

复制# coding:utf-8
import sys
from PyQt5.QtCore import QEasingCurve, Qt
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import QApplication, QLabel


class Demo(SmoothScrollArea):

    def __init__(self):
        super().__init__()
        self.label = QLabel(self)
        self.label.setPixmap(QPixmap("shoko.jpg"))

        # customize scroll animation
        self.setScrollAnimation(Qt.Vertical, 400, QEasingCurve.OutQuint)
        self.setScrollAnimation(Qt.Horizontal, 400, QEasingCurve.OutQuint)

        self.horizontalScrollBar().setValue(1900)
        self.setWidget(self.label)
        self.resize(1200, 800)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    w = Demo()
    w.show()
    app.exec_()

测试用的图片如下(硝子酱真可爱🥰:

2065884-20230125115717213-1315493252.jpg

至此平滑滚动的实现方式就已介绍完毕了,更多自定义小部件可以参见 PyQt-Fluent-Widgets,以上~~


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK