如何在 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_()

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

shoko.jpg

写在最后

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

本文转载于网络 如有侵权请联系删除

相关文章

  • SpringBoot+RabbitMQ 实现手动消息确认(ACK)

    点击上方“芋道源码”,选择“设为星标”管她前浪,还是后浪?能浪的浪,才是好浪! 每天10:33 更新文章,每天掉亿点点头发...源码精品专栏原创|Java2021 超神之路,很肝~ 中文详细注释的开源项目 RPC框架Dubbo源码解析网络应用框架Netty源码解析 消息中间件RocketMQ源码解析 数据库中间件Sharding-JDBC和MyCAT源码解析作业调度中间件Elastic-Job源码解析分布式事务中间件TCC-Transaction源码解析Eureka和Hystrix源码解析Java并发源码来源:writing-bugs.blog.csdn.net/article/details/103701101一、前言二、准备2.1、依赖引入2.2、连接yml的配置2.3、config注入配置2.4、消费者的配置2.5、消息生产者三、ack配置和测试3.1、模拟消费者二出问题四、分析几个回执方法4.1、确认消息4.2、拒绝消息4.3、拒绝消息五、总结一、前言前几天我研究了关于springboot整合简单消息队列,实现springboot推送消息至队列中,消费者成功消费。同时也加了消息

  • 编程有感,Web 新时代的机遇与挑战

    一般来说,一项新技术是否会随着时代的推进而被快速地迭代和发展,要看这项技术所应用在的实际业务场景中是否有相应的技术需求,毕竟没有任何技术是会被凭空创造出来的。技术决定了业务需求的多样性,而业务需求的多样性又推动着技术不断向前发展,两者相辅相成最终才能推动行业整体的发展和进步。自1991年HTTP协议和HTML超文本标记语言这两种核心的Web技术诞生以来,Web开发技术领域便开始不断地发生着翻天覆地的变化。如下图给出的Web技术发展史所示,从1991年到2002年的这十年里,Web技术的发展过程还是比较缓和和稳定的。在这十年的时间里,首先是NetScape、Opera和InternetExplorer(IE)三大浏览器开始逐渐走入人们的视野。一些用于构建更丰富Web应用的基础性技术开始逐渐涌现,比如Flash技术从1996年开始可以被应用在浏览器端,这使得传统的Web应用中可以嵌入包含丰富多媒体信息的Flash应用,这一发展也使得Web应用的交互性和动态性大大的增强。Flash技术的出现催生了一批以提供视频播放、视频发布和视频分享服务为主的视频服务平台的出现,另一方面同时也推动了基于Fl

  • C++代码调试方式建议

    1.代码调试的重要性代码调试在程序开发阶段占有举足轻重的地位,可见代码调试的重要性。但是有一点必须强调:程序是设计出来的,而不是调试出来的。这是所有程序员必须牢记在心的一条准则。一个没有设计或者这几得很糟糕的程序,无论怎样调试,也不会成为一个合格的程序。程序有着良好的设计的前提下,软件开发的过程中,编码错误在所难免。所有程序可能出现的错误可分为两类:语法错误和逻辑错误。调试通常是指在消除了语法错误之后,发现程序中的逻辑错误的过程。对C/C++程序进行调试,有这样集中常用的手段。它们既可以单独使用,也可以配合使用。2.代码调试方式的几点建议2.1使用打印语句这是最朴素,也是最直接的方法。程序的运行可以看成是一组变量(状态)不断变化的过程,这个过程就是数据处理的过程。如果程序的最终结果不对,那么我们必须考虑这一组状态什么时候出现了问题,而查看中间结果就成了一种最有效的手段。因此,不要过分迷信功能强大的调试工具。在大部分情况下,程序出现的问题都是一些小问题。而正是这些小问题,却造成了大麻烦。程序员可以通过对最有可能出错的代码附近使用简单的printf()语句或cout<<…语句来

  • git 上传当前分支

    因为我现在的分支是的名很长,每次需要上次当前分支需要写很多代码,是不是有很简单方法上传当前分支。如果要上传一个分支到仓库origin那么就需要使用下面的命令gitpushorigin分支复制我存在下面几个分支t/lindexi/Avalonial_Grid_Arrange t/lindexi/Avalonial_Grid_Infinity复制我在上传的时候需要写很多代码,至少很难用tab出我现在的分支我在网上找到一个方法,用来上传当前的分支gitpushoriginHEAD复制但是我发现每次这样写还是很长,再告诉大家一个方法gitconfig--globalpush.defaultcurrent复制设置默认使用gitpush就是上传当前分支可以设置push.default的值为nothing不上传任何分支matching上传所有分支upstream/tracking上传当前跟踪分支current上传当前分支实际上还有更多,请看Git-git-configDocumentationhttps://stackoverflow.com/q/14031970/6116637

  • 【机器学习】--回归问题的数值优化

    一、前述回归问题求解时梯度下降由于样本数据的多样性,往往对模型有很大的影响,所以需要对样本数据做一些优化二、归一化1、背景各个维度的输入如果在数值上差异很大,那么会引起正确的w在各个维度上数值差异很大。这样找寻w的时候,对各个维度的调整基本上是按照同一个数量级来进行调整的。因此需要归一化。2、归一化方法•归一化的一种方法:最大值最小值法 •缺点是抗干扰能力弱 •受离群值得影响比较大 •中间容易没有数据归一化的一种方法:方差归一化 •优点是抗干扰能力强,和所有数据都有关.使数量级在一个量级 •缺点是最终未必会落到0到1之间 •牺牲归一化结果为代价提高稳定归一化的一种方法:均值归一化3、案例分析一优化方法:方差归一化结果:4、案例分析二解决办法:尽可能让X的各个维度上取值有正有负。均值归一化,每个数量减去平均值。

  • Django(44)drf序列化源码分析「建议收藏」

    大家好,又见面了,我是你们的朋友全栈君。序列化与反序列化  一般后端数据返回给前端的数据格式都是json格式,简单易懂,但是我们使用的语言本身并不是json格式,像我们使用的Python如果直接返回给前端,前端用的javascript语言是识别不出的,所以我们需要把python语言转换为通用的json格式的数据,在django中就是将orm模型或者queryset对象转换成字典,再由字典转换成json,整个过程就是序列化。  当用户通过在form表单中填写的数据,发送请求给后端,将数据提交给后端数据库,这个过程就是反序列化。反序列化的时候,我们需要去验证前端提交的数据是否符合后端制定的规范,符合才进行入库。 drf的序列化类drf的序列化类有3个SerializerModelSerializer(使用最多的)ListSerializer我们使用最多的就是ModelSerializer,这三个序列化类都继承自BaseSerializer,虽然我们项目中99%都是使用的ModelSerializer,但是光知道怎么使用,是没有灵魂的,我们还需要去了解源码,多看源码能迅速提高代码能力。 Ba

  • String的+号操作到底做了什么事情

    前言在之前的面试经历中,对于String的考察还是挺频繁的,大致考察以下几个知识点:String常量池newString()==和equals的区别native方法String.intern() 虽然面试中大体答对了,但是今天早上微信群里的一个问题我却答不上来,这个问题是这样的:Stringstr3="what"; Stringstr4=str3+"aniceday"; //运行时,+相当于new,所以堆中会有"whataniceday"对象,常量池中会有"what","aniceday"两个对象,而不会有"whataniceday"对象。 //这句话大佬们看看对不对啊,我怎么感觉不对啊 //常量池不会有"whataniceday"对象吗? 复制看完这个问题,说实话我也是有点懵的,我只是知道"whataniceday"不会在常量池,但是不知道具体的原因,后来群里的同学说+号是调用了StringBuffer的append方法。我去

  • 时间

    代码示例 见initSort方法 functionuseTable(table){ table.render({ elem:'#test' //,url:"{:url('admin/statistical/order_num')}" ,cellMinWidth:80//全局定义常规单元格的最小宽度,layui2.2.1新增 ,cols:[[ {field:'statistical_date',width:200,title:'时间'} ,{field:'turnover',width:200,title:'销售额'} ,{field:'play_type_count',width:200,title:'微信支付'} ,{field:'invalid_order_count',width:200,title:'作废额度'} ,{field:'packing_amount',width:200,title:'打包费'} ,{field:'shipping_fee',width:200,title:'外送费'} ,{field:'coupon_amount',width:200,tit

  • Mac下安装GIT的坑

    先去 https://git-scm.com/download/mac下载GIT客户端   双击安装,界面中有三个文件   接着双节.pkg文件,却提示无法安装 解决方式是按住Control,再点击该文件解决方式是按住Control,再点击该文件  

  • 开源项目SMSS发开指南(三)——protobuf协议设计

    本文的第一部分将介绍protobuf使用基础以及如何利用protobuf设计通信协议。第二部分会给出smss项目的协议设计规范和源码讲解。 一.Protobuf使用基础 什么是protobuf protobuf是谷歌研发的一种数据序列化和存储技术。主要可以用来解决网络通讯中异构系统的通讯和数据持久化,与同类技术相比(JSON或XML),官方宣称的数据量长度减少3~10倍,运算速度20~100倍。由于与平台无关,因此非常适合使用在多系统的交互中。 目前常见的使用版本是2和3,个人推荐如果你打算在项目中引入protobuf技术,不妨直接选择版本3。以下的所有介绍也都基于protobuf3作为标准。 从个人的使用感受来看,protobuf的优点还是相当明显的。不过,也有一些问题需要注意。 如何使用protobuf以及常见问题 protobuf依赖于一个可阅读的描述文件,后缀以.proto结束。编写proto描述文件有固定的格式,详细说明参照官方文档。smss项目的doc目录下也有提供,描述文件可阅读,因此不存在太大难度。只是需要注意,目前如果你打算使用protobuf3版本需要在文件开头注明

  • 国外程序员整理的Java资源大全分享

    Java几乎是许多程序员们的入门语言,并且也是世界上非常流行的编程语言。国外程序员AndreasKull在其Github上整理了非常优秀的Java开发资源,推荐给大家。   译文由ImportNew- 唐尤华翻译完成。   以下为具体资源列表。   构建   这里搜集了用来构建应用程序的工具。 ApacheMaven:Maven使用声明进行构建并进行依赖管理,偏向于使用约定而不是配置进行构建。Maven优于ApacheAnt。后者采用了一种过程化的方式进行配置,所以维护起来相当困难。 Gradle:Gradle采用增量构建。Gradle通过Groovy编程而不是传统的XML声明进行配置。Gradle可以很好地配合Maven进行依赖管理,并且把Ant脚本当作头等公民。   字节码操作   编程操作Java字节码的函数库。 ASM:通用底层字节码操作及分析。 Javassist:尝试简化字节码编辑。 ByteBuddy:使用“流式API”进一步简化字节码生成。   代码分析   软件度量和质量评估工具。 Checkstyle:对编程规范和标准进行静态分析。 FindBu

  • Exp4 恶意代码分析

    目录1、基础知识2、实践过程记录2.1系统运行监控(2分)2.1.1schtasks创建定时netstat计划任务监控网络1.写好脚本(实际上就是命令行语句)netcontrol1326.bat,再新建一个txt文档netstatlog.txt用于接收数据2.用命令行创建一个计划任务4.在常规中选择使用最高权限运行,在条件中关闭电源限制5.在操作栏清空参数,选择批处理文件6.等待一段时间后就可以得到想要的数据了7.收集了较多数据后,停止任务,将所得数据在excel中进行分析7.分析结果3.系统运行监控—sysmon3.1安装sysmon编写配置文件3.2启动sysmon3.3打开日志打开kali,Win运行后门文件20191326_backdoor.exe,回连到kali,kali执行shell获取win10的cmd再次查看日志4.恶意软件分析4.1用wireshark分析TCP包4.2动态恶意软件分析 1、基础知识 监控你自己系统的运行状态,看有没有可疑的程序在运行。 分析一个恶意软件,就分析Exp2或Exp3中生成后门软件;分析工具尽量使用原生指令或sysinternals,sys

  • CTreeView和CTreeCtrl的使用方法(转)

    (一)树控制的主要功能 树控制和视(TreeControl&View)主要用来显示具有一定层次结构的数据项,如资源管理器中的磁盘目录等,以供用户在其中进行各种选择。树控制中的每个数据项包括数据项名 称的文本字符串和用于表示该数据项的图像,每个数据项下面均可包含各种子项,整个结构就象目录树一样。对于包含各种子项的数据项,可通过鼠标双击来展开或合拢,这可以 通过控制树的不同风格来实现树控制的不同显示形态。这些风格主要包括: TVS_HASLINES表示用连线来连接父项和它下面的各个子项,这可以使树的显示层次结构更加清晰,但在无父项的各子项之间并没有连线; TVS_LINESATROOT表示在无父项的各子项即根下面的各子项之间存在连线; TVS_HASBUTTONS表示在带有子项的父项前面增加一个带“+”或“-”的按钮,这使得用户也可以通过单击这个小按钮来实现子项的展开和合拢,当存在子项时,按钮的初始状态 为“+”,当子项被展开时,按小按钮由“+”变为“-”号,当子项合拢时,小按钮由“-”变为“+”号,这一风格同样对于根项无效,如果需要可通过组合TVS_LINESATROOT 风格来

  • merge (oracle中merge用法)

    [Oracle]Merge语句 Merge的语法例如以下:   MERGE[hint]INTO[schema.]table[t_alias]USING[schema.] {table|view|subquery}[t_alias]ON(condition) WHENMATCHEDTHENmerge_update_clause WHENNOTMATCHEDTHENmerge_insert_clause;复制 MERGE是什么,怎样使用呢?让我们先看一个简单的需求:   需求是,从T1表更新数据到T2表中。假设T2表的NAME在T1表中已存在,就将MONEY累加,假设不存在。将T1表的记录插入到T2表中。 大家知道,在等价的情况下,一定需要至少两条语句,一条为UPDATE,一条为INSERT,并且语句中必需要与推断的逻辑,或者写在过程中,假设是单条语句,就要写全条件。写在UPDATE和INSERT的语句中,显的比較麻烦并且easy出错。假设了解MERGE,我们能够不借助存储过程,直接用单条SQL便实现了该业务逻辑,且代码非常简洁。详细例如以下:   MER

  • leetcode--堆排序

    defbuildMaxHeap(arr): importmath foriinrange(math.floor(len(arr)/2),-1,-1): heapify(arr,i) defheapify(arr,i): left=2*i+1 right=2*i+2 largest=i ifleft<arrLenandarr[left]>arr[largest]: largest=left ifright<arrLenandarr[right]>arr[largest]: largest=right iflargest!=i: swap(arr,i,largest) heapify(arr,largest) defswap(arr,i,j): arr[i],arr[j]=arr[j],arr[i] defheapSort(arr): globalarrLen arrLen=len(arr) buildMaxHeap(arr) foriinrange(len(arr)-1,0,-1): swap(arr,0,i) arrLen-=1 heapify(arr,

  • terminating with uncaught exception of type NSException

    ***Terminatingappduetouncaughtexception'NSInvalidArgumentException',reason:'-[__NSSingleObjectArrayIaddObject:]:unrecognizedselectorsenttoinstance0x145d3a9b0' terminatingwithuncaughtexceptionoftypeNSException

  • 常见错误请求状态码

    常见的状态码: 200-服务器成功返回网页 404-请求的网页不存在 503-服务不可用 详细分解: 1xx(临时响应) 表示临时响应并需要请求者继续执行操作的状态代码。 代码说明 100(继续)请求者应当继续提出请求。服务器返回此代码表示已收到请求的第一部分,正在等待其余部分。 101(切换协议)请求者已要求服务器切换协议,服务器已确认并准备切换。 2xx(成功) 表示成功处理了请求的状态代码。 代码说明 200(成功)服务器已成功处理了请求。通常,这表示服务器提供了请求的网页。 201(已创建)请求成功并且服务器创建了新的资源。 202(已接受)服务器已接受请求,但尚未处理。 203(非授权信息)服务器已成功处理了请求,但返回的信息可能来自另一来源。 204(无内容)服务器成功处理了请求,但没有返回任何内容。 205(重置内容)服务器成功处理了请求,但没有返回任何内容。 206(部分内容)服务器成功处理了部分GET请求。 3xx(重定向) 表示要完成请求,需要进一步操作。通常,这些状态代码用来重定向。 代码说明 300(多种选择)针对请求,服务器可执行多种操作。服务器可根据请求者(

  • 数据合规中的身份和适用范围的简介

    相关立法中的“数据主体”及法律适用范围 “数据主体”解释 《通用数据保护条例》(GDPR)第4条第1款“个人数据”指的是任何已识别或可识别的自然人(“数据主体”)。《中华人民共和国个人信息保护法》第三条在中华人民共和国境内处理自然人个人信息的活动,适用本法。由此可见,在欧盟和中国数据主体指的都是个人数据或叫做个人信息所归属的自然人。《加州消费者隐私法案》(CCPA)对消费者的定义是“根据加州法规,属于加州居民的自然人”。可见,美国加州的立法规定的他们的“数据主体”(也就是消费者的概念)也是自然人。基本可以断定,数据主体就是拥有个人信息或个人数据的自然人,但是范围根据立法原则和立法机构的管辖区域而有所不同。 数据主体权利对比 权利项 GDPR 《中华人民共和国个人信息保护法》 解释 知情权 第12条 第44条 数据处理前的告知义务,例如平台服务协议、隐私政策文本等 反对权 第21条 第44条 决定权 拒绝权 - 第44条 决定权 限制处理权 第13条 第44条 决定权 删除权 第17条 第47条 - 更正权 第16条 第46条 更正、补充个人信息

  • List(支持按笔画排序的List类)

    usingSystem; usingSystem.Collections.Generic; usingSystem.Globalization; usingSystem.Threading; namespaceHahaMil.Util { ///<summary> ///List(string\笔画) ///用于按笔画排序 ///</summary> publicclassStrokesList:List<string> { ///<summary> ///排序方法 ///按笔画对列表中的对象排序 ///</summary> publicvoidSort() { CultureInfoculInfo=Thread.CurrentThread.CurrentCulture; Thread.CurrentThread.CurrentCulture=newCultureInfo(133124); base.Sort(); Thread.CurrentThread.CurrentCulture=culInfo; } } }复制 &

  • 函数式编程

    函数也是对象 python中函数也是一个对象,因此可以当做参数传递 函数map() map()函数接收两个参数,一个是函数,一个是Iterable,map将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回,和java1.8的map一致,用来转换数据,生成新的惰性序列 deff(x): returnx*x #构建一个返回x*x列表的迭代器 r=map(f,[1,2,3,4,5,6,7,8,9]) 复制 函数reduce() reduce把一个函数作用在一个序列[x1,x2,x3,...]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算 #求和 fromfunctoolsimportreduce defadd(x,y): returnx+y reduce(add,[1,3,5,7,9]) 复制 函数filter() 和map()类似,filter()也接收一个函数和一个序列。和map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素 #过滤列表中的偶数,只保留奇

  • 用css设置a标签无效,让链接跳转失效

    这个代码有个坑,就是如果a标签里面放了一个图片,然后给a标签设置这个样式,这个时候是不起效果的,只能在a标签外面包一个div,然后给div设置这个样式 pointer-events:none; 这个是css3的代码,这句代码可以把 onlick 事件去掉,相当于变为普通文本 复制

相关推荐

推荐阅读