前言
假期最后一天,看到一篇文章使用pyqt实现了一个音乐播放器,刚好前段时间学完pyqt,来撸一个玩一玩,最终的效果如下:
后来又萌生想法,升级了一下UI:
一、安装pyqt5
新建虚拟环境:
python -m venv venv
激活虚拟环境,安装pyqt5:
pip install pyqt5
二、类设计
MP3音乐播放器的所有内容都设计为MP3Player类,所以main.c文件中的内容变的简单:
#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""
MP3 player
author: Mculover666
version: v1.0.0
"""
import sys
from MP3Player import MP3Player
from PyQt5.QtWidgets import (QApplication)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = MP3Player()
sys.exit(app.exec_())
MP3Player类的文件为:MP3Player.py
。
from PyQt5.QtWidgets import (QWidget, QDesktopWidget,
QMessageBox, QHBoxLayout, QVBoxLayout, QSlider, QListWidget,
QPushButton, QLabel, QComboBox)
from PyQt5.QtGui import QIcon
from PyQt5.QtCore import Qt
class MP3Player(QWidget):
def __init__(self):
super().__init__()
#add your code here
self.initUI()
def initUI(self):
self.resize(600, 400)
self.center()
self.setWindowTitle('MP3 player')
self.setWindowIcon(QIcon('resource/music.png'))
self.show()
# bring the window to the center of the screen
def center(self):
qr = self.frameGeometry()
cp = QDesktopWidget().availableGeometry().center()
qr.moveCenter(cp)
self.move(qr.topLeft())
图标文件从iconfont下载,放在resource文件夹下,文件名为music.png:
此时运行main.py,可以看到窗口和图标:
三、界面设计
def __init__(self):
super().__init__()
self.startTimeLabel = QLabel('00:00')
self.endTimeLabel = QLabel('00:00')
self.slider = QSlider(Qt.Horizontal, self)
self.playBtn = QPushButton('播放', self)
self.prevBtn = QPushButton('上一曲', self)
self.nextBtn = QPushButton('下一曲', self)
self.openBtn = QPushButton('打开文件夹', self)
self.musicList = QListWidget()
self.modeCom = QComboBox()
self.modeCom.addItem('顺序播放')
self.modeCom.addItem('单曲循环')
self.modeCom.addItem('随机播放')
self.authorLabel = QLabel('Mculover666')
self.textLable = QLabel('星光不问赶路人,时光不负有心人')
self.versionLabel = QLabel('v1.0.0')
self.hBoxSlider = QHBoxLayout()
self.hBoxSlider.addWidget(self.startTimeLabel)
self.hBoxSlider.addWidget(self.slider)
self.hBoxSlider.addWidget(self.endTimeLabel)
self.hBoxButton = QHBoxLayout()
self.hBoxButton.addWidget(self.playBtn)
self.hBoxButton.addWidget(self.nextBtn)
self.hBoxButton.addWidget(self.prevBtn)
self.hBoxButton.addWidget(self.modeCom)
self.hBoxButton.addWidget(self.openBtn)
self.vBoxControl = QVBoxLayout()
self.vBoxControl.addLayout(self.hBoxSlider)
self.vBoxControl.addLayout(self.hBoxButton)
self.hBoxAbout = QHBoxLayout()
self.hBoxAbout.addWidget(self.authorLabel)
self.hBoxAbout.addStretch(1)
self.hBoxAbout.addWidget(self.textLable)
self.hBoxAbout.addStretch(1)
self.hBoxAbout.addWidget(self.versionLabel)
self.vboxMain = QVBoxLayout()
self.vboxMain.addWidget(self.musicList)
self.vboxMain.addLayout(self.vBoxControl)
self.vboxMain.addLayout(self.hBoxAbout)
self.setLayout(self.vboxMain)
self.initUI()
设计的界面如下图:
四、实现功能
1. 打开文件夹添加音乐功能
引入 QFileDialog 类:
from PyQt5.QtWidgets import (QWidget, QDesktopWidget,
QMessageBox, QHBoxLayout, QVBoxLayout, QSlider, QListWidget,
QPushButton, QLabel, QComboBox, QFileDialog)
添加支持音乐格式和音乐列表成员(在__init__
函数中):
self.song_formats = ['mp3', 'm4a', 'flac', 'wav', 'ogg']
self.songs_list = []
self.cur_playing_song = ''
self.is_pause = True
编写读取文件夹和添加音乐列表功能的函数:
# open floder
def openMusicFloder(self):
self.cur_path = QFileDialog.getExistingDirectory(self, "选取音乐文件夹", './')
if self.cur_path:
self.showMusicList()
self.cur_playing_song = ''
self.startTimeLabel.setText('00:00')
self.endTimeLabel.setText('00:00')
self.slider.setSliderPosition(0)
self.is_pause = True
self.playBtn.setText('播放')
#show music list
def showMusicList(self):
self.musicList.clear()
for song in os.listdir(self.cur_path):
if song.split('.')[-1] in self.song_formats:
self.songs_list.append([song, os.path.join(self.cur_path, song).replace('\\', '/')])
self.musicList.addItem(song)
self.musicList.setCurrentRow(0)
if self.songs_list:
self.cur_playing_song = self.songs_list[self.musicList.currentRow()][-1]
设置打开按钮的回调函数:
self.openBtn.clicked.connect(self.openMusicFloder)
提前下载放到文件夹中:
运行程序,选择该文件夹,可以看到播放列表添加成功:
2. 播放音乐功能
引入 QMediaPlayer类,QMediaContent类,QUrl类:
from PyQt5.QtCore import Qt, QUrl
from PyQt5.QtMultimedia import QMediaPlayer, QMediaContent
添加用于实现播放/暂停功能的新成员:
self.player = QMediaPlayer()
self.is_switching = False
编写一个基于MessageBox的提示函数:
# 提示
def Tips(self, message):
QMessageBox.about(self, "提示", message)
编写播放当前设置的音乐函数:
# 设置当前播放的音乐
def setCurPlaying(self):
self.cur_playing_song = self.songs_list[self.musicList.currentRow()][-1]
self.player.setMedia(QMediaContent(QUrl(self.cur_playing_song)))
编写播放/暂停函数:
# 播放音乐
def playMusic(self):
if self.musicList.count() == 0:
self.Tips('当前路径内无可播放的音乐文件')
return
if not self.player.isAudioAvailable():
self.setCurPlaying()
if self.is_pause or self.is_switching:
self.player.play()
self.is_pause = False
self.playBtn.setText('暂停')
elif (not self.is_pause) and (not self.is_switching):
self.player.pause()
self.is_pause = True
self.playBtn.setText('播放')
最后设置播放按钮的回调函数:
self.playBtn.clicked.connect(self.playMusic)
此时已经可以正常的播放、暂停。
3. 歌曲切换功能
歌曲切换功能包括三个:
- 上一曲
- 下一曲
- 双击音乐播放
编写上一曲和下一曲函数:
# 上一曲
def prevMusic(self):
self.slider.setValue(0)
if self.musicList.count() == 0:
self.Tips('当前路径内无可播放的音乐文件')
return
pre_row = self.musicList.currentRow()-1 if self.musicList.currentRow() != 0 else self.musicList.count() - 1
self.musicList.setCurrentRow(pre_row)
self.is_switching = True
self.setCurPlaying()
self.playMusic()
self.is_switching = False
# 下一曲
def nextMusic(self):
self.slider.setValue(0)
if self.musicList.count() == 0:
self.Tips('当前路径内无可播放的音乐文件')
return
next_row = self.musicList.currentRow()+1 if self.musicList.currentRow() != self.musicList.count()-1 else 0
self.musicList.setCurrentRow(next_row)
self.is_switching = True
self.setCurPlaying()
self.playMusic()
self.is_switching = False
设置上一曲按钮和下一曲按钮的回调函数:
self.prevBtn.clicked.connect(self.prevMusic)
self.nextBtn.clicked.connect(self.nextMusic)
编写双击歌曲播放的函数:
# 双击歌曲名称播放音乐
def doubleClicked(self):
self.slider.setValue(0)
self.is_switching = True
self.setCurPlaying()
self.playMusic()
self.is_switching = False
设置双击事件回调函数:
self.musicList.itemDoubleClicked.connect(self.doubleClicked)
4. 进度条
进度条需要每1s刷新一次,所以肯定需要用定时器来实现。
导入QTimer类:
from PyQt5.QtCore import Qt, QUrl, QTimer
引入 Time 类:
import os, time
首先编写设置进度条的函数:
# 根据播放模式自动播放,并刷新进度条
def playByMode(self):
# 刷新进度条
if (not self.is_pause) and (not self.is_switching):
self.slider.setMinimum(0)
self.slider.setMaximum(self.player.duration())
self.slider.setValue(self.slider.value() + 1000)
self.startTimeLabel.setText(time.strftime('%M:%S', time.localtime(self.player.position()/1000)))
self.endTimeLabel.setText(time.strftime('%M:%S', time.localtime(self.player.duration()/1000)))
# 顺序播放
if (self.modeCom.currentIndex() == 0) and (not self.is_pause) and (not self.is_switching):
if self.musicList.count() == 0:
return
if self.player.position() == self.player.duration():
self.nextMusic()
# 单曲循环
elif (self.modeCom.currentIndex() == 1) and (not self.is_pause) and (not self.is_switching):
if self.musicList.count() == 0:
return
if self.player.position() == self.player.duration():
self.is_switching = True
self.setCurPlaying()
self.slider.setValue(0)
self.playMusic()
self.is_switching = False
# 随机播放
elif (self.modeCom.currentIndex() == 2) and (not self.is_pause) and (not self.is_switching):
if self.musicList.count() == 0:
return
if self.player.position() == self.player.duration():
self.is_switching = True
self.musicList.setCurrentRow(random.randint(0, self.musicList.count()-1))
self.setCurPlaying()
self.slider.setValue(0)
self.playMusic()
self.is_switching = False
在初始化时设置定时器:
self.timer = QTimer(self)
self.timer.start(1000)
self.timer.timeout.connect(self.playByMode)
运行,可以看到进度条实时刷新。
接着还要实现反向功能,由进度条拖动控制播放进度,添加下面这行代码添加回调函数实现:
self.slider.sliderMoved[int].connect(lambda: self.player.setPosition(self.slider.value()))
5. 配置文件读写的实现
每次运行软件时,都要手动设置音乐文件夹的目录,非常麻烦,使用配置文件来实现记录配置的功能。
配置文件读写使用 configparser 模块实现。
导入该模块:
import configparser
添加配置文件名的成员:
self.settingfilename = 'config.ini'
5.1. 写配置文件的实现
编写记录配置的函数:
# 更新配置文件
def updateSetting(self):
config = configparser.ConfigParser()
config.read(self.settingfilename)
if not os.path.isfile(self.settingfilename):
config.add_section('MP3Player')
config.set('MP3Player', 'PATH', self.cur_path)
config.write(open(self.settingfilename, 'w'))
在用户选择完音乐文件夹之后调用:
# 打开文件夹
def openMusicFloder(self):
self.cur_path = QFileDialog.getExistingDirectory(self, "选取音乐文件夹", './')
if self.cur_path:
self.showMusicList()
self.cur_playing_song = ''
self.startTimeLabel.setText('00:00')
self.endTimeLabel.setText('00:00')
self.slider.setSliderPosition(0)
self.updateSetting()
self.is_pause = True
self.playBtn.setText('播放')
运行,选择音乐文件夹,软件创建的配置文件内容为:
5.2. 加载配置文件的实现
编写加载配置文件的函数:
# 加载配置文件
def loadingSetting(self):
config = configparser.ConfigParser()
config.read(self.settingfilename)
if not os.path.isfile(self.settingfilename):
return
self.cur_path = config.get('MP3Player', 'PATH')
self.showMusicList()
在软件初始化阶段自动加载配置文件:
self.loadingSetting()
运行软件,可以看到自动加载出上次用户设置的目录。
6. 退出确认功能的实现
目前用户点击关闭按钮后,软件直接退出,接下来我们添加一个弹窗,确认用户是否要真的退出:
# 确认用户是否要真正退出
def closeEvent(self, event):
reply = QMessageBox.question(self, 'Message',
"确定要退出吗?", QMessageBox.Yes |
QMessageBox.No, QMessageBox.No)
if reply == QMessageBox.Yes:
event.accept()
else:
event.ignore()
添加该函数实现后,当我们再次点击退出按钮时,即可看到提示信息:
至此,音乐播放器实现完成。
五、升级UI界面
v1版本用的都是普通按钮,太丑了,v2版本升级都用背景图片显示:
1. 按钮显示背景图片
self.playBtn.setStyleSheet("QPushButton{border-image: url(resource/image/play.png)}")
self.playBtn.setFixedSize(48, 48)
self.nextBtn.setStyleSheet("QPushButton{border-image: url(resource/image/next.png)}")
self.nextBtn.setFixedSize(48, 48)
self.prevBtn.setStyleSheet("QPushButton{border-image: url(resource/image/prev.png)}")
self.prevBtn.setFixedSize(48, 48)
self.openBtn.setStyleSheet("QPushButton{border-image: url(resource/image/open.png)}")
self.openBtn.setFixedSize(24, 24)
2. 播放模式设置按钮
v1版本使用comboBox选择播放模式,v2版本使用按钮点击,轮流选择。
先在初始化代码中创建按钮:
self.PlayModeBtn = QPushButton(self)
设置默认背景图片:
self.PlayModeBtn.setStyleSheet("QPushButton{border-image: url(resource/image/sequential.png)}")
self.PlayModeBtn.setFixedSize(24, 24)
编写回调函数设置播放模式:
# 播放模式设置
def playModeSet(self):
# 设置为单曲循环模式
if self.playMode == 0:
self.playMode = 1
self.PlayModeBtn.setStyleSheet("QPushButton{border-image: url(resource/image/circulation.png)}")
# 设置为随机播放模式
elif self.playMode == 1:
self.playMode = 2
self.PlayModeBtn.setStyleSheet("QPushButton{border-image: url(resource/image/random.png)}")
# 设置为顺序播放模式
elif self.playMode == 2:
self.playMode = 0
self.PlayModeBtn.setStyleSheet("QPushButton{border-image: url(resource/image/sequential.png)}")
修改之前的定时器回调函数:
# 根据播放模式自动播放,并刷新进度条
def playByMode(self):
# 刷新进度条
if (not self.is_pause) and (not self.is_switching):
self.slider.setMinimum(0)
self.slider.setMaximum(self.player.duration())
self.slider.setValue(self.slider.value() + 1000)
self.startTimeLabel.setText(time.strftime('%M:%S', time.localtime(self.player.position()/1000)))
self.endTimeLabel.setText(time.strftime('%M:%S', time.localtime(self.player.duration()/1000)))
# 顺序播放
if (self.playMode == 0) and (not self.is_pause) and (not self.is_switching):
if self.musicList.count() == 0:
return
if self.player.position() == self.player.duration():
self.nextMusic()
# 单曲循环
elif (self.playMode == 1) and (not self.is_pause) and (not self.is_switching):
if self.musicList.count() == 0:
return
if self.player.position() == self.player.duration():
self.is_switching = True
self.setCurPlaying()
self.slider.setValue(0)
self.playMusic()
self.is_switching = False
# 随机播放
elif (self.playMode == 2) and (not self.is_pause) and (not self.is_switching):
if self.musicList.count() == 0:
return
if self.player.position() == self.player.duration():
self.is_switching = True
self.musicList.setCurrentRow(random.randint(0, self.musicList.count()-1))
self.setCurPlaying()
self.slider.setValue(0)
self.playMusic()
self.is_switching = False
最后将回调函数与按钮绑定:
self.PlayModeBtn.clicked.connect(self.playModeSet)
v2版本效果图如下:
转载:https://blog.csdn.net/Mculover666/article/details/116424865