深入解析:PYcharm——pyqt音乐播放器

news/2025/9/17 20:06:31/文章来源:https://www.cnblogs.com/ljbguanli/p/19097456

深入解析:PYcharm——pyqt音乐播放器

在这里插入图片描述

系统概述

使用python编写一个简单的音乐播放器,假如你也无聊不妨动手试一下。
建一个基于 PyQt 的音乐播放器,实现图片中展示的功能,包括播放控制、进度条、音量调节、播放列表等核心功能。

我们将使用 PyQt5 作为 GUI 框架,结合 PyQt5 的 QMediaPlayer 组件来实现音频播放功能。
该音乐播放器构建了以下功能:

核心播放控制:播放、暂停、停止、上一首、下一首
进度条显示和控制:可以拖动进度条调整播放位置
音量调节功能
播放列表管理:添加音乐、删除音乐、清空列表
歌曲信息显示:当前播放歌曲名、播放时间
右键菜单支持:对播放列表项进行操作

使用方法:

点击 “添加音乐” 按钮选择本地音频文件(支持 mp3、wav、flac、m4a 等格式)
在播放列表中双击歌曲开始播放
使用控制按钮进行播放控制
可以拖动进度条调整播放位置
使用音量滑块调节音量大小

要在 PyCharm 中运行此程序,你需先安装 PyQt5 和 PyQt5 多媒体模块:

通过代码已经设置了中文字体支持,能够正常显示中文。界面布局简洁直观,符合图片中展示的音乐播放器基本功能需求。你可能根据需要进一步美化界面或添加更多功能,如均衡器、歌词显现等。

软件介绍

