前段时间写的Python自制一款炫酷音乐播放器 有不少小伙伴私信我 对播放器提了不少改进建议 让我完善播放器的功能。今天音乐播放器2.0版本完成了 大家一起来看看是如何用python自制一款炫酷的音乐播放器的吧~
首先我们还是一起来看看实现的音乐播放器最终效果如何
由于之前已经介绍过了音乐播放器V1.0版本的大致功能 如果有不了解的小伙伴可以先去看看V1.0版本的音乐播放器大致介绍。【python自制一款炫酷音乐播放器 想听啥随便搜 】下面我们就介绍这个音乐播放器V2.0版本新加的部分功能制作过程。
直接跳到文末获取源码及exe打包程序。
一、核心功能设计在之前的V1.0版本中 我们已经完成了播放器UI界面的设计 对播放器的画面布局进行排版设计 其次音乐播放器的最重要的核心功能根据关键字搜索自动爬取音乐并且进行音乐播放已经完成了。当然也完成了一些播放器常见的附加功能 如播放方式列表循环、单曲循环、随机播放 当前上一首下一首播放 播放暂停开始 音量增加减少等。
V2.0版本中 我们主要增加了以下几个核心功能
对UI排版布局增加子列表页面 主要包括最近播放、喜爱的歌、歌词显示、本地音乐播放4个子列表页面音乐本地下载及添加我喜爱的歌曲功能我喜爱的歌内容保存及初始化读取我喜爱的歌列表显示 喜爱歌曲列表播放功能当前播放歌曲歌词读取显示功能读取播放本地歌曲功能 二、实现步骤 1. UI排版布局V1.0版本中 我们已经对排版布局进行了初步设计。头部主要包括关键字搜索和音乐来源选择 以及窗体最小化 最大化 关闭功能 底部主要来显示当前播放音乐 播放进度条 音量控制 上一首/下一首 暂停/开始 播放方式等附加功能 中间主体包含左右两侧 左侧用来显示播放音乐封面图 右侧用来进行音乐列表显示。
V2.0版本中我们将新增的几个子列表页面放在界面中间主体右侧 叠加在音乐搜索显示列表页。这里我们使用的还是pyqt5。添加子列表页面核心设计代码如下
def init_ui(self):
self.main_layout.addWidget(self.up_widget, 0, 0, 1, 110)
self.main_layout.addWidget(self.left_widget, 1, 0, 90, 20)
self.main_layout.addWidget(self.right_widget, 1, 20, 90, 90) # 22右侧部件在第0行第3列 占8行9列
self.main_layout.addWidget(self.down_widget, 100, 0, 10, 110)
self.main_layout.addWidget(self.close_widget, 0, 105, 1, 5) # 左侧部件在第0行第0列 占1行3列
self.down_layout.addWidget(self.label, 1, 0, 1, 1)
self.setCentralWidget(self.main_widget) # 设置窗口主部件
self.tabWidget QTabWidget(self)
self.tabWidget.setGeometry(QRect(33, 20, 716, 471))
self.tab QWidget()
self.tab.setObjectName( tab )
self.tab_layout QGridLayout()
self.tab.setLayout(self.tab_layout)
self.listwidget QListWidget(self.tab)
self.label361 QLabel(self)
self.label361.setText( )
self.label361.setStyleSheet( color:#6DDF6D )
self.tab_layout.addWidget(self.label361, 0, 1, 1, 1)
self.button_1235 QPushButton(icon( fa.download , color #D0D0D0 , font 24), 下载全部 )
self.button_1235.clicked.connect(self.downloadalls)
self.button_1235.setStyleSheet(
QPushButton{background:#222225;border-radius:5px;}QPushButton:hover{background:#303030;} )
self.tab_layout.addWidget(self.button_1235, 0, 2, 1, 1)
self.button_1236 QPushButton(icon( fa.trash-o , color #D0D0D0 , font 24), 清空列表 )
self.button_1236.clicked.connect(self.dell)
self.button_1236.setStyleSheet(
QPushButton{background:#222225;border-radius:5px;}QPushButton:hover{background:#303030;} )
self.tab_layout.addWidget(self.button_1236, 0, 3, 1, 1)
self.listwidget.doubleClicked.connect(lambda: self.change_func(self.listwidget))
self.listwidget.setContextMenuPolicy(Qt.CustomContextMenu)
self.listwidget.customContextMenuRequested[QPoint].connect(self.myListWidgetContext)
self.listwidget.setStyleSheet(self.css)
self.listwidget.setObjectName( listWidget )
self.tab_layout.addWidget(self.listwidget, 1, 0, 1, 4)
self.tabWidget.addTab(self.tab, 搜索页 )
self.tab2 QWidget()
self.tab2.setObjectName( tab )
self.tab2_layout QGridLayout()
self.tab2.setLayout(self.tab2_layout)
self.listwidget2 QListWidget(self.tab2)
self.listwidget2.doubleClicked.connect(lambda: self.change_funcse(self.listwidget2))
self.listwidget2.setContextMenuPolicy(Qt.CustomContextMenu)
self.listwidget2.customContextMenuRequested[QPoint].connect(self.myListWidgetContext2)
self.listwidget2.setStyleSheet(self.css)
self.listwidget2.setObjectName( listWidget2 )
self.listwidget2.setContextMenuPolicy(3)
self.tab2_layout.addWidget(self.listwidget2, 0, 0, 1, 1)
self.tabWidget.addTab(self.tab2, 最近播放 )
self.tab3 QWidget()
self.tab3.setObjectName( tab )
self.tab3_layout QGridLayout()
self.tab3.setLayout(self.tab3_layout)
self.label223 QLabel(self)
# self.label5.setScaledContents(True)
pix_img QPixmap(str(data /backdown.png ))
pix pix_img.scaled(100, 100, Qt.KeepAspectRatio)
self.label223.setPixmap(pix)
# self.label5.setMaximumSize(1,1)
self.tab3_layout.addWidget(self.label223, 0, 0, 1, 1)
self.button_1237 QPushButton(icon( fa.play , color #FFFFFF , font 24), 播放全部 )
self.button_1237.clicked.connect(self.allplaylove)
self.button_1237.setStyleSheet(
QPushButton{background:#EC4141;border-radius:5px;}QPushButton:hover{background:#E92121;} )
self.tab3_layout.addWidget(self.button_1237, 0, 1, 1, 1)
self.button_1235 QPushButton(icon( fa.download , color #D0D0D0 , font 24), 下载全部 )
self.button_1235.clicked.connect(self.downloadalllove)
self.button_1235.setStyleSheet(
QPushButton{background:#222225;border-radius:5px;}QPushButton:hover{background:#303030;} )
self.tab3_layout.addWidget(self.button_1235, 0, 2, 1, 1)
self.button_1236 QPushButton(icon( fa.trash-o , color #D0D0D0 , font 24), 清空列表 )
self.button_1236.clicked.connect(self.delove)
self.button_1236.setStyleSheet(
QPushButton{background:#222225;border-radius:5px;}QPushButton:hover{background:#303030;} )
self.tab3_layout.addWidget(self.button_1236, 0, 3, 1, 1)
self.listwidget3 QListWidget(self.tab3)
self.listwidget3.doubleClicked.connect(lambda: self.change_funclove(self.listwidget3))
self.listwidget3.setContextMenuPolicy(Qt.CustomContextMenu) self.listwidget3.customContextMenuRequested[QPoint].connect(self.myListWidgetContext3)
self.listwidget3.setStyleSheet(self.css)
self.listwidget3.setObjectName( listWidget3 )
self.tab3_layout.addWidget(self.listwidget3, 1, 0, 1, 4)
self.tabWidget.addTab(self.tab3, 喜爱的歌 )
self.tab4 QWidget()
self.tab4.setObjectName( tab )
self.tab4_layout QGridLayout()
self.tab4.setLayout(self.tab4_layout)
self.listwidget4 QListWidget(self.tab4)
# self.listwidget4.doubleClicked.connect(lambda: self.change_func(self.listwidget))
self.listwidget4.setStyleSheet(self.css)
self.listwidget4.setObjectName( listWidget4 )
self.tab4_layout.addWidget(self.listwidget4, 0, 0, 1, 1)
self.tabWidget.addTab(self.tab4, 歌词 )
self.tab5 QWidget()
self.tab5.setObjectName( tab5 )
self.tab5_layout QGridLayout()
self.tab5.setLayout(self.tab5_layout)
self.listwidget5 QListWidget(self.tab5)
self.listwidget5.doubleClicked.connect(lambda: self.change(self.listwidget5))
self.listwidget5.setContextMenuPolicy(Qt.CustomContextMenu)
self.listwidget5.customContextMenuRequested[QPoint].connect(self.myListWidgetContext5)
self.button_12351 QPushButton(icon( fa.download , color #D0D0D0 , font 24), 添加目录 )
self.button_12351.clicked.connect(self.add)
self.button_12351.setStyleSheet(
QPushButton{background:#222225;border-radius:5px;}QPushButton:hover{background:#303030;} )
self.tab5_layout.addWidget(self.button_12351, 0, 2, 1, 1)
self.button_12361 QPushButton(icon( fa.trash-o , color #D0D0D0 , font 24), 清空列表 )
self.button_12361.clicked.connect(self.dellocal)
self.button_12361.setStyleSheet(
QPushButton{background:#222225;border-radius:5px;}QPushButton:hover{background:#303030;} )
self.tab5_layout.addWidget(self.button_12361, 0, 3, 1, 1)
self.listwidget5.setStyleSheet(self.css)
self.listwidget5.setObjectName( listWidget5 )
self.tab5_layout.addWidget(self.listwidget5, 1, 0, 1, 4)
self.tabWidget.addTab(self.tab5, 本地歌曲 )
self.right_layout.addWidget(self.tabWidget, 3, 0, 100, 90)
UI界面布局实现效果如下
我们根据V1.0版本完成的 输入的关键字和选择音乐来源进行音乐爬取 通过多线程 将歌曲、歌手、歌曲url地址全都获取 并将这些爬取的音乐数据列表显示到搜索页面中。双击列表页面中某一首歌曲 即可实现音乐播放功能。
V2.0版本中 我们添加了对当前播放音乐的本地下载、搜索页音乐列表一键下载、喜爱的歌列表一键下载、添加我喜爱的歌曲功能。
音乐下载
对于音乐下载 我们可以进行当前播放音乐下载、搜索页音乐列表一键下载、喜爱的歌一键下载。如下图所示
核心代码如下
# 当前播放音乐下载
def down(self):
if bo local :
downpath str(filew)
downpath downpath.replace( / , \ )
downpath downpath SongName[num]
print(downpath)
print( explorer /select,{} .format(downpath))
call( explorer /select,{} .format(downpath))
else:
call( explorer /select,{} .format(to))
# 下载所有音乐
def downloadall(self, typer):
try:
global typerr
typerr typer
print(typer)
print(typerr)
self.work downall()
self.work.start()
self.work.trigger.connect(self.disdownall)
except:
print( 默认图片下载错误 )
pass
# 下载搜索页列表所有音乐
def downloadalls(self):
self.downloadall( boing )
# 下载喜爱的歌列表所有音乐
def downloadalllove(self):
self.downloadall( love )
添加喜爱的歌
对于添加喜爱的歌曲 我们可以通过当前播放音乐的❤标志 也可以通过右键音乐列表进行喜爱的歌曲添加。核心代码如下
# 通过点击❤标志 对当前播放音乐添加喜爱的歌 def lovesong(self): if bo boing or bo boed : try: global loves global loveurls global lovepics global lovelrc if bo boing : loves.append(songs[num]) loveurls.append(urls[num]) lovepics.append(pic[num]) lovelrc.append(lrcs[num]) elif bo boed : loves.append(songed[num]) loveurls.append(urled[num]) lovepics.append(picd[num]) lovelrc.append(lrcd[num]) else: pass except: pass self.work firstThread() self.work.start() self.work.trigger.connect(self.dispng) self.listwidget3.clear() for i in loves: self.listwidget3.addItem(i) self.listwidget3.item(r).setForeground(Qt.white) r r 1 print(loves) else: pass # 通过右键音乐列表进行喜爱的歌曲添加 def addItem(self): try: global loves global loveurls global lovepics global lovelrc if list_confident boing : loves.append(songs[num_m]) loveurls.append(urls[num_m]) lovepics.append(pic[num_m]) lovelrc.append(lrcs[num_m]) else: loves.append(songed[num_m]) loveurls.append(urled[num_m]) lovepics.append(picd[num_m]) lovelrc.append(lrcd[num_m]) self.work firstThread() self.work.start() self.work.trigger.connect(self.dispng) except: pass self.listwidget3.clear() for i in loves: self.listwidget3.addItem(i) self.listwidget3.item(r).setForeground(Qt.white) r r 1 print( done ) print(loves)
这样我们就可以把歌曲添加到喜爱的歌子列表页面下 如下图所示。
对于添加到喜爱的歌曲列表 我们需要在退出音乐播放器程序时 将这些数据保存到本地文件 方便后面重新运行时初始化读取显示列表。
喜爱歌曲本地保存
我们可以把喜爱的歌曲列表、喜爱歌曲图片、歌曲url链接、歌词等数据保存到本地。核心代码如下
def close(self):
reply QMessageBox.question(self, u 警告 , u 确定退出? , QMessageBox.Yes,
QMessageBox.No)
if reply QMessageBox.Yes:
close True
try:
mixer.music.stop()
except:
pass
try:
rmtree(str(data))
except Exception as e:
print( 删除错误类型是 , e.__class__.__name__)
print( 删除错误明细是 , e)
filepath {}/musicdata .format(apdata)
try:
mkdir(filepath)
except:
pass
print(filepath)
with open(filepath /loves , w , encoding utf-8 ) as f:
f.truncate(0)
print(f.write(str(loves)))
with open(filepath /lovepics , w , encoding utf-8 ) as f:
f.truncate(0)
print(f.write(str(lovepics)))
with open(filepath /loveurls , w , encoding utf-8 ) as f:
f.truncate(0)
print(f.write(str(loveurls)))
with open(filepath /lovelrc , w , encoding utf-8 ) as f:
f.truncate(0)
print(f.write(str(lovelrc)))
with open(filepath /voice , w , encoding utf-8 ) as f:
f.truncate(0)
print(f.write(str(voice)))
try:
rmtree(str(data))
except Exception as e:
print( 删除错误类型是 , e.__class__.__name__)
print( 删除错误明细是 , e)
exit()
else:
pass
喜爱的歌初始化读取
重新运行音乐播放器时 我们需要加载保存在本地的喜爱歌曲数据 并将歌曲列表信息加载显示到喜爱的歌子列表页面中。核心代码如下
def run(self):
try:
apdataas getenv( APPDATA )
filepathas {}/musicdata .format(apdataas)
global lovelrc
global loveurls
global loves
global lovepics
global voice
# 读取历史数据开始
try:
with open(filepathas /voice , r , encoding utf-8 ) as f:
a f.read()
# print(a)
voice float(a)
print(voice)
self.trigger.emit(str( voicedone ))
except:
self.trigger.emit(str( voicedone ))
pass
with open(filepathas /loves , r , encoding utf-8 ) as f:
a f.read()
print(a)
strer a
loves literal_eval(strer)
with open(filepathas /lovepics , r , encoding utf-8 ) as f:
a f.read()
print(a)
strer a
lovepics literal_eval(strer)
with open(filepathas /loveurls , r , encoding utf-8 ) as f:
a f.read()
print(a)
strer a
loveurls literal_eval(strer)
with open(filepathas /lovelrc , r , encoding utf-8 ) as f:
a f.read()
print(a)
strer a
lovelrc literal_eval(strer)
self.trigger.emit(str( login ))
print(loves)
print( read finish )
except:
print( read error )
pass
# 读取数据结束
# 下载喜爱的歌列表中首项的歌曲封面
try:
req get(lovepics[0])
checkfile open(str(data /ls3.png ), w b )
for i in req.iter_content(100000):
checkfile.write(i)
checkfile.close()
lsfile str(data /ls3.png )
safile str(data /first.png )
draw(lsfile, safile)
self.trigger.emit(str( first ))
except:
self.trigger.emit(str( nofirst ))
pass
列表播放
对于加载的喜爱歌曲列表 我们可以对歌曲进行列表一键播放。
# 音乐播放 def bofang(self, num, bo): print( 尝试进行播放 ) try: import urllib global pause global songs global music global downloading downloading True self.console_button_3.setIcon(icon( fa.pause , color #F76677 , font 18)) pause False try: mixer.stop() except: pass mixer.init() try: self.Timer QTimer() self.Timer.start(500) except: pass try: self.label.setText( 正在寻找文件... ) self.work WorkThread() self.work.start() self.work.trigger.connect(self.display) except: print( 无法播放 歌曲下载错误 ) downloading False pass except: sleep(0.1) print( 播放系统错误 ) pass # 播放所有歌曲 def playall(self, typer): global num global bo try: bo typer num 0 self.bofang(bo, num) except: print( playall error ) pass # 播放喜爱歌曲 def allplaylove(self): self.playall( love )4. 歌词显示
对于播放的歌曲 我们可以进行歌词打印显示。核心代码如下
def run(self):
if bo love :
try:
proxies {
http : http://124.72.109.183:8118 ,
http : http://49.85.1.79:31666
headers {
User-Agent : Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36 ,
X-Requested-With : XMLHttpRequest }
try:
try:
try:
aq lovepics[num]
aqq aq.split( / )
except:
pass
if type kugou and len(aqq) - 1 6:
aqqe str(aqq[0]) str( // ) str(aqq[2]) str( / ) str(aqq[3]) str( / ) str(
400 ) str( / ) str(aqq[5]) str( / ) str(aqq[6])
print(aqqe)
elif type netease and len(aqq) - 1 4:
aqn aq.split( ? )
b ?param 500x500
aqqe (str(aqn[0]) str(b))
print(aqqe)
else:
aqqe lovepics[num]
req get(aqqe)
checkfile open(str(data /ls1.png ), w b )
for i in req.iter_content(100000):
checkfile.write(i)
checkfile.close()
lsfile str(data /ls1.png )
safile str(data /back.png )
draw(lsfile, safile)
picno True
except:
print( 图片错误 )
picno False
pass
url1 loveurls[num]
print(url1)
# os.makedirs( music , exist_ok True)
number number 1
path str(data {}.临时文件 .format(number))
headers {
User-Agent : Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.110.430.128 Safari/537.36 ,
X-Requested-With : XMLHttpRequest
with get(url1, stream True, headers headers) as r, open(path, wb ) as file:
total_size int(r.headers[ content-length ])
content_size 0
for content in r.iter_content(chunk_size 1024):
if not stopdown:
file.write(content)
content_size len(content)
plan (content_size / total_size) * 100
# print(int(plan))
develop str(int(plan)) str( % )
self.trigger.emit(str(develop))
else:
print ( down )
break
stopdown False
to downloadmusic{}.mp3 .format(songed[num])
makedirs( downloadmusic , exist_ok True)
except:
self.trigger.emit(str( nofinish ))
pass
try:
lrct []
f lovelrc[num] # 按行读取
# print(f)
lines f.split( n )
# print(lines)
for i in lines:
line1 i.split( [ )
try:
line2 line1[1].split( ] )
if line2 :
pass
else:
linew line2[1]
# print(linew)
lrct.append(linew)
self.trigger.emit(str( lrcfinish ))
except:
print( 歌词错误 )
except:
pass
try:
copyfile(path, to)
except:
pass
downloading False
self.trigger.emit(str( finish ))
except:
self.trigger.emit(str( nofinish ))
效果如下所示
首先我们可以对本地文件夹目录下的音乐进行读取添加到本地歌曲列表中 根据歌曲的文件名进行列表显示。核心代码如下
# 本地歌曲文件夹读取添加 def add(self): try: global SongPath global SongName global num global filew global asas fileN QFileDialog.getExistingDirectory(None, 选取文件夹 , ) if not fileN : self.listwidget5.clear() filew fileN / asas filew l1 [name for name in listdir(fileN) if name.endswith( .mp3 ) or name.endswith( .flac ) or name.endswith( .wma ) or name.endswith( .MP3 ) or name.endswith( .FLAC ) or name.endswith( .WMA )] SongNameadd l1 SongPathadd [filew i for i in SongNameadd] SongName SongName SongNameadd SongPath SongPath SongPathadd print(SongPath) for i in SongName: self.listwidget5.addItem(i) # 将文件名添加到listWidget self.listwidget5.item(r).setForeground(Qt.white) r r 1 except: filew asas
当然我们还可以对本地加载歌曲进行双击播放 首先我们需要将本地歌曲子列表页面中进行双击事件绑定。
self.tab5 QWidget() self.tab5.setObjectName( tab5 ) self.tab5_layout QGridLayout() self.tab5.setLayout(self.tab5_layout) self.listwidget5 QListWidget(self.tab5) self.listwidget5.doubleClicked.connect(lambda: self.change(self.listwidget5)) self.listwidget5.setContextMenuPolicy(Qt.CustomContextMenu) self.listwidget5.customContextMenuRequested[QPoint].connect(self.myListWidgetContext5)
对子列表页面进行绑定之后 接下来我们就可以完成双击事件 获取歌曲名称 并进行本地播放。
# 加载当前播放的本地歌曲 def change(self, listwidget): global num global bo # print (item.flags()) bo local num int(listwidget.currentRow()) print(num) # self.label.setText(wenjianming)#设置标签的文本为音乐的名字 f str(SongName[num]).split( .mp3 ) f str(f[0]).split( .flac ) f str(f[0]).split( .MP3 ) f str(f[0]).split( .FLAC ) f str(f[0]).split( .wma ) f str(f[0]).split( .WMA ) self.label.setText(f[0]) print(listwidget.currentRow()) self.bofanglocal()
# 本地音乐播放 def bofanglocal(self): try: global pause try: self.photo( local ) except: pass self.console_button_3.setIcon(icon( fa.pause , color #F76677 , font 18)) pause False fill SongPath[num] print(fill) try: global timenum mp3 str(SongPath[num]) xx load(mp3) timenum xx.info.time_secs global start start True except: print( 进度条错误 播放失败 ) try: mixer.stop() except: pass try: mixer.music.load(SongPath[num]) # 载入音乐 mixer.music.play() # 播放音乐 except: print( MP3音频文件出现错误 ) except: sleep(0.1) print( system error ) self.next() pass
音乐播放器的本地音乐播放功能效果如下
至此 V2.0升级版的音乐播放器就完成了 一起来看看效果吧
源码及打包exe已上传 关注文末公众号回复【播放器源码】即可获取完整源码
Python往期精彩
见过仙女蹦迪吗 一起用python做个小仙女代码蹦迪视频
python自制一款炫酷音乐播放器 想听啥随便搜
斗地主老是输 一起用Python做个AI出牌器 豆子蹭蹭涨
一起用Python做个车牌自动识别系统 好玩又实用
桌面太单调 一起用Python做个自定义动态壁纸 竟然还可以放视频
一起用Python做个自动化弹钢琴脚本 我竟然弹出了《天空之城》
往期精彩源码均可通过下方公众号获取



