从零开始:基于 Python PyQt5 打造多功能音乐播放器 | 支持播放、暂停、进度控制与歌词同步

效果图

前言

在日常开发中,多媒体应用无疑是最常见的需求之一。音乐播放器作为其中的典型代表,不仅要求具备流畅的播放体验,还需提供丰富的功能,例如音量控制、进度调节以及歌词同步显示等功能。本文将带领你一步步学习如何使用 Python 和 PyQt5 库开发一个功能全面的音乐播放器。我们将从基础的播放、暂停、停止功能开始,逐步扩展音量调节、进度控制以及自动同步歌词等实用功能。无论是初学者还是有一定经验的开发者,都能从中获得实用的开发技巧和知识。

## 环境安装

在开始之前,首先需要安装 PyQt5 库,它是我们开发音乐播放器的主要图形界面框架。在 Python 环境中,可以使用 pip 来安装 PyQt5,执行以下命令:

复制代码
pip install PyQt5

安装完成后,我们就可以开始编写代码,开发属于自己的多功能音乐播放器了。

技术栈

  • 编程语言: Python
  • GUI框架: PyQt5
  • 多媒体处理: PyQt5.QtMultimedia
  • 文件格式: 支持MP3、WAV、OGG音频格式及LRC歌词文件

功能特性

  1. 播放/暂停/停止控制
  2. 音量调节滑块
  3. 播放进度控制
  4. 实时歌词同步显示
  5. 自动加载同名歌词文件
  6. 友好的用户界面

项目结构

我们的音乐播放器主要由以下几个核心组件构成:

  • 主窗口类 MusicPlayer
  • 多媒体播放器 QMediaPlayer
  • 用户界面布局管理
  • 歌词解析与显示系统

核心代码实现

下面是完整的音乐播放器代码实现:

python 复制代码
import sys
import os
import re
from PyQt5.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QPushButton, QSlider, QLabel, QFileDialog, QWidget
from PyQt5.QtMultimedia import QMediaPlayer, QMediaContent
from PyQt5.QtCore import Qt, QUrl
from PyQt5.QtGui import QFont

class MusicPlayer(QMainWindow):
    def __init__(self):
        super().__init__()

        self.setWindowTitle("简单音乐播放器")
        self.setGeometry(100, 100, 600, 500)

        self.player = QMediaPlayer()
        self.lyrics = []  # 存储歌词 [(timestamp, lyric), ...]

        self.setup_ui()

        # 连接信号
        self.player.durationChanged.connect(self.update_duration)
        self.player.positionChanged.connect(self.update_position)
        self.player.positionChanged.connect(lambda pos: self.update_lyrics())  # 歌词随进度更新

        self.show()

    def setup_ui(self):
        central_widget = QWidget(self)
        self.setCentralWidget(central_widget)
        layout = QVBoxLayout()

        # 播放/暂停按钮
        self.play_pause_button = QPushButton("播放")
        self.play_pause_button.clicked.connect(self.play_pause_music)
        layout.addWidget(self.play_pause_button)

        # 停止按钮
        stop_button = QPushButton("停止")
        stop_button.clicked.connect(self.stop_music)
        layout.addWidget(stop_button)

        # 音量调节
        self.volume_slider = QSlider(Qt.Horizontal)
        self.volume_slider.setRange(0, 100)
        self.volume_slider.setValue(50)
        self.volume_slider.valueChanged.connect(self.set_volume)
        layout.addWidget(QLabel("音量"))
        layout.addWidget(self.volume_slider)

        # 进度条
        self.progress_slider = QSlider(Qt.Horizontal)
        self.progress_slider.setEnabled(False)
        self.progress_slider.sliderMoved.connect(self.set_position)
        layout.addWidget(QLabel("进度"))
        layout.addWidget(self.progress_slider)

        # 歌词显示
        self.lyrics_label = QLabel("暂无歌词")
        self.lyrics_label.setAlignment(Qt.AlignCenter)
        self.lyrics_label.setWordWrap(True)
        self.lyrics_label.setFont(QFont("Microsoft YaHei", 16))  # 美化字体
        self.lyrics_label.setStyleSheet("color: #333333;")
        layout.addWidget(self.lyrics_label)

        # 选择文件按钮
        select_file_button = QPushButton("选择文件")
        select_file_button.clicked.connect(self.select_music_file)
        layout.addWidget(select_file_button)

        central_widget.setLayout(layout)

    def play_pause_music(self):
        if self.player.state() == QMediaPlayer.PlayingState:
            self.player.pause()
            self.play_pause_button.setText("播放")
        else:
            self.player.play()
            self.play_pause_button.setText("暂停")
            self.progress_slider.setEnabled(True)

    def stop_music(self):
        self.player.stop()
        self.play_pause_button.setText("播放")
        self.progress_slider.setEnabled(False)
        self.lyrics_label.setText("暂无歌词")

    def set_volume(self, value):
        self.player.setVolume(value)

    def set_position(self, position):
        self.player.setPosition(position)

    def update_duration(self, duration):
        self.progress_slider.setMaximum(duration)

    def update_position(self, position):
        # 当用户正在拖动进度条时不更新,防止跳动
        if not self.progress_slider.isSliderDown():
            self.progress_slider.setValue(position)

    def select_music_file(self):
        file_dialog = QFileDialog()
        file_dialog.setNameFilter("音频文件 (*.mp3 *.wav *.ogg)")
        if file_dialog.exec_() == QFileDialog.Accepted:
            selected_file = file_dialog.selectedFiles()[0]
            self.player.setMedia(QMediaContent(QUrl.fromLocalFile(selected_file)))
            self.player.setVolume(self.volume_slider.value())

            # 重置歌词
            self.lyrics = []
            self.lyrics_label.setText("加载中...")

            # 加载同名 lrc 文件
            lyrics_file = os.path.splitext(selected_file)[0] + ".lrc"
            if os.path.exists(lyrics_file):
                self.load_lyrics(lyrics_file)

            # 初始更新歌词显示(位置为0)
            self.update_lyrics()

    def load_lyrics(self, lyrics_file):
        self.lyrics = []
        try:
            with open(lyrics_file, "r", encoding="utf-8") as f:
                time_pattern = re.compile(r'\[(\d{1,2}:\d{2}\.?\d*)\]')
                for line in f:
                    line = line.strip()
                    if not line:
                        continue
                    times = time_pattern.findall(line)
                    lyric = time_pattern.sub('', line).strip()
                    if times and lyric:
                        for t in times:
                            try:
                                mm, ss = t.split(':')
                                timestamp = int(mm) * 60 + float(ss)
                                self.lyrics.append((timestamp, lyric))
                            except ValueError:
                                continue
        except Exception as e:
            print(f"歌词加载失败: {e}")

        self.lyrics.sort(key=lambda x: x[0])
        print(f"成功加载 {len(self.lyrics)} 行歌词")

    def update_lyrics(self):
        if not self.lyrics:
            self.lyrics_label.setText("无歌词可用")
            return

        current_pos = self.player.position() / 1000.0

        # 找到当前行(时间戳 <= 当前位置 的最大索引)
        current_index = -1
        for i, (ts, _) in enumerate(self.lyrics):
            if ts <= current_pos:
                current_index = i
            else:
                break

        # 显示前后各4行(可调整)
        before = 4
        after = 4
        start = max(0, current_index - before) if current_index >= 0 else 0
        end = min(len(self.lyrics), current_index + after + 1) if current_index >= 0 else before + after + 1

        lines = []
        for i in range(start, min(end, len(self.lyrics))):
            lyric = self.lyrics[i][1]
            if current_index >= 0 and i == current_index:
                # 当前行:红色、加大、加粗
                lines.append(f'<font color="red" size="+3"><b>{lyric}</b></font>')
            elif i <= current_index:
                # 已唱:深灰
                lines.append(f'<font color="#555555">{lyric}</font>')
            else:
                # 未唱:浅灰
                lines.append(f'<font color="#aaaaaa">{lyric}</font>')

        if current_index == -1:
            lines.insert(0, '<font color="#888888">歌曲即将开始...</font>')

        self.lyrics_label.setText("<br>".join(lines))


