PyQt通过动画实现平滑滚动的QScrollArea

前言

实现过程

SmoothScrollBar

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

""" 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())

测试

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

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-Fluent-Widgets

作者:之一Yo原文地址:https://www.cnblogs.com/zhiyiYo/p/17066835.html

%s 个评论

要回复文章请先登录注册