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

具体实现方式
ScrollTextWindow 类
窗口 ScrollTextWindow 上显示歌名和歌手名。通过 QFontMetrics 来计算歌名和歌手名字符串的宽度,选出最大者,再和窗口最大宽度作比较,以设定窗口宽度并决定是否启用滚动效果。在构造函数里面设置了一个 spacing 属性,用来隔开两段相同的字符串,还设置了两个用于标志位 isSongNameAllOut 和 isSongerNameAllOut 来标记字符串是否已经全部移出过窗口,这两个标志位很重要,没弄好标志位的话之后会出现字符串位置的跳变。前面说过,实现滚动效果就是靠绘制两段相同的字符串,所以这两段字符串的第一个字符的横坐标就很重要,通过设置的标志位isXXXAllOut、spacing、定时器的溢出次数 currentIndex 以及字符串的移动步长 moveStep 来决定这两个字符串的的横坐标,具体代码如下:
import sys
from PyQt5.QtCore import Qt, QTimerfrom PyQt5.QtGui import QFont, QFontMetrics, QPainter,QPixmapfrom 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_())结语
至此,完美实现了丝滑的滚动效果。
Share
If this article helped you, please share it with others!
如何在pyqt中实现丝滑滚动字幕
https://blog.aegisr.xyz/posts/pyqt-smooth-scrolling-text/ Some information may be outdated
Related Posts Smart
1
如何美化 Matplotlib 3D坐标系
技术 ~mpl_toolkits.mplot3d 生成的3D坐标系背景色是灰色的,刻度线也向内延伸了。本文介绍如何修改背景色和刻度线方向,一劳永逸地解决这两个问题。
2
如何美化 Matplotlib 的工具栏和绘图风格
技术 matplotlib 功能十分强大,就是工具栏丑了点。本文介绍如何魔改 matplotlib 的工具栏样式,并分享一份按照 MATLAB 编写的 matplotlib 样式文件。
3
如何使用 C++ 和 OpenCV 实现截屏
技术 使用 Windows API 和 OpenCV 实现屏幕截屏,包含屏幕缩放率的计算,支持截取指定区域。
4
IDEA 添加 Maven Archetype 无效的处理
技术 在 IDEA 中点击新建 Maven 模块,会发现他已经为我们罗列出来了许多的 archetype,但有些时候满足不了我们的需求。
5
《声之形》:声音若有形状,那一定是和解的模样
小说 初看时,我以为它在讲校园欺凌;再看时,我发觉它在讲自我救赎。