Python用Pyqt5制作音乐播放器

news2024/11/14 2:54:15

具体效果如下

需要实现的功能主要的几个有:

1、搜索结果更新至当前音乐的列表,这样播放下一首是搜素结果的下一首

2、自动播放

3、滚动音乐文本

4、音乐进度条

5、根据实际情况生成音乐列表。我这里的是下面的情况,音乐文件的格式是 

歌名_歌手.mp3

所以根据需求修改 find_mp3_files 方法,我这里返回的是

[ {"path":音乐文件路径,

"music":歌名,

"singer":歌手},

{"path":音乐文件路径,

"music":歌名,

"singer":歌手},

...]

 全部代码在此,试试吧

import fnmatch
import os
import random
import sys
import logging

from PyQt5.QtGui import QPalette, QBrush, QPixmap, QLinearGradient, QColor
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QSlider, QLabel, \
    QTableWidget, QLineEdit, QTableWidgetItem
from PyQt5.QtMultimedia import QMediaPlayer, QMediaContent
from PyQt5.QtCore import QUrl, Qt, QTimer

logging.basicConfig(filename="music.log", filemode="w", format="%(asctime)s %(name)s:%(levelname)s:%(message)s",
                    datefmt="%Y-%m-%d %H:%M:%S", level=logging.INFO)
logger = logging.getLogger("music")
KZT = logging.StreamHandler()
KZT.setLevel(logging.INFO)
logger.addHandler(KZT)


