python实现的音乐播放器

音乐播放器,原来写过一个简陋的例子,可见
https://blog.csdn.net/cnds123/article/details/137874107

那个不能拖动播放进度条上的滑块到新的位置播放。下面介绍的可以拖动播放进度条上的滑块到新的位置播放

简单实用的音乐播放器

这个简单实用的音乐播放器,运行界面:

需要安装,PyQt6这个第三方库。 

源码如下:

import sys
import os
from PyQt6.QtWidgets import (QApplication, QWidget, QLabel, QPushButton, QSlider, QHBoxLayout, QVBoxLayout, QFileDialog, QListWidget, QListWidgetItem
)
from PyQt6.QtCore import Qt, QTimer, QUrl
from PyQt6.QtGui import QPixmap, QPainter, QTransform, QPainterPath
from PyQt6.QtMultimedia import QMediaPlayer, QAudioOutputclass MP3Player(QWidget):def __init__(self):super().__init__()self.song_list = []self.current_index = -1self.init_ui()self.setup_player()def init_ui(self):self.setWindowTitle("音乐播放器 V1.0.2")self.setGeometry(300, 300, 600, 400)# 控件初始化self.playlist = QListWidget()self.playlist.itemDoubleClicked.connect(self.play_selected)self.play_btn = QPushButton("▶")self.prev_btn = QPushButton("⏮")self.next_btn = QPushButton("⏭")self.stop_btn = QPushButton("⏹")self.slider = QSlider(Qt.Orientation.Horizontal)self.time_label = QLabel("00:00 / 00:00")# 按钮样式btn_style = """QPushButton {font-size: 20px;min-width: 40px;min-height: 40px;border-radius: 20px;background: #666;color: white;}QPushButton:hover { background: #09f; }"""for btn in [self.play_btn, self.prev_btn, self.next_btn, self.stop_btn]:btn.setStyleSheet(btn_style)# 布局control_layout = QHBoxLayout()control_layout.addWidget(self.prev_btn)control_layout.addWidget(self.play_btn)control_layout.addWidget(self.next_btn)control_layout.addWidget(self.stop_btn)main_layout = QVBoxLayout()main_layout.addWidget(self.playlist)main_layout.addWidget(self.slider)main_layout.addWidget(self.time_label)main_layout.addLayout(control_layout)# 功能按钮buttons_layout = QHBoxLayout()# 添加文件按钮add_file_btn = QPushButton("添加文件")add_file_btn.clicked.connect(self.add_files)buttons_layout.addWidget(add_file_btn)# 删除文件按钮delete_file_btn = QPushButton("删除文件")delete_file_btn.clicked.connect(self.delete_file)buttons_layout.addWidget(delete_file_btn)# 帮助按钮help_btn = QPushButton("帮助")help_btn.clicked.connect(self.show_help)buttons_layout.addWidget(help_btn)main_layout.addLayout(buttons_layout)# 添加音量控制self.volume_slider = QSlider(Qt.Orientation.Horizontal)self.volume_slider.setRange(0, 100)self.volume_slider.setValue(70)  # 默认音量70%self.volume_slider.valueChanged.connect(self.change_volume)volume_layout = QHBoxLayout()volume_layout.addWidget(QLabel("音量:"))volume_layout.addWidget(self.volume_slider)main_layout.addLayout(volume_layout)self.setLayout(main_layout)# 连接信号self.play_btn.clicked.connect(self.toggle_play)self.prev_btn.clicked.connect(self.play_prev)self.next_btn.clicked.connect(self.play_next)self.stop_btn.clicked.connect(self.stop)self.slider.sliderMoved.connect(self.seek_position)def delete_file(self):# 获取当前选中的项目current_items = self.playlist.selectedItems()if not current_items:return# 逐一删除所选项目for item in current_items:index = self.playlist.row(item)# 如果删除的是正在播放的歌曲,先停止播放if index == self.current_index:self.player.stop()self.current_index = -1# 从列表和界面中删除项目self.playlist.takeItem(index)self.song_list.pop(index)# 如果正在播放的歌曲在被删除的歌曲之后,需要调整索引if index < self.current_index:self.current_index -= 1# 如果删除后列表为空,重置界面if not self.song_list:self.time_label.setText("00:00 / 00:00")self.slider.setValue(0)self.update_play_button(QMediaPlayer.PlaybackState.StoppedState)def show_help(self):# 创建帮助消息help_text = """<h3>音乐播放器使用帮助</h3><p><b>播放控制:</b></p><ul><li>播放/暂停:点击 ▶/⏸ 按钮</li><li>上一首:点击 ⏮ 按钮</li><li>下一首:点击 ⏭ 按钮</li><li>停止:点击 ⏹ 按钮</li></ul><p><b>播放列表:</b></p><ul><li>添加文件:点击"添加文件"按钮</li><li>删除文件:选择文件后点击"删除文件"按钮</li><li>播放指定歌曲:双击列表中的歌曲</li></ul><p><b>其他控制:</b></p><ul><li>调整进度:拖动进度条</li><li>调整音量:拖动音量滑块</li></ul>"""# 导入需要的组件from PyQt6.QtWidgets import QMessageBox# 显示帮助对话框help_dialog = QMessageBox(self)help_dialog.setWindowTitle("帮助")help_dialog.setTextFormat(Qt.TextFormat.RichText)help_dialog.setText(help_text)help_dialog.setIcon(QMessageBox.Icon.Information)help_dialog.exec()def change_volume(self, value):self.audio_output.setVolume(value / 100.0)def setup_player(self):self.player = QMediaPlayer()self.audio_output = QAudioOutput()self.audio_output.setVolume(0.7)  # 默认音量设置self.player.setAudioOutput(self.audio_output)# 定时器更新进度self.timer = QTimer()self.timer.timeout.connect(self.update_progress)self.timer.start(1000)# 播放状态变化self.player.playbackStateChanged.connect(self.update_play_button)# 添加媒体结束时的信号连接self.player.mediaStatusChanged.connect(self.handle_media_status_change)# 添加媒体时长变化的信号连接self.player.durationChanged.connect(self.duration_changed)self.player.errorOccurred.connect(self.handle_error)##    def handle_media_status_change(self, status):
##        # 使用 QTimer.singleShot 来避免潜在的递归调用或信号冲突
##        if status == QMediaPlayer.MediaStatus.EndOfMedia:
##            QTimer.singleShot(10, self.play_next)def handle_media_status_change(self, status):# 仅当媒体结束且不是暂停状态时处理if status == QMediaPlayer.MediaStatus.EndOfMedia:# 检查是否只有一首歌曲if len(self.song_list) == 1:# 只有一首歌曲时,重置到开始位置而不是尝试播放"下一首"self.player.setPosition(0)self.player.stop()self.update_play_button(QMediaPlayer.PlaybackState.StoppedState)# 重新播放if self.player.position() >= self.player.duration() - 100 and self.player.duration() > 0:self.player.setPosition(0)self.player.play()else:# 多首歌曲时,播放下一首QTimer.singleShot(10, self.play_next)def add_files(self):files, _ = QFileDialog.getOpenFileNames(self, "选择音频文件", "", "音频文件 (*.mp3 *.wav *.flac)")for file in files:if file not in self.song_list:self.song_list.append(file)self.playlist.addItem(os.path.basename(file))def play_selected(self, item):self.current_index = self.playlist.row(item)self.play()def duration_changed(self, duration):self.slider.setRange(0, duration)def handle_error(self, error, error_string):print(f"播放器错误: {error_string}")def play(self):if self.current_index < 0 and self.song_list:self.current_index = 0if 0 <= self.current_index < len(self.song_list):# 高亮当前播放的歌曲self.playlist.setCurrentRow(self.current_index)try:file = self.song_list[self.current_index]self.player.setSource(QUrl.fromLocalFile(file))self.player.play()except Exception as e:print(f"播放错误: {e}")def toggle_play(self):if self.player.isPlaying():self.player.pause()else:if self.player.position() == self.player.duration():self.play()else:self.player.play()def update_play_button(self, state):if state == QMediaPlayer.PlaybackState.PlayingState:self.play_btn.setText("⏸")else:self.play_btn.setText("▶")def update_progress(self):duration = self.player.duration()if duration > 0:  # 确保时长大于0current = self.player.position()self.slider.setValue(int(current))self.time_label.setText(f"{self.format_time(current)} / {self.format_time(duration)}")def seek_position(self, position):self.player.setPosition(position)def play_prev(self):if self.song_list:self.current_index = (self.current_index - 1) % len(self.song_list)self.play()def play_next(self):if not self.song_list:return# 先停止当前播放self.player.stop()# 然后切换到下一首self.current_index = (self.current_index + 1) % len(self.song_list)# 使用短延迟来确保状态已正确更新QTimer.singleShot(50, self.play)def stop(self):self.player.stop()self.slider.setValue(0)self.time_label.setText("00:00 / 00:00")def format_time(self, ms):seconds = ms // 1000minutes = seconds // 60seconds = seconds % 60return f"{minutes:02d}:{seconds:02d}"if __name__ == "__main__":app = QApplication(sys.argv)player = MP3Player()player.show()sys.exit(app.exec())

