<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
酷狗、網抑雲和 QQ 音樂都有桌面歌詞功能,這篇部落格也將使用 pyqt 實現桌面歌詞功能,效果如下圖所示:
桌面歌詞部件 LyricWidget
在 paintEvent
中繪製歌詞。我們可以直接使用 QPainter.drawText
來繪製文字,但是通過這種方式無法對歌詞進行描邊。所以這裡更換為 QPainterPath
來實現,使用 QPainterPath.addText
將歌詞新增到繪製路徑中,接著使用 Qainter.strokePath
進行描邊,Qainter.fillPath
繪製歌詞,這裡的繪製順序不能調換。
對於歌詞的高亮部分需要特殊處理,假設當前高亮部分的寬度為 w
,我們需要對先前繪製歌詞的 QPainterPath
進行裁剪,只留下寬度為 w
的部分,此處通過 QPainterPath.intersected
計算與寬度為 w
的矩形路徑的交集來實現裁剪。
對於高亮部分的動畫,我們既可以使用傳統的 QTimer
,也可以使用封裝地更加徹底的 QPropertyAnimation
來實現(本文使用後者)。這裡需要進行動畫展示的是高亮部分,也就是說我們只需改變“高亮寬度”這個屬性即可。PyQt 為我們提供了 pyqtProperty
,類似於 python 自帶的 property
,使用 pyqtProperty
可以給部件註冊一個屬性,該屬性可以搭配動畫來食用。
除了高亮動畫外,我們還在 LyricWidget
中註冊了捲動動畫,用於處理歌詞長度大於視口寬度的情況。
# coding:utf-8 from PyQt5.QtCore import QPointF, QPropertyAnimation, Qt, pyqtProperty from PyQt5.QtGui import (QColor, QFont, QFontMetrics, QPainter, QPainterPath, QPen) from PyQt5.QtWidgets import QWidget config = { "lyric.font-color": [255, 255, 255], "lyric.highlight-color": [0, 153, 188], "lyric.font-size": 50, "lyric.stroke-size": 5, "lyric.stroke-color": [0, 0, 0], "lyric.font-family": "Microsoft YaHei", "lyric.alignment": "Center" } class LyricWidget(QWidget): """ Lyric widget """ def __init__(self, parent=None): super().__init__(parent=parent) self.setAttribute(Qt.WA_TranslucentBackground) self.lyric = [] self.duration = 0 self.__originMaskWidth = 0 self.__translationMaskWidth = 0 self.__originTextX = 0 self.__translationTextX = 0 self.originMaskWidthAni = QPropertyAnimation( self, b'originMaskWidth', self) self.translationMaskWidthAni = QPropertyAnimation( self, b'translationMaskWidth', self) self.originTextXAni = QPropertyAnimation( self, b'originTextX', self) self.translationTextXAni = QPropertyAnimation( self, b'translationTextX', self) def paintEvent(self, e): if not self.lyric: return painter = QPainter(self) painter.setRenderHints( QPainter.Antialiasing | QPainter.TextAntialiasing) # draw original lyric self.__drawLyric( painter, self.originTextX, config["lyric.font-size"], self.originMaskWidth, self.originFont, self.lyric[0] ) if not self.hasTranslation(): return # draw translation lyric self.__drawLyric( painter, self.translationTextX, 25 + config["lyric.font-size"]*5/3, self.translationMaskWidth, self.translationFont, self.lyric[1] ) def __drawLyric(self, painter: QPainter, x, y, width, font: QFont, text: str): """ draw lyric """ painter.setFont(font) # draw background text path = QPainterPath() path.addText(QPointF(x, y), font, text) painter.strokePath(path, QPen( QColor(*config["lyric.stroke-color"]), config["lyric.stroke-size"])) painter.fillPath(path, QColor(*config['lyric.font-color'])) # draw foreground text painter.fillPath( self.__getMaskedLyricPath(path, width), QColor(*config['lyric.highlight-color']) ) def __getMaskedLyricPath(self, path: QPainterPath, width: float): """ get the masked lyric path """ subPath = QPainterPath() rect = path.boundingRect() rect.setWidth(width) subPath.addRect(rect) return path.intersected(subPath) def setLyric(self, lyric: list, duration: int, update=False): """ set lyric Parameters ---------- lyric: list list contains original lyric and translation lyric duration: int lyric duration in milliseconds update: bool update immediately or not """ self.lyric = lyric or [""] self.duration = max(duration, 1) self.__originMaskWidth = 0 self.__translationMaskWidth = 0 # stop running animations for ani in self.findChildren(QPropertyAnimation): if ani.state() == ani.Running: ani.stop() # start scroll animation if text is too long fontMetrics = QFontMetrics(self.originFont) w = fontMetrics.width(lyric[0]) if w > self.width(): x = self.width() - w self.__setAnimation(self.originTextXAni, 0, x) else: self.__originTextX = self.__getLyricX(w) self.originTextXAni.setEndValue(None) # start foreground color animation self.__setAnimation(self.originMaskWidthAni, 0, w) if self.hasTranslation(): fontMetrics = QFontMetrics(self.translationFont) w = fontMetrics.width(lyric[1]) if w > self.width(): x = self.width() - w self.__setAnimation(self.translationTextXAni, 0, x) else: self.__translationTextX = self.__getLyricX(w) self.translationTextXAni.setEndValue(None) self.__setAnimation(self.translationMaskWidthAni, 0, w) if update: self.update() def __getLyricX(self, w: float): """ get the x coordinate of lyric """ alignment = config["lyric.alignment"] if alignment == "Right": return self.width() - w elif alignment == "Left": return 0 return self.width()/2 - w/2 def getOriginMaskWidth(self): return self.__originMaskWidth def getTranslationMaskWidth(self): return self.__translationMaskWidth def getOriginTextX(self): return self.__originTextX def getTranslationTextX(self): return self.__translationTextX def setOriginMaskWidth(self, pos: int): self.__originMaskWidth = pos self.update() def setTranslationMaskWidth(self, pos: int): self.__translationMaskWidth = pos self.update() def setOriginTextX(self, pos: int): self.__originTextX = pos self.update() def setTranslationTextX(self, pos): self.__translationTextX = pos self.update() def __setAnimation(self, ani: QPropertyAnimation, start, end): if ani.state() == ani.Running: ani.stop() ani.setStartValue(start) ani.setEndValue(end) ani.setDuration(self.duration) def setPlay(self, isPlay: bool): """ set the play status of lyric """ for ani in self.findChildren(QPropertyAnimation): if isPlay and ani.state() != ani.Running and ani.endValue() is not None: ani.start() elif not isPlay and ani.state() == ani.Running: ani.pause() def hasTranslation(self): return len(self.lyric) == 2 def minimumHeight(self) -> int: size = config["lyric.font-size"] h = size/1.5+60 if self.hasTranslation() else 40 return int(size+h) @property def originFont(self): font = QFont(config["lyric.font-family"]) font.setPixelSize(config["lyric.font-size"]) return font @property def translationFont(self): font = QFont(config["lyric.font-family"]) font.setPixelSize(config["lyric.font-size"]//1.5) return font originMaskWidth = pyqtProperty( float, getOriginMaskWidth, setOriginMaskWidth) translationMaskWidth = pyqtProperty( float, getTranslationMaskWidth, setTranslationMaskWidth) originTextX = pyqtProperty(float, getOriginTextX, setOriginTextX) translationTextX = pyqtProperty( float, getTranslationTextX, setTranslationTextX)
上述程式碼對外提供了兩個介面 setLyric(lyric, duration, update)
和 setPlay(isPlay)
,用於更新歌詞和控制歌詞動畫的開始與暫停。下面是一個最小使用範例,裡面使用 Qt.SubWindow
標誌使得桌面歌詞可以在主介面最小化後仍然顯示在桌面上,同時不會多出一個應用圖示(Windows 是這樣,Linux 不一定):
class Demo(QWidget): def __init__(self): super().__init__(parent=None) # 建立桌面歌詞 self.desktopLyric = QWidget() self.lyricWidget = LyricWidget(self.desktopLyric) self.desktopLyric.setAttribute(Qt.WA_TranslucentBackground) self.desktopLyric.setWindowFlags( Qt.FramelessWindowHint | Qt.SubWindow | Qt.WindowStaysOnTopHint) self.desktopLyric.resize(800, 300) self.lyricWidget.resize(800, 300) # 必須有這一行才能顯示桌面歌詞介面 self.desktopLyric.show() # 設定歌詞 self.lyricWidget.setLyric(["Test desktop lyric style", "測試桌面歌詞樣式"], 3000) self.lyricWidget.setPlay(True) if __name__ == '__main__': app = QApplication(sys.argv) w = Demo() w.show() app.exec_()
至此關於桌面歌詞的實現方案已經介紹完畢,完整的播放器介面程式碼可參見:https://github.com/zhiyiYo/Groove,以上
到此這篇關於教你使用pyqt實現桌面歌詞功能的文章就介紹到這了,更多相關pyqt實現桌面歌詞內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45