背景故事
基于Pyqt5制作的一款本地离线音乐播放器,支持.mp3和.wav音频文件的播放。开发初衷是省去每个月听歌会员,把喜欢的歌曲录制或转码下载到本地,即可永久使用了。
现有功能
- 选择指定文件夹的歌曲;
- 点击歌曲进行播放,再次点击暂停;
- 播放的进度条;
依赖安装
pip install pydub PyQT5
源码参数
源文件两个,music.ui和music.py,将两个文件放在同一目录下运行music.py即可。
music.py
import os
from pydub import AudioSegment
from PyQt5.QtGui import QStandardItemModel, QStandardItem
from PyQt5.QtWidgets import QApplication, QFileDialog, QTableView, QMainWindow
from PyQt5 import uic
from PyQt5.QtMultimedia import QMediaPlayer, QMediaContent
from PyQt5.QtCore import QUrl
class Music_Player(QMainWindow):
def __init__(self):
super().__init__()
# 初始化根目录
self.root_name = None
# 初始化歌曲名称
self.sing_name = ''
# 初始化点击次数
self.clicked_count = {}
# 初始化播放器
self.player = QMediaPlayer(self)
self.player.positionChanged.connect(self.update_progress)
# 动态加载UI文件
self.ui = uic.loadUi('Music Player.ui')
# 按钮绑定
self.ui.pushButton.clicked.connect(self.select_dir)
self.ui.tableView.clicked.connect(self.table_clicked)
# 创建一个数据模型
self.model = QStandardItemModel()
# 设置表头
self.model.setHorizontalHeaderLabels(["歌名", "歌手", "时长"])
# 将数据模型设置给表格视图
self.ui.tableView.setModel(self.model)
# 设置表格视图的属性
self.ui.tableView.setEditTriggers(QTableView.NoEditTriggers) # 禁止编辑
self.ui.tableView.setSelectionBehavior(QTableView.SelectRows) # 选择整行
#设置列宽
self.ui.tableView.setColumnWidth(0, 190)
self.ui.tableView.setColumnWidth(1, 190)
self.ui.tableView.setColumnWidth(2, 190)
self.ui.progressBar.setRange(0, 100)
def select_dir(self):
# 选择文件夹
FileDirectory = QFileDialog.getExistingDirectory(None, "选择歌曲文件夹", "D:/")
self.root_name = FileDirectory
# 获取目录列表
file_list = [i for i in os.listdir(FileDirectory) if i.endswith('mp3') or i.endswith('wav')]
if len(file_list) == 0:
pass
else:
result = []
# 获取文件夹名称 ,歌手信息
root_name = os.path.basename(FileDirectory)
for file in file_list:
obj_path = os.path.join(FileDirectory, file)
duration = self.get_audio_duration(obj_path)
result.append([file, root_name, duration]) # [:-4]
self.clicked_count[file] = 0
# 填充tabelView
for row in range(len(result)):
for column in range(3):
item = QStandardItem(result[row][column])
#设置每个位置的文本值
self.model.setItem(row,column, item)
def get_audio_duration(self, file_path):
# 获取音频的时长
audio = AudioSegment.from_file(file_path)
duration = audio.duration_seconds
minutes = int(duration / 60)
seconds = int(duration % 60)
return f"{minutes}:{seconds:02d}"
def table_clicked(self, index):
# 获取选中行的数据
row = index.row()
# 获取歌曲名称
sing_name = self.model.item(row, 0).text()
# 获取歌曲路径
sing_path = os.path.join(self.root_name, sing_name)
# 获取点击次数
count = self.clicked_count[sing_name]
if count % 2 == 0:
# 偶数次点击,执行播放操作
media_content = QMediaContent(QUrl.fromLocalFile(sing_path))
self.player.setMedia(media_content)
self.player.play()
else:
# 奇数次点击,执行暂停操作
self.player.pause()
# 更新点击次数
self.clicked_count[sing_name] += 1
def update_progress(self, position):
# 更新进度条
duration = self.player.duration()
if duration > 0:
progress = position / duration * 100
self.ui.progressBar.setValue(int(progress))
app = QApplication([])
Music = Music_Player()
Music.ui.show()
app.exec_()
music.ui
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>mainWindow</class>
<widget class="QMainWindow" name="mainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>791</width>
<height>557</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<property name="autoFillBackground">
<bool>false</bool>
</property>
<widget class="QWidget" name="centralwidget">
<widget class="QPushButton" name="pushButton">
<property name="geometry">
<rect>
<x>70</x>
<y>50</y>
<width>75</width>
<height>24</height>
</rect>
</property>
<property name="text">
<string>选择曲库</string>
</property>
</widget>
<widget class="QTableView" name="tableView">
<property name="geometry">
<rect>
<x>70</x>
<y>110</y>
<width>650</width>
<height>301</height>
</rect>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
</widget>
<widget class="QProgressBar" name="progressBar">
<property name="geometry">
<rect>
<x>140</x>
<y>460</y>
<width>511</width>
<height>16</height>
</rect>
</property>
<property name="value">
<number>0</number>
</property>
<property name="textVisible">
<bool>true</bool>
</property>
</widget>
</widget>
<widget class="QStatusBar" name="statusbar"/>
<widget class="QMenuBar" name="menuBar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>791</width>
<height>22</height>
</rect>
</property>
</widget>
</widget>
<resources/>
<connections/>
</ui>