首页 > 编程语言 >基于Python中的tkinter和pygame库创建一个简单音乐播放器

基于Python中的tkinter和pygame库创建一个简单音乐播放器

时间:2024-06-20 14:00:59浏览次数:23  
标签:index playlist tkinter song Python self current pygame tk

import os
import time
import tkinter as tk
from tkinter import filedialog, messagebox, ttk
import pygame
import mutagen.mp3  # 用于获取MP3文件时长


class MusicPlayer:
    def __init__(self, root):
        pygame.init()
        self.root = root
        self.root.title("音乐播放器")
        self.root.geometry("650x720")
        icon_path = "music.ico"  # 请替换为你的图标文件路径
        if os.path.exists(icon_path):
            self.root.iconbitmap(icon_path)

        # 初始化pygame.mixer
        pygame.mixer.init()

        # 初始化变量
        self.playlist = []  # 存储音乐文件路径
        self.current_index = -1  # 当前播放的歌曲索引
        self.is_paused = False  # 是否处于暂停状态
        self.is_stopped = True  # 是否停止播放
        self.single_loop = False

        # 创建控件
        self.create_widgets()

    def create_widgets(self):
        # 创建控件
        self.frame_left = tk.Frame(self.root)
        self.frame_left.pack(side=tk.TOP, fill=tk.X, expand=False)

        self.frame_right = tk.Frame(self.root)
        self.frame_right.pack(side=tk.BOTTOM, padx=10, pady=0, fill=tk.BOTH, expand=True)

        # 播放列表
        self.playlist_lb = tk.Listbox(self.frame_right, selectmode=tk.SINGLE)
        self.playlist_lb.pack(side=tk.TOP, fill=tk.BOTH, expand=True)
        self.playlist_lb.bind("<Double-Button-1>", self.play_selected_song)
        self.playlist_lb.bind("<Button-3>", self.show_menu)  # 绑定右键菜单事件

        # 滚动条
        scrollbar = ttk.Scrollbar(self.playlist_lb, orient=tk.VERTICAL, command=self.playlist_lb.yview)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        self.playlist_lb.config(yscrollcommand=scrollbar.set)

        # 右键菜单
        self.menu = tk.Menu(self.playlist_lb, tearoff=0)
        self.menu.add_command(label="删除", command=self.delete_song)

        # 控制按钮
        control_frame = tk.Frame(self.frame_left)
        control_frame.pack(pady=5)

        self.play_btn = tk.Button(control_frame, text="加载音乐", command=self.scan_and_add_music)
        self.play_btn.grid(row=0, column=0, padx=10)

        self.play_btn = tk.Button(control_frame, text="播放", command=self.play_music)
        self.play_btn.grid(row=0, column=1, padx=10)

        self.pause_btn = tk.Button(control_frame, text="暂停", command=self.pause_music)
        self.pause_btn.grid(row=0, column=2, padx=10)

        self.prev_btn = tk.Button(control_frame, text="上一首", command=self.play_prev_song)
        self.prev_btn.grid(row=0, column=3, padx=10)

        self.next_btn = tk.Button(control_frame, text="下一首", command=self.play_next_song)
        self.next_btn.grid(row=0, column=4, padx=10)

        self.volume_down_btn = tk.Button(control_frame, text="音量减", command=self.volume_down)
        self.volume_down_btn.grid(row=0, column=5, padx=10)

        self.volume_up_btn = tk.Button(control_frame, text="音量加", command=self.volume_up)
        self.volume_up_btn.grid(row=0, column=6, padx=10)

        self.loop_mode_btn = tk.Button(control_frame, text="单曲循环", command=self.toggle_loop_mode)
        self.loop_mode_btn.grid(row=0, column=7, padx=10)

        self.playlist_mode_btn = tk.Button(control_frame, text="列表循环", command=self.playlist_mode)
        self.playlist_mode_btn.grid(row=0, column=8, padx=10)

        # 歌曲信息和进度条
        progress_bar_frame = tk.Frame(self.frame_right)
        progress_bar_frame.pack(pady=1)
        self.song_info_label = tk.Label(progress_bar_frame, text="")
        self.song_info_label.grid(row=0, column=0, padx=0)

        self.progress_bar = ttk.Progressbar(progress_bar_frame,orient=tk.HORIZONTAL, length=200, mode='determinate')
        self.progress_bar.grid(row=0, column=1, padx=10)
        self.progress_bar.bind("<Button-1>", self.change_position)  # 绑定进度条拖动事件

    def scan_and_add_music(self):
        # 选择文件夹并扫描音乐文件并添加音乐到播放列表
        folder_selected = filedialog.askdirectory()
        if folder_selected:
            for dirpath, _, files in os.walk(folder_selected):
                for file in files:
                    if file.endswith(('.mp3', '.m4a', '.wma', '.wav', '.acc')):
                        self.playlist.append(os.path.join(dirpath, file))
                        self.playlist_lb.insert(tk.END, os.path.basename(file))

    def play_music(self):
        # 播放选中的歌曲或者继续播放
        if self.is_stopped:
            if self.current_index == -1:
                self.current_index = 0
            self.load_and_play_song()
        elif self.is_paused:
            pygame.mixer.music.unpause()
            self.is_paused = False
            self.update_song_info()

    def pause_music(self):
        # 暂停音乐
        if not self.is_paused:
            pygame.mixer.music.pause()
            self.is_paused = True

    def play_prev_song(self):
        # 播放上一首歌曲
        if self.current_index > 0:
            self.current_index -= 1
            self.load_and_play_song()

    def play_next_song(self):
        # 播放下一首歌曲
        if self.current_index < len(self.playlist) - 1:
            self.current_index += 1
            self.load_and_play_song()
        else:
            self.current_index = 0
            self.load_and_play_song()

    def load_and_play_song(self):
        # 加载并播放当前索引的歌曲
        if self.current_index == -1 or self.current_index >= len(self.playlist):
            if self.playlist:
                self.current_index = 0
                self.load_and_play_song()
            else:
                return
        pygame.mixer.music.load(self.playlist[self.current_index])
        pygame.mixer.music.play()
        pygame.mixer.music.set_endevent(pygame.USEREVENT)
        self.playlist_lb.selection_clear(0, tk.END)
        self.playlist_lb.selection_set(self.current_index)
        self.playlist_lb.activate(self.current_index)
        self.is_stopped = False
        self.is_paused = False
        self.update_song_info()
        self.root.after(100, self.toggle_playlist_mode)

    def play_selected_song(self, event):
        # 双击列表播放选中的歌曲
        index = self.playlist_lb.curselection()
        if index:
            self.current_index = index[0]
            self.load_and_play_song()

    def update_song_info(self):
        # 更新当前播放的歌曲信息和进度条
        if not self.is_stopped:
            song_path = self.playlist[self.current_index]
            song_name = os.path.basename(song_path)
            self.root.title(f"正在播放: {song_name}")

            # 获取歌曲时长(以秒为单位)
            try:
                audio = mutagen.mp3.MP3(song_path)
                song_length = audio.info.length
            except:
                song_length = 0

            self.song_info_label.config(text=f"{song_name}")
            self.progress_bar.config(maximum=song_length, value=0)

            self.update_progress()

    def update_progress(self):
        # 更新进度条
        if not self.is_stopped and not self.is_paused:
            current_pos = pygame.mixer.music.get_pos() / 1000.0
            self.progress_bar.config(value=current_pos)
            self.root.after(1000, self.update_progress)

    def volume_down(self):
        # 减小音量
        current_volume = pygame.mixer.music.get_volume()
        if current_volume > 0:
            pygame.mixer.music.set_volume(current_volume - 0.1)

    def volume_up(self):
        # 增加音量
        current_volume = pygame.mixer.music.get_volume()
        if current_volume < 1:
            pygame.mixer.music.set_volume(current_volume + 0.1)

    def toggle_loop_mode(self):
        self.single_loop = not self.single_loop
        mode = "单曲循环" if self.single_loop else "列表循环"
        messagebox.showinfo("播放模式", f"{mode} 已启用")

    def playlist_mode(self):
        self.single_loop = False
        messagebox.showinfo("播放模式", "列表循环已启用")

    def toggle_playlist_mode(self):
        # 切换列表循环模式
        for event in pygame.event.get():
            if event.type == pygame.USEREVENT:
                # if not pygame.mixer.music.get_busy():
                if self.single_loop:
                    self.load_and_play_song()  # 单曲循环
                else:
                    self.play_next_song()  # 列表循环

        self.root.after(100, self.toggle_playlist_mode)

    def show_menu(self, event):
        # 显示右键菜单
        self.menu.post(event.x_root, event.y_root)

    def delete_song(self):
        # 删除选中的歌曲
        selection = self.playlist_lb.curselection()
        if selection:
            index = int(selection[0])
            del self.playlist[index]
            self.playlist_lb.delete(index)

            # 如果删除的是当前正在播放的歌曲,停止播放并清空信息
            if index == self.current_index:
                pygame.mixer.music.stop()
                self.is_stopped = True
                self.is_paused = False
                self.current_index = -1
                self.song_info_label.config(text="正在播放:")
                self.root.title("音乐播放器")

                # 重新加载当前歌曲信息(如果有下一首)
                if self.playlist:
                    self.load_and_play_song()

    def change_position(self, event):
        # 处理进度条拖动事件
        if pygame.mixer.music.get_busy():
            x = event.x
            length = self.progress_bar.winfo_width()
            song_length = pygame.mixer.music.get_pos() / 1000.0
            new_pos = x / length * song_length
            pygame.mixer.music.rewind()
            pygame.mixer.music.set_pos(new_pos)

    def update_song_info(self):
        # 更新当前播放的歌曲信息和进度条
        if not self.is_stopped:
            song_path = self.playlist[self.current_index]
            song_name = os.path.basename(song_path)
            self.root.title(f"正在播放: {song_name[:-4]}")

            # 获取歌曲时长(以秒为单位)
            try:
                audio = mutagen.mp3.MP3(song_path)
                song_length = audio.info.length
            except:
                song_length = 0

            self.song_info_label.config(
                text=f"正在播放:{song_name} - {self.format_time(0)} / {self.format_time(song_length)}")
            self.progress_bar.config(maximum=song_length, value=0)

            self.update_progress()

    def update_progress(self):
        # 更新进度条和当前播放时间
        if not self.is_stopped and not self.is_paused:
            current_pos = pygame.mixer.music.get_pos() / 1000.0
            self.progress_bar.config(value=current_pos)
            self.song_info_label.config(
                text=f"{os.path.basename(self.playlist[self.current_index])[:-4]} - {self.format_time(current_pos)} / {self.format_time(self.progress_bar['maximum'])}")
            self.root.after(1000, self.update_progress)

    def format_time(self, seconds):
        # 将秒数格式化为 mm:ss 的形式
        return time.strftime('%M:%S', time.gmtime(seconds))