专业级别的音乐播放器

下面这个音乐播放器,源码来源于网络,适当修改,转载记录于此。

需要安装PyQt6、python-vlc、mutagen 这3个第三方库。

需要安装 VLC 播放器(python-vlc 是 VLC 的 Python 绑定,需依赖系统安装的 VLC),访问 VLC 官网Official download of VLC media player, the best Open Source player - VideoLAN ,下载并安装对应系统的版本。否则 程序运行时提示 vlc.dll not found。


运行效果:

源码如下:

import sys
import os
from PyQt6.QtWidgets import (QApplication, QWidget, QLabel, QPushButton, QSlider, QHBoxLayout, QVBoxLayout, QGridLayout, QFileDialog, QListWidget, QListWidgetItem, QMenu
)
from PyQt6.QtCore import Qt, QTimer, QUrl, QByteArray, pyqtSignal
from PyQt6.QtGui import QPixmap, QPainter, QTransform, QPainterPath, QFont, QColor, QLinearGradient, QBrush, QPen, QCursor, QScreen
from mutagen.mp3 import MP3
from mutagen.id3 import ID3, APIC
from PyQt6.QtMultimedia import QMediaPlayer, QAudioOutput
import vlc
import redef resource_path(relative_path):if hasattr(sys, '_MEIPASS'):return os.path.join(sys._MEIPASS, relative_path)return os.path.join(os.path.abspath("."), relative_path)# 旋转封面控件
class RotatingCover(QLabel):def __init__(self, song_path, default_cover="fm.png"):super().__init__()self.angle = 0self.pixmap = self.load_cover(song_path, default_cover)if self.

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

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

