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

前言

在之前的博客《如何在 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_() 

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

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

写在最后

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

发表评论

相关文章