前言

在之前的博客《如何在 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-Fluent-Widgets,以上~~

如何在 pyqt 中使用动画实现平滑滚动的 QScrollArea的更多相关文章

  1. 如何在pyqt中在实现无边框窗口的同时保留Windows窗口动画效果(一)

    无边框窗体的实现思路 在pyqt中只要 self.setWindowFlags(Qt.FramelessWindowHint) 就可以实现边框的去除,但是没了标题栏也意味着窗口大小无法改变.窗口无法拖 ...

  2. 如何在pyqt中实现带动画的动态QMenu

    弹出菜单的视觉效果 QLineEdit 原生的菜单弹出效果十分生硬,而且样式很丑.所以照着Groove中单行输入框弹出菜单的样式和动画效果写了一个可以实现动态变化Item的弹出菜单,根据剪贴板的内容是 ...

  3. 如何在pyqt中自定义无边框窗口

    前言 之前写过很多关于无边框窗口并给窗口添加特效的博客,按照时间线罗列如下: 如何在pyqt中实现窗口磨砂效果 如何在pyqt中实现win10亚克力效果 如何在pyqt中通过调用SetWindowCo ...

  4. 如何在pyqt中通过调用 SetWindowCompositionAttribute 实现Win10亚克力效果

    亚克力效果 在<如何在pyqt中实现窗口磨砂效果>和<如何在pyqt中实现win10亚克力效果>中,我们调用C++ dll来实现窗口效果,这种方法要求电脑上必须装有MSVC.V ...

  5. 如何在pyqt中给无边框窗口添加DWM环绕阴影

    前言 在之前的博客<如何在pyqt中通过调用SetWindowCompositionAttribute实现Win10亚克力效果>中,我们实现了窗口的亚克力效果,同时也用SetWindowC ...

  6. 如何在pyqt中实现窗口磨砂效果

    磨砂效果的实现思路 这两周一直在思考怎么在pyqt上实现窗口磨砂效果,网上搜了一圈,全都是 C++ 的实现方法.正好今天查python的官方文档的时候看到了 ctypes 里面的 HWND,想想倒不如 ...

  7. 如何在pyqt中实现win10亚克力效果

    亚克力效果的实现思路 上一篇博客<如何在pyqt中实现窗口磨砂效果> 中实现了win7中的Aero效果,但是和win10的亚克力效果相比,Aero还是差了点内味.所以今天早上又在网上搜了一 ...

  8. 如何在 pyqt 中捕获并处理 Alt+F4 快捷键

    前言 如果在 Windows 系统的任意一个窗口中按下 Alt+F4,默认行为是关闭窗口(或者最小化到托盘).对于使用了亚克力效果的窗口,使用 Alt+F4 最小化到托盘,再次弹出窗口的时候可能出现亚 ...

  9. 如何在pyqt中实现平滑滚动的QScrollArea

    平滑滚动的视觉效果 Qt 自带的 QScrollArea 滚动时只能在两个像素节点之间跳变,看起来很突兀.刚开始试着用 QPropertyAnimation 来实现平滑滚动,但是效果不太理想.所以直接 ...

  10. 如何在pyqt中使用 QGraphicsView 实现图片查看器

    前言 在 PyQt 中可以使用很多方式实现照片查看器,最朴素的做法就是重写 QWidget 的 paintEvent().mouseMoveEvent 等事件,但是如果要在图像上多添加一些形状,那么在 ...

随机推荐

  1. SpringBoot报错: No identifier specified for entity: XXX.XXX.XXX.XXX

    今天练习的时候报错说是 : 没有为实体指定标识符 仔细看了实体类才发现忘记写了一些注解 用JPA写实体类时一些注解是必须的 @entity  标名本类是实体类 @table(name="表名 ...

  2. 【保姆教程】RuoYi-Radius搭建实现portal认证

    [保姆教程]RuoYi-Radius搭建实现portal认证 一.简介 以若依后台管理框架V4.6.0做为基础框架,实现了ToughRADIUS大部分功能,支持标准RADIUS协议(RFC 2865, ...

  3. yum 更新yum源

    yum 更新yum源 # 1.做好备份,防止更新失败时切换回去 $ mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base. ...

  4. Python基础部分:8、for循环和range的使用

    目录 一.while循环补充说明 1.死循环 2.嵌套及全局标志位 二.for...循环 1.for...循环特点 2.for...循环语法结构 三.range方法 1.什么是range 2.不同版本 ...

  5. 【题解】CF919D Substring

    题面传送门 解决思路: DP 与拓扑结合.\(f_{i,j}\) 表示到 \(i\) 位置 \(j\) 的最大次数. 将 \(a \sim z\) 转成数字 \(0\sim 25\) ,方便存储. 考 ...

  6. SpringBoot yml配置文件中,logging.level报错

    报错 *************************** APPLICATION FAILED TO START *************************** Description: ...

  7. Go语言核心36讲34

    我们在上篇文章中讲到了sync.WaitGroup类型:一个可以帮我们实现一对多goroutine协作流程的同步工具. 在使用WaitGroup值的时候,我们最好用"先统一Add,再并发Do ...

  8. SSH(六)hibernate持久层模板于事务管理

    持久层只要完成数据对数据库增删改查的操作,我们常说的hibernate区别于mybatis是在于他的全自动,而hibernate的全自动则主要体现于 他的模板,一些简单的数据操作我们就不用再去手写sq ...

  9. Datawhale组队学习_Task01:概览西瓜书+南瓜书第1、2章

    第一章 绪论 1.1引言 打开书,五分钟后,合上书:嗯!学会了!如何判断一个好瓜 1.2基本术语 分类:对离散值进行预测的学习任务,是有监督学习的代表 回归:对连续值进行预测的学习任务,是有监督学习的 ...

  10. 【Shell案例】【小数点scale&bc】14、求平均值

    描述写一个bash脚本以实现一个需求,求输入的一个的数组的平均值 第1行为输入的数组长度N第2~N行为数组的元素,如以下为:数组长度为4,数组元素为1 2 9 8示例:41298 那么平均值为:5.0 ...