相关文章

[网安工具] 端口信息收集工具 —— 御剑高速 TCP 全端口扫描工具 · 使用手册

&#x1f31f;想了解其它网安工具&#xff1f;看看这个&#xff1a;[网安工具] 网络安全工具管理 —— 工具仓库 管理手册 https://github.com/NepoloHebo/Yujian-high-speed-TCP-full-port-scannerhttps://github.com/NepoloHebo/Yujian-high-speed-TCP-full-port-scanner 0…

数字孪生赋能智慧城市:从概念到落地的深度实践

在城市规模与复杂度持续攀升的当下&#xff0c;传统管理模式已难以满足现代城市精细化治理需求。数字孪生技术凭借构建虚拟城市镜像、实现实时数据交互与智能决策的特性&#xff0c;成为智慧城市建设的核心引擎。本文将通过多个典型案例&#xff0c;深度解析数字孪生技术如何重…

DeFi开发系统软件开发:技术架构与生态重构

DeFi开发系统软件开发&#xff1a;技术架构与生态重构 ——2025年去中心化金融开发的范式革新与实践指南 一、技术架构演进&#xff1a;从单一链到多链混合引擎 现代DeFi系统开发已从单一公链架构转向“跨链互操作混合模式”&#xff0c;结合中心化效率与去中心化安全双重优势…

相同IP和端口的服务器ssh连接时出现异常

起因 把服务器上的一个虚拟机搞坏了&#xff0c;所以删除重新创建了一个&#xff0c;端口号和IP与之前的虚拟机相同。 ssh usernameIP -p port 时报错 WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY! Someone…

验证es启动成功

1. 查看命令行输出信息 在启动 Elasticsearch 时&#xff0c;命令行窗口会输出一系列日志信息。若启动成功&#xff0c;日志里通常会有类似下面的信息&#xff1a; plaintext [2025-05-06T13:20:00,000][INFO ][o.e.n.Node ] [node_name] started其中 [node_na…

CentOS网络之network和NetworkManager深度解析

文章目录 CentOS网络之network和NetworkManager深度解析1. CentOS网络服务发展历史1.1 传统network阶段&#xff08;CentOS 5-6&#xff09;1.2 过渡期&#xff08;CentOS 7&#xff09;1.3 新时代&#xff08;CentOS 8&#xff09; 2. network和NetworkManager的核心区别3. ne…

Unity:父挂 Rigidbody2D、子挂 Collider2D 时触发器不生效的问题分析

目录 ❓问题现象 &#x1f50d; 排查与定位 ⚠️ Unity 触发机制的核心要求 ✅ 为什么把 Collider2D 移到父物体后就能触发&#xff1f; &#x1f4a1; 解决方案 在 Unity 2D 游戏开发中&#xff0c;很多人习惯用父物体挂载 Rigidbody2D&#xff0c;而将不同的身体部位&am…

Google AI版图:解析AI Studio, Gemini, NotebookLM与GCP

1. 2C vs 2B: AI Studio: 主要是面向开发者&#xff0c;提供一个易用的界面来探索和构建基于Google模型的应用。虽然最终的应用可能服务于C端或B端&#xff0c;但AI Studio本身更多是一个开发者的工具平台&#xff0c;可以看作是连接模型能力和各种应用的桥梁。它可以被个人开…