import sys
import os
from PyQt5.QtWidgets import (QApplication, QMainWindow, QPushButton, QSlider,
QLabel, QListWidget, QFileDialog, QVBoxLayout,
QHBoxLayout, QWidget, QStyle, QListWidgetItem,
QSplitter, QMenu, QAction, QMessageBox)
from PyQt5.QtCore import Qt, QUrl, QTimer, QPoint
from PyQt5.QtMultimedia import QMediaPlayer, QMediaContent
from PyQt5.QtGui import QIcon, QPixmap, QFont
class MusicPlayer
(QMainWindow):
def __init__(self):
super().__init__()
# 设置中文字体支持
font = QFont()
font.setFamily("SimHei")
self.setFont(font)
# 初始化媒体播放器
self.player = QMediaPlayer()
self.player.durationChanged.connect(self.update_duration)
self.player.positionChanged.connect(self.update_position)
self.player.stateChanged.connect(self.update_state)
# 当前播放列表索引
self.current_index = -1
self.playlist = []
# 初始化UI
self.init_ui()
def init_ui(self):
# 设置窗口标题和大小
self.setWindowTitle("音乐播放器")
self.setGeometry(300, 300, 800, 600)
# 创建中心部件
central_widget = QWidget()
self.setCentralWidget(central_widget)
# 主布局
main_layout = QVBoxLayout(central_widget)
# 专辑封面
self.album_cover = QLabel()
self.album_cover.setAlignment(Qt.AlignCenter)
self.album_cover.setMinimumHeight(300)
# 设置默认封面
default_pixmap = QPixmap(300, 300)
default_pixmap.fill(Qt.lightGray)
self.album_cover.setPixmap(default_pixmap.scaled(
self.album_cover.width(), self.album_cover.height(),
Qt.KeepAspectRatio, Qt.SmoothTransformation))
main_layout.addWidget(self.album_cover)
# 歌曲信息
self.song_info = QLabel("准备就绪")
self.song_info.setAlignment(Qt.AlignCenter)
self.song_info.setStyleSheet("font-size: 16px; font-weight: bold;")
main_layout.addWidget(self.song_info)
# 进度条
self.progress_layout = QHBoxLayout()
self.time_label = QLabel("00:00")
self.progress_layout.addWidget(self.time_label)
self.progress_bar = QSlider(Qt.Horizontal)
self.progress_bar.sliderMoved.connect(self.set_position)
self.progress_layout.addWidget(self.progress_bar)
self.total_time_label = QLabel("00:00")
self.progress_layout.addWidget(self.total_time_label)
main_layout.addLayout(self.progress_layout)
# 控制按钮
self.controls_layout = QHBoxLayout()
self.prev_button = QPushButton()
self.prev_button.setIcon(self.style().standardIcon(QStyle.SP_MediaSkipBackward))
self.prev_button.clicked.connect(self.play_previous)
self.controls_layout.addWidget(self.prev_button)
self.play_button = QPushButton()
self.play_button.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay))
self.play_button.clicked.connect(self.toggle_play_pause)
self.controls_layout.addWidget(self.play_button)
self.stop_button = QPushButton()
self.stop_button.setIcon(self.style().standardIcon(QStyle.SP_MediaStop))
self.stop_button.clicked.connect(self.stop)
self.controls_layout.addWidget(self.stop_button)
self.next_button = QPushButton()
self.next_button.setIcon(self.style().standardIcon(QStyle.SP_MediaSkipForward))
self.next_button.clicked.connect(self.play_next)
self.controls_layout.addWidget(self.next_button)
self.controls_layout.addSpacing(20)
# 音量控制
self.volume_label = QLabel("音量:")
self.controls_layout.addWidget(self.volume_label)
self.volume_slider = QSlider(Qt.Horizontal)
self.volume_slider.setMaximumWidth(100)
self.volume_slider.setValue(70)
self.player.setVolume(70)
self.volume_slider.valueChanged.connect(self.set_volume)
self.controls_layout.addWidget(self.volume_slider)
main_layout.addLayout(self.controls_layout)
# 分割器
splitter = QSplitter(Qt.Vertical)
# 播放列表
self.playlist_label = QLabel("播放列表")
self.playlist_label.setStyleSheet("font-size: 14px; font-weight: bold;")
main_layout.addWidget(self.playlist_label)
self.playlist_widget = QListWidget()
self.playlist_widget.doubleClicked.connect(self.play_selected)
# 右键菜单支持
self.playlist_widget.setContextMenuPolicy(Qt.CustomContextMenu)
self.playlist_widget.customContextMenuRequested.connect(self.show_context_menu)
main_layout.addWidget(self.playlist_widget)
# 底部按钮
self.bottom_layout = QHBoxLayout()
self.add_button = QPushButton("添加音乐")
self.add_button.clicked.connect(self.add_music)
self.bottom_layout.addWidget(self.add_button)
self.clear_button = QPushButton("清空列表")
self.clear_button.clicked.connect(self.clear_playlist)
self.bottom_layout.addWidget(self.clear_button)
main_layout.addLayout(self.bottom_layout)
# 状态栏显示当前状态
self.statusBar().showMessage("就绪")
# 显示窗口
self.show()
def add_music(self):
"""添加音乐到播放列表"""
files, _ = QFileDialog.getOpenFileNames(
self, "选择音乐文件", "", "音频文件 (*.mp3 *.wav *.flac *.m4a)"
)
if files:
for file in files:
# 确保文件路径正确
file_url = QUrl.fromLocalFile(file)
# 获取文件名
file_name = os.path.basename(file)
# 添加到播放列表
self.playlist.append((file_name, file_url))
# 添加到列表控件
self.playlist_widget.addItem(file_name)
self.statusBar().showMessage(f"已添加 {
len(files)
} 首音乐")
# 如果是第一个添加的音乐,自动选中
if len(self.playlist) == len(files):
self.playlist_widget.setCurrentRow(0)
self.current_index = 0
def play_selected(self):
"""播放选中的音乐"""
selected_index = self.playlist_widget.currentRow()
if selected_index >= 0 and selected_index <
len(self.playlist):
self.current_index = selected_index
self.play_current()
def play_current(self):
"""播放当前索引的音乐"""
if 0 <= self.current_index <
len(self.playlist):
song_name, url = self.playlist[self.current_index]
self.player.setMedia(QMediaContent(url))
self.player.play()
self.song_info.setText(f"正在播放: {song_name
}")
self.statusBar().showMessage(f"正在播放: {song_name
}")
def toggle_play_pause(self):
"""切换播放/暂停状态"""
if self.player.state() == QMediaPlayer.PlayingState:
self.player.pause()
else:
if self.player.mediaStatus() == QMediaPlayer.NoMedia and len(self.playlist) >
0:
self.current_index = 0
self.play_current()
else:
self.player.play()
def stop(self):
"""停止播放"""
self.player.stop()
self.song_info.setText("已停止")
self.statusBar().showMessage("已停止")
def play_next(self):
"""播放下一首"""
if len(self.playlist) == 0:
return
self.current_index = (self.current_index + 1) % len(self.playlist)
self.playlist_widget.setCurrentRow(self.current_index)
self.play_current()
def play_previous(self):
"""播放上一首"""
if len(self.playlist) == 0:
return
self.current_index = (self.current_index - 1) % len(self.playlist)
self.playlist_widget.setCurrentRow(self.current_index)
self.play_current()
def set_volume(self, value):
"""设置音量"""
self.player.setVolume(value)
self.statusBar().showMessage(f"音量: {value
}%")
def update_duration(self, duration):
"""更新总时长显示"""
self.progress_bar.setRange(0, duration)
# 转换为分:秒格式
total_seconds = duration // 1000
minutes = total_seconds // 60
seconds = total_seconds % 60
self.total_time_label.setText(f"{minutes:02d
}:{seconds:02d
}")
def update_position(self, position):
"""更新当前播放位置"""
# 防止进度条更新时触发sliderMoved事件
self.progress_bar.blockSignals(True)
self.progress_bar.setValue(position)
self.progress_bar.blockSignals(False)
# 更新当前时间标签
current_seconds = position // 1000
minutes = current_seconds // 60
seconds = current_seconds % 60
self.time_label.setText(f"{minutes:02d
}:{seconds:02d
}")
# 如果播放结束,自动播放下一首
if position >= self.player.duration() and self.player.duration() >
0:
self.play_next()
def set_position(self, position):
"""设置播放位置"""
self.player.setPosition(position)
def update_state(self, state):
"""更新播放状态"""
if state == QMediaPlayer.PlayingState:
self.play_button.setIcon(self.style().standardIcon(QStyle.SP_MediaPause))
else:
self.play_button.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay))
def clear_playlist(self):
"""清空播放列表"""
self.stop()
self.playlist.clear()
self.playlist_widget.clear()
self.current_index = -1
self.song_info.setText("播放列表已清空")
self.statusBar().showMessage("播放列表已清空")
def show_context_menu(self, position):
"""显示右键菜单"""
if not self.playlist_widget.selectedItems():
return
menu = QMenu()
remove_action = QAction("移除选中项", self)
remove_action.triggered.connect(self.remove_selected)
play_action = QAction("播放选中项", self)
play_action.triggered.connect(self.play_selected)
menu.addAction(play_action)
menu.addAction(remove_action)
menu.exec_(self.playlist_widget.mapToGlobal(position))
def remove_selected(self):
"""移除选中的项目"""
selected_items = self.playlist_widget.selectedItems()
if not selected_items:
return
# 获取选中项的索引
indexes = [self.playlist_widget.row(item) for item in selected_items]
# 按降序排序,从后往前删除
indexes.sort(reverse=True)
for index in indexes:
# 如果删除的是当前播放的歌曲
if index == self.current_index:
self.stop()
# 如果不是最后一首歌,播放下一首
if index <
len(self.playlist) - 1:
self.current_index = index
else:
self.current_index = 0 if len(self.playlist) >
1 else -1
# 从列表中删除
del self.playlist[index]
# 从控件中删除
item = self.playlist_widget.takeItem(index)
del item
# 更新当前索引(如果删除了前面的项目)
if self.current_index >
0 and self.current_index >= len(self.playlist):
self.current_index = len(self.playlist) - 1
self.statusBar().showMessage(f"已移除 {
len(selected_items)
} 首音乐")
if __name__ == "__main__":
# 确保中文显示正常
font = QFont("SimHei")
app = QApplication(sys.argv)
app.setFont(font)
player = MusicPlayer()
sys.exit(app.exec_())

