如何在pyqt中实现丝滑滚动字幕
滚动字幕的视觉效果
网上有很多博客介绍了滚动字幕的实现方法,懂得都懂,大部是 Ctrl C + Ctrl V,效果还很差,最后还是得靠自己。主要思路就是通过定时器定时刷新+绘制两段完整的字符串来达到 蒙蔽双眼 滚动的效果,具体效果如下图所示

具体实现方式
ScrollTextWindow 类
窗口 ScrollTextWindow 上显示歌名和歌手名。通过 QFontMetrics 来计算歌名和歌手名字符串的宽度,选出最大者,再和窗口最大宽度作比较,以设定窗口宽度并决定是否启用滚动效果。在构造函数里面设置了一个 spacing 属性,用来隔开两段相同的字符串,还设置了两个用于标志位 isSongNameAllOut 和 isSongerNameAllOut 来标记字符串是否已经全部移出过窗口,这两个标志位很重要,没弄好标志位的话之后会出现字符串位置的跳变。前面说过,实现滚动效果就是靠绘制两段相同的字符串,所以这两段字符串的第一个字符的横坐标就很重要,通过设置的标志位isXXXAllOut、spacing、定时器的溢出次数 currentIndex 以及字符串的移动步长 moveStep 来决定这两个字符串的的横坐标,具体代码如下:
import sys
from PyQt5.QtCore import Qt, QTimer
from PyQt5.QtGui import QFont, QFontMetrics, QPainter,QPixmap
from PyQt5.QtWidgets import QApplication, QLabel, QWidget
class ScrollTextWindow(QWidget):
    """ 滚动字幕 """
    def __init__(self, songName, songerName, parent=None):
        super().__init__(parent)
        self.songName = songName
        self.songerName = songerName
        # 实例化定时器
        self.timer = QTimer(self)
        # 设置刷新时间和移动距离
        self.timeStep = 20
        self.moveStep = 1
        self.songCurrentIndex = 0
        self.songerCurrentIndex = 0
        # 设置字符串溢出标志位
        self.isSongNameAllOut = False
        self.isSongerNameAllOut = False
        # 设置两段字符串之间留白的宽度
        self.spacing = 25
        # 初始化界面
        self.initWidget()
    def initWidget(self):
        """ 初始化界面 """
        self.setFixedHeight(115)
        self.setAttribute(Qt.WA_StyledBackground)
        # 调整窗口宽度
        self.adjustWindowWidth()
        # 初始化定时器
        self.timer.setInterval(self.timeStep)
        self.timer.timeout.connect(self.updateIndex)
        # 只要有一个字符串宽度大于窗口宽度就开启滚动:
        if self.isSongerNameTooLong or self.isSongNameTooLong:
            self.timer.start()
    def getTextWidth(self):
        """ 计算文本的总宽度 """
        songFontMetrics = QFontMetrics(QFont('Microsoft YaHei', 14, 400))
        self.songNameWidth = songFontMetrics.width(self.songName)
        songerFontMetrics = QFontMetrics(QFont('Microsoft YaHei', 12, 500))
        self.songerNameWidth = songerFontMetrics.width(self.songerName)
    def adjustWindowWidth(self):
        """ 根据字符串长度调整窗口宽度 """
        self.getTextWidth()
        maxWidth = max(self.songNameWidth, self.songerNameWidth)
        # 判断是否有字符串宽度超过窗口的最大宽度
        self.isSongNameTooLong = self.songNameWidth > 250
        self.isSongerNameTooLong = self.songerNameWidth > 250
        # 设置窗口的宽度
        self.setFixedWidth(min(maxWidth, 250))
    def updateIndex(self):
        """ 更新下标 """
        self.update()
        self.songCurrentIndex += 1
        self.songerCurrentIndex += 1
        # 设置下标重置条件
        resetSongIndexCond = self.songCurrentIndex * \
            self.moveStep >= self.songNameWidth + self.spacing * self.isSongNameAllOut
        resetSongerIndexCond = self.songerCurrentIndex * \
            self.moveStep >= self.songerNameWidth + self.spacing * self.isSongerNameAllOut
        # 只要条件满足就要重置下标并将字符串溢出置位,保证在字符串溢出后不会因为留出的空白而发生跳变
        if resetSongIndexCond:
            self.songCurrentIndex = 0
            self.isSongNameAllOut = True
        if resetSongerIndexCond:
            self.songerCurrentIndex = 0
            self.isSongerNameAllOut = True
    def paintEvent(self, e):
        """ 绘制文本 """
        # super().paintEvent(e)
        painter = QPainter(self)
        painter.setPen(Qt.white)
        # 绘制歌名
        painter.setFont(QFont('Microsoft YaHei', 14))
        if self.isSongNameTooLong:
            # 实际上绘制了两段完整的字符串
            # 从负的横坐标开始绘制第一段字符串
            painter.drawText(self.spacing * self.isSongNameAllOut - self.moveStep *
                             self.songCurrentIndex, 54, self.songName)
            # 绘制第二段字符串
            painter.drawText(self.songNameWidth - self.moveStep * self.songCurrentIndex +
                             self.spacing * (1 + self.isSongNameAllOut), 54, self.songName)
        else:
            painter.drawText(0, 54, self.songName)
        # 绘制歌手名
        painter.setFont(QFont('Microsoft YaHei', 12, 500))
        if self.isSongerNameTooLong:
            painter.drawText(self.spacing * self.isSongerNameAllOut - self.moveStep *
                             self.songerCurrentIndex, 82, self.songerName)
            painter.drawText(self.songerNameWidth - self.moveStep * self.songerCurrentIndex +
                             self.spacing * (1 + self.isSongerNameAllOut), 82, self.songerName)
        else:
            painter.drawText(0, 82, self.songerName)