Oracle EBS AP发票被预付款核算创建会计科目时间超长

背景 由于客户职能部门的水电、通信和物业等等费用统一管理或对接部门报销费,在报销费的时候,用户把所有费用分摊到各个末级部门,形成AP发票行有上千行, 问题症状 1、用户过账时,请求创建会计科目一直执行20多个小时未完成,只能手工强行取消请求。 2、取消请求以后,从后…

MySQL中MVCC指什么?

简要回答&#xff1a; MVCC&#xff08;multi version concurrency control&#xff09;即多版本并发控制&#xff0c;为了确保多线程下数据的安全&#xff0c;可以通过undo log和ReadView来实现不同的事务隔离级别。 对于已提交读和可重复读隔离级别的事务来说&#xff0c;M…

赛季7靶场 -- Checker --User flag

本系列仅说明靶场的攻击思路&#xff0c;不会给出任何的详细代码执行步骤&#xff0c;因为个人觉得找到合适的工具以实现攻击思路的能力也非常重要。root要逆向&#xff0c;没做了&#xff0c;但是user flag也有借鉴意义&#xff0c;关于2FA的绕过我们有必要了解 1.首先Nmap扫描…

【RAG技术全景解读】从原理到工业级应用实践

目录 &#x1f31f; 前言&#x1f3d7;️ 技术背景与价值&#x1f6a8; 当前技术痛点&#x1f6e0;️ 解决方案概述&#x1f465; 目标读者说明 &#x1f50d; 一、技术原理剖析&#x1f4d0; 核心概念图解&#x1f4a1; 核心作用讲解⚙️ 关键技术模块说明⚖️ 技术选型对比 &…

【嵌入式开发-RS-485】

嵌入式开发-RS-485 ■ RS-485 连接方式■ RS-485 半双工通讯■ RS-485 的特点■ UART硬流控■ RS-4851. 全双工、半双工接线2. 拓扑结构3. RS-485收发器3.1 发送模式&#xff08;TX&#xff09;3.2 接收模式&#xff08;RX&#xff09; 4. RS-485数据链路5. RS-485常用电路6. C…

[硬件电路-18]:MCU - LPC1765FBD100是恩智浦(NXP)半导体推出的一款基于ARM Cortex-M3内核的高性能32位微控制器

LPC1765FBD100是恩智浦&#xff08;NXP&#xff09;半导体推出的一款基于ARM Cortex-M3内核的高性能32位微控制器&#xff0c;具备高集成度、低功耗、丰富的外设接口和强大的处理能力&#xff0c;适用于工业控制、消费电子、医疗设备、通信系统等嵌入式应用场景。 以下从核心特…

MyBatis(进阶)(xml标签)

本节⽬标 1. 学习MyBatis的动态SQL查询 2. 掌握MyBatis在项⽬中的应⽤, 可以使⽤Spring MVC完成⼀些基础的功能 1. 动态SQL&#xff08;XML&#xff09; 动态 SQL 是Mybatis的强⼤特性之⼀&#xff0c;能够完成不同条件下不同的 sql 拼接 可以参考官⽅⽂档&#xff1a; M…

QT QList容器及行高亮

总结QList是一个泛型/模板链表&#xff0c;可以自己定义数据类型&#xff0c;ExtraSelection是一种“数据类型”

【Python】Python项目中的依赖与配置:requirements.txt、setup.py、pyproject.toml 详解

在昨天的文章【Python】通过Editable Install模式详解&#xff0c;解决Python开发总是import出错的问题 中&#xff0c; 我们提到了Python项目的配置文件requirements.txt、setup.py、pyproject.toml。在昨天的解决方案中&#xff0c; 我们同时维护了这三个文件。 由同学就问&a…

详细聊聊 Synchronized,以及锁的升级过程

在Java中&#xff0c;synchronized关键字是用于实现线程同步的重要机制&#xff0c;它通过内置锁&#xff08;Monitor&#xff09;确保多个线程对共享资源的安全访问。 1. synchronized 的基本使用与实现原理 使用方式 修饰实例方法&#xff1a;锁是当前对象实例。public syn…

vue3的深入组件-组件 v-model

组件 v-model 基本用法​ v-model 可以在组件上使用以实现双向绑定。 从 Vue 3.4 开始&#xff0c;推荐的实现方式是使用 defineModel() 宏&#xff1a; <script setup> const model defineModel()function update() {model.value } </script><template>…

15.thinkphp的上传功能

一&#xff0e;上传功能 1. 如果要实现上传功能&#xff0c;首先需要建立一个上传表单&#xff0c;具体如下&#xff1a; <form action"http://localhost/tp6/public/upload"enctype"multipart/form-data" method"post"><input type&…