示例演示

在这里插入图片描述

优化建议

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/906787.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

威联通NAS如何导入本地docker镜像

威联通NAS如何导入本地docker镜像 【前言】:有一段时间没有维护NAS docker了,笔记本电脑坏了更换后重新配置开发环境,也就没去折腾这些,有些命令忘记了,重新整理一下,输出驱动输入,还可能帮助到。欢迎大家一起友好讨论, 祝好: 【摘要】:本文主要围绕威联通NAS如何导入…

基本数据类型题目

1、列表的操作可变的数据类型,增删改查等操作都可以进行1、案例1fruits=[苹果, 香蕉, 橙子, 葡萄, 西瓜]向列表中添加两种新水果:菠萝和芒果从列表中删除香蕉元素将列表中的橙子改为柑橘检查葡萄是否在列表中,并返回其索引fruits = [apple, banana, apple, orange, banana, …

2025.9.17

卷2选择 位运算 位运算的优先级很低,没有括号时运算顺序靠后。

mysql库缺失

mysql库缺失libncurses.so.5和libtinfo.so.5 报错信息: mysql: error while loading shared libraries: libncurses.so.5: cannot open shared object file: No such file or directory;mysql: error while loading shared libraries: libtinfo.so.5: cannot open shared obj…

flask简单路由(视图函数)

简单路由(视图函数) 简单返回html @app.route(/tmp) def tmp_page(): # 函数名为tmp_page,对应url_for(tmp_page)return render_template(tmp.html)@app.route(/Custom_routing) def Custom_routing_page(): return render_template(/Custom_routing/index.html) # Custom…

【学习笔记】拉格朗日插值

EZ、什么是拉格朗日插值? 众所周知,\(n+1\) 个点可以唯一确定一个 \(n\) 次多项式。 拉格朗日插值法要解决的就是给定 \(n+1\) 个点确定一个多项式 \(f(x)\),求出在自变量 \(x=k\) 时多项式的取值。 拉格朗日插值法的思想和 CRT 非常像——把每一个维度独立拆开来。 考虑对一…

一种基于动作指令交互的动态活体检测技术,提升人脸识别安全性

在当今数字化时代,人脸识别技术已广泛应用于金融支付、门禁考勤、手机解锁等关键场景,其便捷性深受用户青睐。然而,随之而来的安全风险也日益凸显:高精度的照片、屏幕翻拍、甚至3D面具等攻击手段层出不穷,令传统的静态人脸识别技术面临严峻挑战。为了构筑更加坚固的安全防…

[系统] Windows 已有office版本和visio不兼容的解决方案

最近IC相关论文想用VISIO来画图,但是遇到了如下痛点:VISIO软件的安装必须与现有Office一致,例如现有word等均为32bit,VISIO也必须使用32bit的安装包; 同为32/64bit安装时报错“Office 16 Click-to-Run Extensibility Component”,这种情况需要输入regedit,打开注册表,依…

CF 2127F Hamed and AghaBalaSar

通过枚举破除限制,贡献拆分,等价思考,容斥怎么拆贡献又把自己拆乱了,我也是完蛋了。 首先考虑这个 \(f(a)\) 是什么。 发现跳(仅限第一个操作)的时候形如 \(x\to nxt(x)\to nxt(nxt(x))\to \cdots\),贡献就为 \((a_{nxt(x)} - a_x) + (a_{nxt(nxt(x))} - a_{nxt(x)})\)。…

AT_agc055_b [AGC055B] ABC Supremacy

一眼为学了 trick 不会用。 这种题考虑钦定代表元两边往中间靠。 你思考一下发现任意一个 \(ABC, BCA, CAB\) 可以在序列里任意乱跑,于是你可以把起始状态和最终状态的这种东西全部提前,我们只需要看剩下的部分是否一样就好。 属于是脑电波题。

“Sequential Thinking MCP Server 和codex等AI工具本身任务拆解功能对比

从你提供的文件内容来看,这个工具是一个名为“Sequential Thinking MCP Server”的工具,它用于通过结构化的思考过程解决问题。以下是对它与Codex或Claude Code CLI本身任务拆解功能的不同之处的分析: 功能定位Sequential Thinking MCP Server:专注于通过动态和反思性的思考…

基于错误xsleak 悬空标记 运用css利用帧计数 -- Pure leak ASIS CTF 2025

基于错误xsleak 悬空标记 运用css利用帧计数 -- Pure leak ASIS CTF 2025pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courie…

网易伏羲:当算法遇见社交,解码游戏世界的连接密码

日前,2025游戏安全与创新大会圆满召开,大会以“AI融合创新”与“全链路安全防护”为双主线,聚焦游戏策划与运营核心问题,深度解析AI与游戏结合的提效、玩法及体验升级路径;同时直面开发、运营、发行、支付、出海等阶段的关键挑战,涵盖作弊、合规、欺诈等议题,助力从业者…

在 CentOS 7 上安装Nginx和配置http代理

1. 安装 EPEL 仓库 NGINX 不在 CentOS 的默认仓库中,因此首先需要安装 EPEL(Extra Packages for Enterprise Linux) 仓库。sudo yum install epel-release2. 安装 NGINX 通过 yum 包管理工具安装 NGINX。sudo yum install nginx3. 启动 NGINX 服务sudo systemctl start ngin…

题解:P2624 [HNOI2008] 明明的烦恼

题解:P2624 [HNOI2008] 明明的烦恼 不会 $prufer$ 序列的请右转树的计数,先将 $prufer$ 序列掌握再做这题。 设有 $n$ 个节点,$deg_i$ 为每个节点的度数,由上题可得,此时可能的无根树的方案为: $$\frac{(n-2)!}{\prod_{i=1}^{n}(deg_i-1)!}$$ 但是这题只给了我们部分节点…

在AI技术快速实现创想的时代,挖掘新需求成为核心竞争力——某知名DevOps学习平台需求洞察

该篇文章无摘要a.内容描述 该项目是一个结构化的DevOps学习资源,旨在帮助用户建立DevOps基础知识的系统化理解。核心功能定位是通过90天的学习计划,系统性地覆盖DevOps原则、流程和工具链的关键领域,包括DevOps基础、DevSecOps安全主题以及社区分享内容。 关键应用场景包括:…

Windows Powershell 获取版本version

前言全局说明一、 1.源码 $PSVersionTable.PSVersion2.结果免责声明:本号所涉及内容仅供安全研究与教学使用,如出现其他风险,后果自负。参考、来源: https://www.cnblogs.com/music-liang/p/18813922 作者:悟透原文链接:https://www.cnblogs.com/wutou/p/19097392来源:博…

XXL-JOB (1)

XXL-JOB (1)# 1 测试

公益站Agent Router注册送200刀额度竟然是真的

昨天看到说Agent Router邀请注册送100美刀,我就点了别人的链接,使用github注册了一个,确实得到了额度。但是我去聊天那里,发现会有错误,以为这个不好用:但是今天测试了一下在Claude Code确实能用,而且速度也还可以!!感兴趣的朋友也快来试试吧!! 邀请链接:https://a…

数据集中valid的作用

简单来说,valid(或 val)文件夹的存在是为了在模型训练过程中,定期、独立地评估模型的性能,以便进行模型调优、防止过拟合和选择最佳模型。它是机器学习工作流中至关重要的一环。 一般的数据集结构:1. Train(训练集)目的:这是模型“学习”所用的主要数据。模型通过反复…