如何在pyqt中實現絲滑滾動字幕


滾動字幕的視覺效果

網上有很多博客介紹了滾動字幕的實現方法,懂得都懂,大部是 Ctrl C + Ctrl V,效果還很差,最后還是得靠自己。主要思路就是通過定時器定時刷新+繪制兩段完整的字符串來達到 蒙蔽雙眼 滾動的效果,具體效果如下圖所示

滾動字幕

具體實現方式

ScrollTextWindow 類

窗口 ScrollTextWindow 上顯示歌名和歌手名。通過 QFontMetrics 來計算歌名和歌手名字符串的寬度,選出最大者,再和窗口最大寬度作比較,以設定窗口寬度並決定是否啟用滾動效果。在構造函數里面設置了一個 spacing 屬性,用來隔開兩段相同的字符串,還設置了兩個用於標志位 isSongNameAllOutisSongerNameAllOut 來標記字符串是否已經全部移出過窗口,這兩個標志位很重要,沒弄好標志位的話之后會出現字符串位置的跳變。前面說過,實現滾動效果就是靠繪制兩段相同的字符串,所以這兩段字符串的第一個字符的橫坐標就很重要,通過設置的標志位isXXXAllOutspacing、定時器的溢出次數 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_())

結語

至此,完美實現了絲滑的滾動效果,對你有幫助的話就點個贊吧٩(๑>◡<๑)۶ 。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM