飞道的博客

pyqt5实现一个简易音乐播放器(升级到v2版本)

483人阅读  评论(0)

前言

假期最后一天,看到一篇文章使用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
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场