SongInfoCard 类
父级窗口 SongInfoCard 用来显示专辑封面和滚动字幕,代码如下:
class SongInfoCard(QWidget):
    """ 播放栏左侧歌曲信息卡 """
    def __init__(self, songInfo: dict, parent=None):
        super().__init__(parent)
        # 保存信息
        self.songInfo = songInfo
        self.songName = self.songInfo['songName']
        self.songerName = self.songInfo['songer']
        # 实例化小部件
        self.albumPic = QLabel(self)
        self.scrollTextWindow = ScrollTextWindow(
            self.songName, self.songerName, self)
        # 初始化界面
        self.initWidget()
    def initWidget(self):
        """ 初始化小部件 """
        self.setFixedHeight(115)
        self.setFixedWidth(115 + 15 + self.scrollTextWindow.width() + 25)
        self.setAttribute(Qt.WA_StyledBackground)
        self.setWindowFlags(Qt.FramelessWindowHint)
        self.scrollTextWindow.move(130, 0)
        self.albumPic.setPixmap(QPixmap(self.songInfo['album'][-1]).scaled(
                                115, 115, Qt.KeepAspectRatio, Qt.SmoothTransformation))
if __name__ == "__main__":
    app = QApplication(sys.argv)
    songInfo = {
        'songName': 'ハッピーでバッドな眠りは浅い', 'songer': '鎖那',
        'album': [r'resource\Album Cover\ハッピーでバッドな眠りは浅い\ハッピーでバッドな眠りは浅い.png']}
    demo = SongInfoCard(songInfo)
    demo.setStyleSheet('background:rgb(129,133,137)')
    demo.show()
    sys.exit(app.exec_())
结语
至此,完美实现了丝滑的滚动效果,对你有帮助的话就点个赞吧٩(๑>◡<๑)۶ 。
如何在pyqt中实现丝滑滚动字幕的更多相关文章
- 如何在pyqt中自定义无边框窗口
		前言 之前写过很多关于无边框窗口并给窗口添加特效的博客,按照时间线罗列如下: 如何在pyqt中实现窗口磨砂效果 如何在pyqt中实现win10亚克力效果 如何在pyqt中通过调用SetWindowCo ... 
- 如何在pyqt中实现窗口磨砂效果
		磨砂效果的实现思路 这两周一直在思考怎么在pyqt上实现窗口磨砂效果,网上搜了一圈,全都是 C++ 的实现方法.正好今天查python的官方文档的时候看到了 ctypes 里面的 HWND,想想倒不如 ... 
- 如何在pyqt中实现win10亚克力效果
		亚克力效果的实现思路 上一篇博客<如何在pyqt中实现窗口磨砂效果> 中实现了win7中的Aero效果,但是和win10的亚克力效果相比,Aero还是差了点内味.所以今天早上又在网上搜了一 ... 
