用Python写了一款炫酷音乐播放器,想听啥随便搜!
本文链接:
https://blog.csdn.net/hhladminhhl/article/details/118915410
前言
下面,我们开始介绍这个音乐播放器的制作过程。
一、核心功能设计
二、实现步骤
1. UI设计排版布局
def init_ui(self):
global type
self.setFixedSize(1025, 750)
self.main_widget = QWidget() # 创建窗口主部件
self.main_layout = QGridLayout() # 创建主部件的网格布局
self.main_widget.setLayout(self.main_layout) # 设置窗口主部件布局为网格布局
self.close_widget = QWidget() # 创建关闭侧部件
self.close_widget.setObjectName('close_widget')
self.close_layout = QGridLayout() # 创建左侧部件的网格布局层
self.close_widget.setLayout(self.close_layout) # 设置左侧部件布局为网格
self.left_widget = QWidget() # 创建左边侧部件
self.left_widget.setObjectName('left_widget')
self.left_layout = QGridLayout() # 创建左侧部件的网格布局层
self.left_widget.setLayout(self.left_layout) # 设置左侧部件布局为网格
self.right_widget = QWidget() # 创建右侧部件
self.right_widget.setObjectName('right_widget')
self.right_layout = QGridLayout()
self.right_widget.setLayout(self.right_layout) # 设置右侧部件布局为网格
self.down_widget = QWidget() # 创建下面部件
self.down_widget.setObjectName('down_widget')
self.down_layout = QGridLayout()
self.down_widget.setLayout(self.down_layout) # 设置下侧部件布局为网格
self.up_widget = QWidget() # 创建下面部件
self.up_widget.setObjectName('up_widget')
self.up_layout = QGridLayout()
self.up_widget.setLayout(self.up_layout) # 设置下侧部件布局为网格
self.label = QLabel(self)
self.label.setText("还没有播放歌曲呢╰(*°▽°*)╯")
self.label.setStyleSheet("color:white")
self.label.setMaximumSize(310, 20)
self.main_layout.addWidget(self.up_widget, 0, 0, 1, 115)
self.main_layout.addWidget(self.left_widget, 1, 0, 90, 25)
self.main_layout.addWidget(self.right_widget, 1, 25, 90, 90) # 22右侧部件在第0行第3列,占8行9列
self.main_layout.addWidget(self.down_widget, 100, 0, 10, 115)
self.main_layout.addWidget(self.close_widget, 0, 110, 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.listwidget.doubleClicked.connect(lambda: self.change_func(self.listwidget))
self.listwidget.setContextMenuPolicy(Qt.CustomContextMenu)
self.listwidget.customContextMenuRequested[QPoint].connect(self.myListWidgetContext)
self.listwidget.setObjectName("listWidget")
self.tab_layout.addWidget(self.listwidget, 0, 0, 1, 1)
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.setObjectName("listWidget2")
self.listwidget2.setContextMenuPolicy(3)
self.tab2_layout.addWidget(self.listwidget2, 0, 0, 1, 1)
self.tabWidget.addTab(self.tab2, " 最近播放 ")
self.right_layout.addWidget(self.tabWidget, 3, 0, 100, 90)
self.left_close = QPushButton("") # 关闭按钮
self.left_close.clicked.connect(self.close)
self.left_visit = QPushButton("") # 空白按钮
self.left_visit.clicked.connect(self.big)
self.left_mini = QPushButton("") # 最小化按钮
self.left_mini.clicked.connect(self.mini)
self.close_layout.addWidget(self.left_mini, 0, 0, 1, 1)
self.close_layout.addWidget(self.left_close, 0, 2, 1, 1)
self.close_layout.addWidget(self.left_visit, 0, 1, 1, 1)
self.left_close.setFixedSize(15, 15) # 设置关闭按钮的大小
self.left_visit.setFixedSize(15, 15) # 设置按钮大小
self.left_mini.setFixedSize(15, 15) # 设置最小化按钮大小
self.left_close.setStyleSheet(
'''QPushButton{background:#F76677;border-radius:5px;}QPushButton:hover{background:red;}''')
self.left_visit.setStyleSheet(
'''QPushButton{background:#F7D674;border-radius:5px;}QPushButton:hover{background:yellow;}''')
self.left_mini.setStyleSheet(
'''QPushButton{background:#6DDF6D;border-radius:5px;}QPushButton:hover{background:green;}''')
self.button_123 = QLabel("")
self.left_layout.addWidget(self.button_123, 0, 2, 2, 2)
self.label2 = QLabel(self)
self.label2.setText("当前为顺序播放")
self.label2.setStyleSheet("color:green")
self.left_layout.addWidget(self.label2, 4, 0, 2, 1)
self.button_1234 = QPushButton(icon('fa.download', color='#3FC89C', font=24), "")
self.button_1234.clicked.connect(self.down)
self.button_1234.setStyleSheet(
'''QPushButton{background:#222225;border-radius:5px;}QPushButton:hover{background:#3684C8;}''')
self.left_layout.addWidget(self.button_1234, 4, 2, 2, 1)
self.button_1234 = QPushButton(icon('fa.heart', color='#3FC89C', font=24), "")
self.button_1234.clicked.connect(self.lovesong)
self.button_1234.setStyleSheet(
'''QPushButton{background:#222225;border-radius:5px;}QPushButton:hover{background:#3684C8;}''')
self.left_layout.addWidget(self.button_1234, 4, 3, 2, 2)
self.label3 = QLabel(self)
self.label3.setText("")
self.label3.setStyleSheet("color:white")
self.down_layout.addWidget(self.label3, 1, 3, 1, 1)
self.label7 = QLabel(self)
self.label7.setText("")
self.label7.setStyleSheet("color:white")
self.label5 = QLabel(self)
pix_img = QPixmap(str(data + '/backdown.png'))
pix = pix_img.scaled(300, 300, Qt.KeepAspectRatio)
self.label5.setPixmap(pix)
# self.label5.setMaximumSize(1,1)
self.left_layout.addWidget(self.label5, 2, 0, 2, 8)
self.label6 = QLabel(self)
self.label6.setText("")
self.label6.setStyleSheet("color:#6DDF6D")
self.left_layout.addWidget(self.label6, 2, 0, 2, 2)
self.label23 = QLabel(self)
self.label23.setText(" ")
self.label23.setStyleSheet("color:#6DDF6D")
self.up_layout.addWidget(self.label23, 0, 100, 1, 20)
self.shuru = QLineEdit("")
self.up_layout.addWidget(self.shuru, 0, 120, 1, 40)
self.shuru.returnPressed.connect(self.correct)
self.label23 = QLabel(self)
self.label23.setText(" 软件")
self.label23.setStyleSheet("color:#6DDF6D")
self.up_layout.addWidget(self.label23, 0, 160, 1, 10)
self.label61 = QLabel(self)
self.label61.setText("")
self.label61.setStyleSheet("color:#6DDF6D")
self.up_layout.addWidget(self.label61, 0, 200, 1, 50)
self.cb = QComboBox(self)
self.cb.addItems(['网易云', '酷狗', 'qq'])
self.up_layout.addWidget(self.cb, 0, 180, 1, 30)
self.cb.currentIndexChanged[int].connect(self.print)
self.button_1 = QPushButton(icon('fa.search', color='white'), "")
self.button_1.clicked.connect(self.correct)
self.button_1.setStyleSheet(
'''
QPushButton{color:white;border-radius:5px;}QPushButton:hover{background:green;}
''')
self.up_layout.addWidget(self.button_1, 0, 155, 1, 5)
self.right_process_bar = QProgressBar() # 播放进度部件
self.right_process_bar.setValue(49)
self.right_process_bar.setFixedHeight(3) # 设置进度条高度
self.right_process_bar.setTextVisible(False) # 不显示进度条文字
self.right_process_bar.setRange(0, 10000)
self.right_playconsole_widget = QWidget() # 播放控制部件
self.right_playconsole_layout = QGridLayout() # 播放控制部件网格布局层
self.right_playconsole_widget.setLayout(self.right_playconsole_layout)
self.console_button_1 = QPushButton(icon('fa.backward', color='#3FC89C'), "")
self.console_button_1.clicked.connect(self.last)
self.console_button_1.setStyleSheet(
'''QPushButton{background:#222225;border-radius:5px;}QPushButton:hover{background:#3684C8;}''')
self.console_button_2 = QPushButton(icon('fa.forward', color='#3FC89C'), "")
self.console_button_2.clicked.connect(self.nextion)
self.console_button_2.setStyleSheet(
'''QPushButton{background:#222225;border-radius:5px;}QPushButton:hover{background:#3684C8;}''')
self.console_button_3 = QPushButton(icon('fa.pause', color='#3FC89C', font=18), "")
self.console_button_3.clicked.connect(self.pause)
self.console_button_3.setStyleSheet(
'''QPushButton{background:#222225;border-radius:5px;}QPushButton:hover{background:#3684C8;}''')
self.console_button_4 = QPushButton(icon('fa.volume-down', color='#3FC89C', font=18), "")
self.console_button_4.clicked.connect(self.voicedown)
self.console_button_4.setStyleSheet(
'''QPushButton{background:#222225;border-radius:5px;}QPushButton:hover{background:#3684C8;}''')
self.console_button_5 = QPushButton(icon('fa.volume-up', color='#3FC89C', font=18), "")
self.console_button_5.clicked.connect(self.voiceup)
self.console_button_5.setStyleSheet(
'''QPushButton{background:#222225;border-radius:5px;}QPushButton:hover{background:#3684C8;}''')
self.console_button_6 = QPushButton(icon('fa.align-center', color='#3FC89C', font=18), "")
self.console_button_6.clicked.connect(self.playmode)
self.console_button_6.setStyleSheet(
'''QPushButton{background:#222225;border-radius:5px;}QPushButton:hover{background:#3684C8;}''')
self.console_button_3.setIconSize(QSize(30, 30))
self.right_playconsole_layout.addWidget(self.console_button_4, 0, 0)
self.right_playconsole_layout.addWidget(self.console_button_1, 0, 1)
self.right_playconsole_layout.addWidget(self.console_button_3, 0, 2)
self.right_playconsole_layout.addWidget(self.console_button_2, 0, 3)
self.right_playconsole_layout.addWidget(self.console_button_5, 0, 4)
self.right_playconsole_layout.addWidget(self.console_button_6, 0, 5)
self.right_playconsole_layout.setAlignment(Qt.AlignCenter) # 设置布局内部件居中显示
self.down_layout.addWidget(self.right_process_bar, 0, 0, 1, 4) # 第0行第0列,占8行3列
# 第0行第0列,占8行3列
self.down_layout.addWidget(self.label7, 1, 2, 1, 1)
self.down_layout.addWidget(self.right_playconsole_widget, 1, 0, 1, 4)
self.setWindowOpacity(0.95) # 设置窗口透明度
self.setAttribute(Qt.WA_TranslucentBackground)
self.setWindowFlag(Qt.FramelessWindowHint) # 隐藏边框
self.main_layout.setSpacing(0)
2. 关键字音乐列表爬虫
def run(self):
qmut.lock()
try:
global paing
global stop
global lrcs
global urls
global songs
global name
global songid
global proxies
global pic
global tryed
paing = True
print('搜索软件{}'.format(type))
print('开始搜索')
name = name
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'
}
urls = []
songs = []
pic = []
lrcs = []
pages = 5
print(pages)
for a in range(0, pages):
if not stop:
urlss = ['http://music.9q4.cn/', 'https://defcon.cn/dmusic/','http://www.xmsj.org/',
'http://music.laomao.me/']
print (tryed)
if tryed >3:
tryed = 0
url = urlss[tryed]
else:
url = urlss[tryed]
print (urlss[tryed])
params = {'input': name,
'filter': 'name',
'type': type,
'page': a
}
if not stop:
try:
res = post(url, params, headers=headers, proxies=proxies)
html = res.json()
for i in range(0, 10):
try:
title = jsonpath(html, '$..title')[i]
author = jsonpath(html, '$..author')[i]
url1 = jsonpath(html, '$..url')[i] # 取下载网址
pick = jsonpath(html, '$..pic')[i] # 取歌词
lrc = jsonpath(html, '$..lrc')[i]
print(title, author)
lrcs.append(lrc)
urls.append(url1)
pic.append(pick)
songs.append(str(title) + ' - ' + str(author))
except:
pass
except:
stop = False
paing = False
self.trigger.emit(str('finish'))
else:
self.trigger.emit(str('finish'))
else:
self.trigger.emit(str('clear'))
pass
stop = False
paing = False
except:
print('爬取歌曲出错')
self.trigger.emit(str('unfinish'))
stop = False
paing = False
qmut.unlock()
爬取代码写好了,我们还需要将爬取的这些音乐数据列表显示到画面中搜索页面中。代码如下:
def repite(self, name, type):
global tryed
global paing
global stop
self.listwidget.clear()
self.listwidget.addItem('搜索中')
self.listwidget.item(0).setForeground(Qt.white)
try:
if paing:
stop = True
self.listwidget.clear()
self.work2 = PAThread()
self.work2.start()
self.work2.trigger.connect(self.seafinish)
else:
self.work2 = PAThread()
self.work2.start()
self.work2.trigger.connect(self.seafinish)
except:
tryed = tryed + 1
get_info('https://www.kuaidaili.com/free/inha')
self.listwidget.addItem('貌似没网了呀`(*>﹏<*)′,再试一遍吧~')
self.listwidget.item(0).setForeground(Qt.white)
3. 音乐播放
class WorkThread(QThread):
trigger = pyqtSignal(str)
def __int__(self):
# 初始化函数
super(WorkThread, self).__init__()
# 进度条
def cbk(self, a, b, c):
'''''回调函数
@a:已经下载的数据块
@b:数据块的大小
@c:远程文件的大小
'''
per = 100.0 * a * b / c
if per > 100:
per = 100
# print ('%.2f%%' % per)
self.trigger.emit(str('%.2f%%' % per))
def run(self):
try:
global number
global path
global downloading
try:
proxies = {
'http': 'http://124.72.109.183:8118',
' Shttp': '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 = pic[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 = pic[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)
except:
pass
url1 = urls[num]
print(url1)
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):
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))
to = 'downloadmusic\{}.mp3'.format(songs[num])
makedirs('downloadmusic', exist_ok=True)
except:
pass
try:
copyfile(path, to)
except:
pass
downloading = False
self.trigger.emit(str('finish'))
except:
self.trigger.emit(str('nofinish'))
音乐播放模块:
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('播放系统错误')
# self.next()
pass
def display(self, sd):
global pause
global songed
global urled
global lrcd
global timenum
if sd == 'finish':
try:
if bo == 'boing':
self.label.setText(songs[num])
elif bo == 'boed':
self.label.setText(songed[num])
elif bo == 'love':
self.label.setText(loves[num])
try:
pix_img = QPixmap(str(data + '/back.png'))
pix = pix_img.scaled(300, 300, Qt.KeepAspectRatio)
self.label5.setPixmap(pix)
except:
pix_img = QPixmap(str(data + '/backdown.png'))
pix = pix_img.scaled(300, 300, Qt.KeepAspectRatio)
self.label5.setPixmap(pix)
print(str(data + '\{}.临时文件'.format(number)))
mixer.music.load(str(data + '\{}.临时文件'.format(number))) # 载入音乐
mixer.music.play()
self.console_button_3.setIcon(icon('fa.pause', color='#F76677', font=18))
pause = False
try:
mp3 = str(data + '\{}.临时文件'.format(number))
xx = load(mp3)
timenum = xx.info.time_secs
global start
start = True
except:
print('MP3错误,播放失败')
if bo == 'boing':
songed.append(songs[num])
urled.append(urls[num])
picd.append(pic[num])
lrcd.append(lrcs[num])
r = 0
self.listwidget2.clear()
for i in songed:
# self.listwidget.addItem(i)#将文件名添加到listWidget
self.listwidget2.addItem(i)
self.listwidget2.item(r).setForeground(Qt.white)
r = r + 1
else:
pass
# 播放音乐
except:
pass
elif sd == 'nofinish':
self.label.setText('下载错误')
elif sd == 'lrcfinish':
r = 0
for i in lrct:
if not i == '\r':
r = r + 1
else:
pass
elif sd == 'lrcnofinish':
pass
else:
self.label.setText('加速下载中,已完成{}'.format(sd))
至此,我们的音乐播放器已经可以正常播放音乐了。
4. 附加功能
def shui(self):
global num
global songs
if bo == 'boing':
q = int(len(songs) - 1)
num = int(randint(1, q))
elif bo == 'love':
q = int(len(loves) - 1)
num = int(randint(1, q))
else:
q = int(len(songed) - 1)
num = int(randint(0, q))
try:
print('随机播放下一首')
mixer.init()
self.Timer = QTimer()
self.Timer.start(500)
# self.Timer.timeout.connect(self.timercontorl)#时间函数,与下面的进度条和时间显示有关
if bo == 'boing':
self.label.setText(songs[num])
elif bo == 'love':
self.label.setText(loves[num])
else:
self.label.setText(songed[num])
self.bofang(num, bo) # 播放音乐
except:
pass
(2) 上一首、下一首:
def last(self):
global num
global songs
if num == 0:
print('冇')
num = len(songs) - 1
else:
num = num - 1
try:
self.bofang(num)
self.label.setText(songs[num])
except:
pass
#下一首
def next(self):
print ('nexting')
global num
global songs
if num == len(songs) - 1:
print('冇')
num = 0
else:
num = num + 1
try:
self.label.setText(songs[num])
self.bofang(num)
except:
print ('next error')
pass
(3)单曲循环:
def always(self):
try:
if bo == 'boing':
self.label.setText(songs[num])
else:
self.label.setText(songed[num])
self.bofang(num, bo) # 播放音乐
except:
pass
(4) 播放模式选择:
def playmode(self):
global play
try:
if play == 'shun':
play = 'shui'
print('切换到随机播放')
self.label2.setText("当前为随机播放")
try:
self.console_button_6.setIcon(icon('fa.random', color='#3FC89C', font=18))
print('done')
except:
print('none')
pass
# self.left_shui.setText('切换为单曲循环')
elif play == 'shui':
play = 'always'
print('切换到单曲循环')
self.label2.setText("当前为单曲循环")
try:
self.console_button_6.setIcon(icon('fa.retweet', color='#3FC89C', font=18))
print('done')
except:
print('none')
# self.left_shui.setText('切换为顺序播放')
elif play == 'always':
play = 'shun'
print('切换到顺序播放')
self.label2.setText("当前为顺序播放")
try:
self.console_button_6.setIcon(icon('fa.align-center', color='#3FC89C', font=18))
print('done')
except:
print('none')
# self.left_shui.setText('切换为随机播放')
except:
print('模式选择错误')
pass
播放暂停/开始:
def pause(self):
global pause
if pause:
try:
mixer.music.unpause()
except:
pass
self.console_button_3.setIcon(icon('fa.pause', color='#3FC89C', font=18))
pause = False
else:
try:
mixer.music.pause()
except:
pass
self.console_button_3.setIcon(icon('fa.play', color='#F76677', font=18))
pause = True
音量提高/降低:
def voiceup(self):
try:
print('音量加大')
global voice
voice += 0.1
if voice > 1:
voice = 1
mixer.music.set_volume(voice)
k = Decimal(voice).quantize(Decimal('0.00'))
self.label3.setText('音量:{}'.format(str(k * 100) + '%'))
except:
pass
def voicedown(self):
try:
print('音量减少')
global voice
voice -= 0.1
if voice < 0:
voice = 0
mixer.music.set_volume(voice)
k = Decimal(voice).quantize(Decimal('0.00'))
self.label3.setText('音量:{}'.format(str(k * 100) + '%'))
except:
pass
三、结束语
- EOF -
推荐阅读:
评论