if __name__ == "__main__":
    app = QApplication(sys.argv)
    player = MusicPlayer()
    sys.exit(app.exec_())

代码解析

1. 初始化部分

__init__ 方法中,我们设置了窗口标题、大小,并初始化了多媒体播放器和歌词存储列表。

2. UI界面设置

setup_ui 方法负责构建整个用户界面,包括:

  • 播放/暂停按钮
  • 停止按钮
  • 音量调节滑块
  • 播放进度条
  • 歌词显示区域
  • 文件选择按钮

3. 播放控制

播放/暂停功能通过 play_pause_music 方法实现,根据播放器当前状态切换播放/暂停状态。

4. 歌词处理

歌词功能是本播放器的一大亮点,主要包括:

  • load_lyrics 方法:解析LRC格式歌词文件
  • update_lyrics 方法:根据播放进度实时更新歌词显示

扩展功能建议

  1. 播放列表管理:添加多首歌曲播放列表
  2. 均衡器:集成音频均衡器功能
  3. 皮肤主题:提供更多界面主题选项
  4. 音频可视化:添加频谱分析显示
  5. 快捷键支持:为常用功能添加键盘快捷键

总结

本文展示了如何使用PyQt5构建一个功能丰富的音乐播放器。通过这个项目,我们可以学到:

  • PyQt5 GUI界面设计
  • 多媒体处理技术
  • 文件格式解析
  • 实时数据更新机制

这个播放器虽然功能相对基础,但代码结构清晰,易于扩展。读者可以根据自己的需求添加更多功能,比如播放列表、音效处理等。

希望这篇文章对您的PyQt5学习有所帮助!

相关推荐
Warren983 分钟前
Allure 常用装饰器:实战用法 + 最佳实践(接口自动化)
运维·服务器·git·python·单元测试·自动化·pytest
2401_841495645 分钟前
【LeetCode刷题】翻转二叉树
python·算法·leetcode··递归·节点·翻转二叉树
森爱。7 分钟前
web开发全家桶(django+前端+数据库)
前端·python·django
七夜zippoe13 分钟前
数据可视化高级技巧:Matplotlib + Seaborn实战大全
python·信息可视化·matplotlib·数据可视化·seaborn·gridspec
郝学胜-神的一滴14 分钟前
线性判别分析(LDA)原理详解与实战应用
人工智能·python·程序人生·算法·机器学习·数据挖掘·sklearn
徐同保15 分钟前
python使用vscode打断点调试
开发语言·python
小鸡吃米…16 分钟前
机器学习 - 对抗性机器学习
人工智能·python·机器学习
gentle coder19 分钟前
【langchain】agent部署的基础入门代码(持续更新中~)
python·langchain·react
ZCXZ12385296a28 分钟前
汽车损伤检测技术实现:YOLO13-C3k2-ConvFormer模型优化与性能分析_1
python
晨非辰30 分钟前
Linux包管理器速成:yum/apt双精要/镜像源加速/依赖解析30分钟通解,掌握软件安装的艺术与生态哲学
linux·运维·服务器·c++·人工智能·python