class MusicPlayer(QMainWindow):
    def __init__(self):
        super().__init__()
        self.music_path = "F://wyy"  # 音乐文件夹,可以有子文件夹
        self.start_x = 10
        self.x_step = 100
        self.start_y = 10
        self.y_step = 40
        self.window_w, self.window_h = 500, 800
        self.music_list = self.find_mp3_files()
        self.show_list = self.music_list

        self.duration = 1
        self.position = 0
        self.text = "歌曲未加载"

        self.current_index = -1
        self.current_music = self.show_list[self.current_index]['music']
        self.current_singer = self.show_list[self.current_index]['singer']
        self.current_path = self.show_list[self.current_index]['path']

        self.status = True
        self.update_label = True
        self.init_ui()
        logger.info("配置加载完成")

    def init_ui(self):
        self.setWindowTitle("音乐播放器")
        self.resize(self.window_w, self.window_h)
        self.setMinimumSize(self.window_w, self.window_h)
        self.setMaximumSize(self.window_w, self.window_h)
        palette = QPalette()  # 主界面背景
        palette.setBrush(QPalette.Background, QBrush(QPixmap("imgs/21.jpg")))
        self.setPalette(palette)

        self.player = QMediaPlayer()

        self.table_widget = QTableWidget(0, 2, parent=self)  # 检测结果列表
        self.table_widget.setHorizontalHeaderLabels(['歌手', '歌曲'])  # 设置列表头
        self.table_widget.verticalHeader().hide()  # 隐藏行索引
        self.table_widget.move(10, 150)
        self.table_widget.resize(500, 600)
        self.table_widget.cellClicked.connect(self.line_clicked)
        self.table_widget.cellDoubleClicked.connect(self.play_cell)
        self.table_widget.setColumnWidth(0, 200)
        self.table_widget.setColumnWidth(1, 270)
        self.table_widget.setEditTriggers(QTableWidget.NoEditTriggers)
        self.table_widget.setStyleSheet("QTableWidget {background-image: url('imgs/11.jpg');}")
        self.load_music()

        self.play_button = QPushButton("播放", self)
        self.play_button.clicked.connect(self.play_current_music)
        self.play_button.move(self.start_x, self.start_y)
        self.change_button = QPushButton("暂停", self)
        self.change_button.clicked.connect(self.change_music_status)
        self.change_button.move(self.start_x + self.x_step, self.start_y)
        self.play_pre_button = QPushButton("上一首", self)
        self.play_pre_button.clicked.connect(self.play_pre)
        self.play_pre_button.move(self.start_x, self.start_y + self.y_step)
        self.play_next_button = QPushButton("下一首", self)
        self.play_next_button.clicked.connect(self.play_next)
        self.play_next_button.move(self.start_x + self.x_step, self.start_y + self.y_step)

        self.search_text = QLineEdit(self)
        self.search_text.resize(200, 30)
        self.search_text.move(self.start_x + 3 * self.x_step + 10, self.start_y)
        self.search_text.returnPressed.connect(self.search_music)

        self.search_btn = QPushButton("搜索", self)
        self.search_btn.move(self.start_x + 2 * self.x_step + 10, self.start_y)
        self.search_btn.clicked.connect(self.search_music)

        self.music_label = QLabel(self.text, self)
        self.music_label.resize(self.window_w - self.start_x, 30)
        self.music_label.move(self.start_x, self.start_y + 2 * self.y_step)
        self.music_label.setAlignment(Qt.AlignCenter)

        self.volume_slider = QSlider(Qt.Horizontal, self)
        self.volume_slider.setRange(0, 100)
        self.volume_slider.setValue(50)
        self.volume_slider.setTickPosition(QSlider.TicksBelow)
        self.volume_slider.setTickInterval(10)
        self.volume_slider.resize(200, 30)
        self.volume_slider.sliderReleased.connect(self.change_volume)
        # self.volume_slider.sliderPressed.connect(self.volume_pressed)
        self.volume_slider.move(self.start_x + 2 * self.x_step + 50, self.start_y + self.y_step + 10)

        self.music_pro_bar = QLabel(self)
        self.music_pro_width = self.window_w - self.start_x
        self.music_pro_bar.resize(self.music_pro_width, 10)
        self.music_pro_bar.move(self.start_x, self.start_y + 2 * self.y_step + 40)
        # self.music_pro_bar.setStyleSheet("background:'#123456'")
        gradient = QLinearGradient(0, 0, self.music_pro_width, 0)
        gradient.setColorAt(0, QColor(0, 0, 0))
        gradient.setColorAt(1, QColor(255, 255, 255))
        music_pro_bar_palette = QPalette()
        music_pro_bar_palette.setBrush(QPalette.Background, gradient)
        self.music_pro_bar.setAutoFillBackground(True)
        self.music_pro_bar.setPalette(music_pro_bar_palette)

        self.timer = QTimer()
        self.timer.timeout.connect(self.scroll_text)
        self.timer.start(500)

        self.player.stateChanged.connect(self.handle_player_status_changed)
        self.player.mediaStatusChanged.connect(self.handle_media_status_changed)
        self.player.durationChanged.connect(self.update_duration)
        self.player.positionChanged.connect(self.update_position)

    def play_music(self):
        self.player.setMedia(QMediaContent(QUrl.fromLocalFile(self.current_path)))
        self.player.play()
        self.text = f"当前播放:{self.current_music} —— {self.current_singer} "
        self.music_label.setText(self.text)
        logger.info("正在播放:%s - %s", self.current_music, self.current_singer)

    def play_current_music(self):
        if self.current_index == -1:
            self.current_index = 0
            self.update_current_music()
        logger.info(self.current_index)
        self.play_music()

    def change_music_status(self):
        if self.status:
            self.player.pause()
            self.status = False
            self.change_button.setText('继续')
            logger.info("已暂停当前音乐:%s", self.current_music)
        else:
            self.status = True
            self.player.play()
            self.change_button.setText('暂停')
            logger.info("已继续当前音乐:%s", self.current_music)

    def handle_media_status_changed(self, status):
        if status == QMediaPlayer.EndOfMedia:
            logger.info("播放完毕: %s - %s", self.current_music, self.current_singer)
            if not self.show_list:
                logger.info("当前列表没有歌曲")
                self.reload_music()
            self.play_next()

    def handle_player_status_changed(self, state):
        if state == QMediaPlayer.PlayingState:
            self.update_label = True
        elif state == QMediaPlayer.StoppedState:
            self.update_label = False

    def search_music(self):
        text = self.search_text.text()
        if text:
            self.show_list = []
            for i in self.music_list:
                for j in i.values():
                    if text in j:
                        self.show_list.append(i)
                        break
            self.load_music()
            logger.info("已查询'%s'相关内容,总计 %d 条", text, len(self.show_list))
        else:
            logger.info('未查询到相关音乐')
            self.reload_music()

    def reload_music(self):
        logger.info('重新加载列表')
        self.music_list = self.find_mp3_files()  # 从本地重新加载
        self.show_list = self.music_list
        self.load_music()

    def clear_table(self):
        self.table_widget.clearContents()
        self.table_widget.setHorizontalHeaderLabels(['歌手', '歌曲'])  # 设置列表头
        self.table_widget.setRowCount(0)
        logger.info("已更新列表")

    def line_clicked(self, row, column):
        self.current_index = row
        self.update_current_music()
        logger.info("点击了:%s - %s", self.current_music, self.current_singer)

    def play_cell(self, row, column):
        logger.info("双击播放:%s - %s", self.current_music, self.current_singer)
        self.current_index = row
        self.update_current_music()
        self.play_music()

    def load_music(self):
        self.clear_table()
        n = 0
        for i in self.show_list:
            self.table_widget.setRowCount(n + 1)
            self.table_widget.setItem(n, 0, QTableWidgetItem(i['singer']))
            self.table_widget.setItem(n, 1, QTableWidgetItem(i['music']))
            n += 1
        logger.info('加载音乐列表完成')

    def update_current_music(self):
        self.current_path = self.show_list[self.current_index]['path']
        self.current_singer = self.show_list[self.current_index]['singer']
        self.current_music = self.show_list[self.current_index]['music']
        # logger.info("更新当前音乐为:%s - %s", self.current_music, self.current_singer)

    def play_pre(self):
        logger.info("播放上一首")
        if not self.show_list:
            logger.info("当前列表没有音乐")
            return
        find_path = False
        for i, j in enumerate(self.show_list):
            if self.current_path == j['path']:
                if i != 0:
                    self.current_index = i - 1
                else:
                    self.current_index = -1
                find_path = True
                break
        if not find_path:
            logger.info("当前播放歌曲不在当前列表")
            self.current_index = -1
        self.update_current_music()
        self.change_button.setText('暂停')
        logger.info("切换音乐:%s - %s", self.current_music, self.current_singer)
        self.play_music()

    def play_next(self):
        logger.info("播放下一首")
        if not self.show_list:
            logger.info("当前列表没有音乐")
            return
        find_path = False
        for i, j in enumerate(self.show_list):
            if self.current_path == j['path']:
                if i != len(self.show_list) - 1:
                    self.current_index = i + 1
                else:
                    self.current_index = 0
                find_path = True
                break
        if not find_path:
            logger.info("当前播放歌曲不在当前列表")
            self.current_index = 0
        self.update_current_music()
        self.change_button.setText('暂停')
        logger.info("切换音乐为:%s - %s", self.current_music, self.current_singer)
        self.play_music()

    def change_volume(self):
        value = self.volume_slider.value()
        self.player.setVolume(value)
        logger.info("滑动设置音量为%s", value)

    # def volume_pressed(self):
    #     value = self.volume_slider.value()
    #     self.player.setVolume(value)
    #     logger.info("点击设置音量为%s", value)

    def set_position(self):
        self.music_pro_bar.resize(int(self.position / self.duration * self.music_pro_width), 10)
        # c = int(self.position / self.duration * 255)
        # h = self.rgb2hex(c, c, c)
        # self.music_pro_bar.setStyleSheet(f"background:'{h}'")

    def update_duration(self, duration):
        if not self.update_label:
            return
        self.duration = duration

    def update_position(self, position):
        if not self.update_label:
            return
        self.position = position
        self.set_position()

    def scroll_text(self):
        current_text = self.music_label.text()
        scroll_text = current_text[1:] + current_text[0]
        self.music_label.setText(scroll_text)
        self.music_label.setStyleSheet(f"color:{self.rgb2hex(*self.random_color())}")

    def rgb2hex(self, r, g, b):
        return "#{:02x}{:02x}{:02x}".format(r, g, b)

    def find_mp3_files(self):  # 生成音乐文件列表,根据需求自定义
        mp3_files = []
        for root, dirs, files in os.walk(self.music_path):
            for file in files:
                if fnmatch.fnmatch(file, '*.mp3'):
                    if '_' in file:
                        r = {'music': file.split('_')[0],
                             'singer': file.split('_')[1].split('.')[0],
                             'path': os.path.join(root, file)}
                        mp3_files.append(r)
        return mp3_files

    def random_color(self):  # 随机颜色
        r, g, b = random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)
        return r, g, b


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

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

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