if __name__ == "__main__":
    root = tk.Tk()
    app = MusicPlayer(root)
    root.mainloop()

运行效果

标签:index,playlist,tkinter,song,Python,self,current,pygame,tk
From: https://blog.csdn.net/huangkai42/article/details/139808134

相关文章

  • Python Django 实现教师、学生双端登录管理系统
    文章目录PythonDjango实现教师、学生双端登录管理系统引言Django框架简介环境准备模型设计用户认证视图和模板URL路由前端设计测试和部署获取开源项目参考PythonDjango实现教师、学生双端登录管理系统引言在当今的教育环境中,数字化管理系统已成为必不可少的工......
  • 2.2.1 Python的起源
    1.1Python的起源Python的创始人为GuidovanRossum(后文简称Guido)。1982年,Guido从阿姆斯特丹大学获得数学和计算机硕士学位。尽管Guido算得上是一位数学家,不过他更享受计算机带来的乐趣。用Guido的话说,尽管他拥有数学和计算机双料资质,不过他趋向于做计算机相关的工作,并热衷于做......
  • python中__getattr__和__setattr__
    代码:点击查看代码#!/usr/bin/envpython#!-*-coding:utf-8-*-classA(object):def__setattr__(self,key,value):self.__dict__[key]=valuedef__getattr__(self,name):return"xxx"obj=A()执行操作的代码:代码1:print(......
  • Python中__delattr__()函数详解
    在Python中,__delattr__是一个特殊方法(也称为魔术方法或双下划线方法),它在尝试删除对象的属性时被调用。当你使用del语句来删除一个对象的属性时,Python会自动调用这个方法来执行删除操作。__delattr__方法接受一个参数,即要删除的属性的名称(通常是一个字符串)。你可以在这个方法中自......
  • Appium-Python-Client 4.+版本踩坑实录
    Appium-Python-Client4.+版本TouchAction类被appium.webdriver.extensions.action_helpers.ActionHelpers取代使用方法#点击坐标self.driver.tag(positions,duration=duration)#屏幕元素滑动self.driver.scroll(origin_el=origin_el,destination_el=destination......
  • 如何使用python脚本爬取微信公众号文章
    1、什么是爬虫?在座的各位可能经常听到一个词,叫“爬虫”,这是一种能够悄无声息地将网站数据下载至本地设备的程序。利用爬虫,您无需亲自访问特定网站,逐个点击并手动下载所需数据。相反,爬虫能够全自动地为您完成这项任务,从网站上获取您所需的信息,并将其下载到您的设备上,而整个过程......
  • python队列实例解析
    一队列的概念1创建队列:importqueueq=queue.Queue()#创建Queue队列 2入队和出队foriinrange(3):q.put(i)#在队列中依次插入0、1、2元素foriinrange(3):print(q.get())#依次从队列中取出插入的元素,数据元素输出顺序为2、1、0......
  • 2024 年最新 Python 基于 LangChain 框架基础案例详细教程(更新中)
    LangChain框架搭建安装langchainpipinstalllangchain-ihttps://mirrors.aliyun.com/pypi/simple/安装langchain-openaipipinstalllangchain-openai-ihttps://mirrors.aliyun.com/pypi/simple/ChatOpenAI配置环境变量环境变量OPENAI_API_KEY=OpenAIAP......
  • Python代码解压的中文文件名是乱码,怎么解决?
    大家好,我是Python进阶者。一、前言前几天在Python白银交流群【fashjon】问了一个Python自动化办公的问题,问题如下:importzipfiledefunzip_file(zip_file_path,output_folder_path):withzipfile.ZipFile(zip_file_path,'r')aszip_ref:zip_ref.extractall(......
  • Python基础知识:元组
    元组(Tuple)是Python中一种不可变的序列类型,这意味着一旦创建,元组中的元素就不能被更改。元组常用于存储多个值,当这些值不需要修改时使用,可以提高代码的安全性和性能。以下是关于Python元组的一些基础知识:创建元组元组通过逗号分隔的一系列值构成,并且通常用圆括号包围(虽然在只......