- 如何在pyqt中通过调用 SetWindowCompositionAttribute 实现Win10亚克力效果
		亚克力效果 在<如何在pyqt中实现窗口磨砂效果>和<如何在pyqt中实现win10亚克力效果>中,我们调用C++ dll来实现窗口效果,这种方法要求电脑上必须装有MSVC.V ... 
- 如何在pyqt中在实现无边框窗口的同时保留Windows窗口动画效果(一)
		无边框窗体的实现思路 在pyqt中只要 self.setWindowFlags(Qt.FramelessWindowHint) 就可以实现边框的去除,但是没了标题栏也意味着窗口大小无法改变.窗口无法拖 ... 
- 如何在pyqt中给无边框窗口添加DWM环绕阴影
		前言 在之前的博客<如何在pyqt中通过调用SetWindowCompositionAttribute实现Win10亚克力效果>中,我们实现了窗口的亚克力效果,同时也用SetWindowC ... 
- 如何在pyqt中实现带动画的动态QMenu
		弹出菜单的视觉效果 QLineEdit 原生的菜单弹出效果十分生硬,而且样式很丑.所以照着Groove中单行输入框弹出菜单的样式和动画效果写了一个可以实现动态变化Item的弹出菜单,根据剪贴板的内容是 ... 
- 如何在 pyqt 中捕获并处理 Alt+F4 快捷键
		前言 如果在 Windows 系统的任意一个窗口中按下 Alt+F4,默认行为是关闭窗口(或者最小化到托盘).对于使用了亚克力效果的窗口,使用 Alt+F4 最小化到托盘,再次弹出窗口的时候可能出现亚 ... 
- 如何在pyqt中使用 QGraphicsView 实现图片查看器
		前言 在 PyQt 中可以使用很多方式实现照片查看器,最朴素的做法就是重写 QWidget 的 paintEvent().mouseMoveEvent 等事件,但是如果要在图像上多添加一些形状,那么在 ... 
随机推荐
- 【LeetCode】93. Restore IP Addresses 解题报告(Python & C++)
			作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 回溯法 日期 题目地址:https://leetco ... 
- 用C++创建Https客户端,用Mingw编译
- 业务层面和运维层面优化你的Redis
			业务层面 key的长度尽量要短,在数据量非常大时,过长的key名会占用更多的内存; 一定避免存储过大的数据(大value),过大的数据在分配内存和释放内存时耗时严重,会阻塞主线程; Redis 4.0 ... 
- CS5213demoboard设计电路|DMI转VGA带II2S音频输出转接线|CS5213方案
			CS5213是台湾CAPSTONE瑞奇达推出的一款HDMI(高清多媒体接口)到VGA转换芯片. CS5213设计HDMI转VGA带II2S转接线产品特性: ◇将完整的HDMI信号转换为VGA输出◇支持 ... 
- Java 设置系统参数和运行参数
			系统参数 系统级全局变量,该参数在程序中任何位置都可以访问到.优先级最高,覆盖程序中同名配置. 系统参数的标准格式为:-Dargname=argvalue,多个参数之间用空格隔开,如果参数值中间有空格 ... 
- Linux-saltstack-1 saltstack的安装与基本配置
			@ 目录 一.环境介绍 二.安装saltstack 1.配置安装yum源 2.安装salt-master 3.安装slat-minion 三.配置salt-master 1.指定master的地址 2 ... 
- 通过 v-once 创建低开销的静态组件
			<!DOCTYPE html> <html> <head> <meta charset="utf-8"/> <script s ... 
- hexo 升级5.4.0出现错误解决方法-hexo-theme-butterfly
			本篇文章已同步个人博客,可移步食用.hexo 升级 5.4.0 出现错误解决方法 -hexo-theme-butterfly 周末升级了下 hexo 到新版本,发现升级后,构建时出现了一些错误,以下是 ... 
- VirtualBox 虚拟机怎样设置共享文件夹
			首次在VirtualBox装完系统后,很经常用到的操作就是:想将主机的东西拉倒虚拟机进行使用或安装,那怎么将主机的文件拿到虚拟机呢? 1.在虚拟机 > 设置中选择 >安装增强功能,经过这个 ... 
- Pytest_常用执行参数详解(3)
			前面讲了测试用例的执行方式,也认识了 -v -s 这些参数,那么还有没有其它参数呢?答案肯定是有的,我们可以通过 pytest -h来查看所有可用参数. 从图中可以看出,pytest的参数有很多,但 ... 