相关文章

图——图的遍历(DFS与BFS算法详解)

前面的文章中我们学习了图的基本概念和存储结构,大家可以通过下面的链接学习: 图的定义和基本术语 图的类型定义和存储结构 这篇文章就来学习一下图的重要章节——图的遍历。 目录 一,图的遍历定义: 二,深度优先…

【MySQL】:学习数据库必须要知道的背景知识

客户端—服务器 客户端是一个“客户端—服务器”结构的程序 C(client)—S(server) 客户端和服务器是两个独立的程序,这两个程序之间通过“网络”进行通信(相当于是两种角色) 客户端 主动发起网…

CV12_ONNX转RKNN模型(谛听盒子)

暂时简单整理一下: 1.在边缘设备上配置相关环境。 2.配置完成后,获取模型中间的输入输出结果,保存为npy格式。 3.将onnx格式的模型,以及中间输入输出文件传送到边缘设备上。 4.编写一个python文件用于转换模型格式&#xff0c…

对某根域的一次渗透测试

前言 两个月之前的一个渗透测试项目是基于某网站根域进行渗透测试,发现该项目其实挺好搞的,就纯粹的没有任何防御措施与安全意识所以该项目完成的挺快,但是并没有完成的很好,因为有好几处文件上传没有绕过(虽然从一个…

linux|多线程(一)

主要介绍了为什么要有线程 和线程的调用 和简单的对线程进行封装。 背景知识 a.重谈地址空间 我们知道物理内存的最小单元大小是4kB 物理内存是4G那么这样的单元友1M个 操作系统先描述再组织struct page[1M] 对于32位数据字长的机器,页表有2^32条也就是4G条&#…

springboot的JWT令牌

生成JWT令牌 依赖 <!--jwt令牌--> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency> <dependency> <groupId>javax.xml.bind<…

怎样在 PostgreSQL 中优化对大数据量的分页查询?

&#x1f345;关注博主&#x1f397;️ 带你畅游技术世界&#xff0c;不错过每一次成长机会&#xff01;&#x1f4da;领书&#xff1a;PostgreSQL 入门到精通.pdf 文章目录 《PostgreSQL 中大数据量分页查询的优化之道》一、理解分页查询的基本原理二、优化分页查询的策略&…

2024年06月CCF-GESP编程能力等级认证C++编程七级真题解析

本文收录于专栏《C等级认证CCF-GESP真题解析》&#xff0c;专栏总目录&#xff1a;点这里。订阅后可阅读专栏内所有文章。 一、单选题&#xff08;每题 2 分&#xff0c;共 30 分&#xff09; 第 1 题 下列C代码的输出结果是&#xff08; &#xff09;。 #include <iostr…

SwiftUI 6.0(Xcode 16)新 PreviewModifier 协议让预览调试如虎添翼

概览 用 SwiftUI 框架开发过应用的小伙伴们都知道&#xff0c;SwiftUI 中的视图由各种属性和绑定“扑朔迷离”的缠绕在一起&#xff0c;自成体系。 想要在 Xcode 预览中泰然处之的调试 SwiftUI 视图有时并不是件容易的事。其中&#xff0c;最让人秃头码农们头疼的恐怕就要数如…

Spring Cloud Gateway 自定义断言以及过滤器

1.Spring Cloud gateway介绍 Spring Cloud Gateway 是一个基于 Spring Framework 和 Spring Boot 的 API 网关服务&#xff0c;它利用了 Spring WebFlux 来提供响应式非阻塞式Web请求处理能力。它的核心功能是路由&#xff0c;即根据请求的特定规则将请求转发到后端服务&#…

DP(1500-1700)(刷题)

1.状态机模型&#xff1a;https://codeforces.com/contest/1984/problem/C2 记一下max与min状态转移即可&#xff0c;下面是AC代码&#xff1a; #include<bits/stdc.h> using namespace std; typedef long long ll; ll a[200010],t,n; ll dp[200010][2];//dp[i][0]表示…

啊?现在不懂 AI ,相当于十年前不懂电脑?

最近有关萝卜快跑的新闻铺天盖地&#xff0c;一篇篇都在唱衰&#xff0c;好像千万滴滴师傅立马就要失业了一样。 还没等多久&#xff0c;在朋友圈看到这样一句话&#xff0c;“现在不懂 AI &#xff0c;相当于十年前不懂电脑”。 我想了许久&#xff0c;最终不得不承认这个事实…

深度学习入门——误差反向传播

要正确理解误差反向传播法&#xff0c;我个人认为有两种方法&#xff1a;一种是基于数学式&#xff1b;另一种是基于计算图&#xff08;computational graph&#xff09; 前者是比较常见的方法&#xff0c;机器学习相关的图书中多数都是以数学式为中心展开论述的。因为这种方法…

达梦数据库的系统视图v$sqltext

达梦数据库的系统视图v$sqltext 在达梦数据库&#xff08;DM Database&#xff09;中&#xff0c;V$SQLTEXT 是一个系统视图&#xff0c;用于显示当前正在执行或最近执行的SQL语句的文本信息。这个视图对于监控和分析数据库中的SQL活动非常有用&#xff0c;尤其是在需要调试性…

【python】OpenCV—Coordinates Sorted Clockwise

文章目录 1、需求介绍2、算法实现3、完整代码 1、需求介绍 调用 opencv 库&#xff0c;绘制轮廓的矩形边框&#xff0c;坐标顺序为右下→左下→左上→右上&#xff0c;我们实现一下转化为熟悉的 左上→右上→右下→左下 形式 按照这样的顺序组织边界框坐标是执行透视转换或匹…

13. C++继承 | 详解 | 虚拟继承及底层实现

目录 1.定义 1.1继承的概念 1.2 继承的定义 2. 对象赋值转换 3. 继承中的作用域 a. 隐藏/重定义 (Hiding/Redefinition) b. 重载 (Overloading) c. 重写/覆盖 (Overriding) d. 编译报错 (Compilation Error) 4. 派生类的默认成员函数 构造 拷贝构造 运算符重载 析…

处理uniapp刷新后,点击返回按钮跳转到登录页的问题

在使用uniapp的原生返回的按钮时&#xff0c;如果没有刷新会正常返回到对应的页面&#xff0c;如果刷新后会在当前页反复横跳&#xff0c;或者跳转到登录页。那个时候我第一个想法时&#xff1a;使用浏览器的history.back()方法。因为浏览器刷新后还是可以通过右上角的返回按钮…

Vscode+Pyside6开发之虚拟环境配置以及错误解决

Pyside开发之虚拟环境配置以及错误解决 开发环境一、项目创建以及虚拟环境设置1.创建项目2. 新建py文件,新建虚拟环境3.激活虚拟环境二、项目位置改变pip命令报错1.删除原来的虚拟环境2. 产生包列表文件requirements.txt3.重新创建虚拟环境4.重新安装包文件5.其他错误开发环境…

操作系统 输入输出系统

输入输出系统 I/O系统的功能、模型和接口 功能 隐藏物理设备的细节&#xff1a;仅向上层进程提供少量的、抽象的读/写命令 与设备无关性&#xff1a;用户不仅可以使用抽象的I/O命令&#xff0c;还可使用抽象的逻辑设备名来使用设备 提高处理机和I/O设备的利用率&#xff1a;…

IDEA SpringBoot实现定时任务(保姆级教程,超详细!!!)

目录 1. 前言 2. 创建SpringBoot项目 3. Maven依赖引入 4. 创建定时任务 5. 问题&#xff1a;执行时间延迟和单线程执行 5.1 问题原因 5.2 解决方式 1. 前言 在现代化应用中&#xff0c;定时任务&#xff08;Scheduled Tasks&#xff09;是不可或缺的一部分&#xff…