You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Python(Tkinter/Pygame)的MP3播放器如何实现自动切歌与单曲循环?

实现自动切歌与单曲循环的解决方案

嘿,你的MP3播放器基础功能已经跑通了,要实现音乐结束自动切下一首和单曲循环其实很简单,咱们可以借助Pygame的音乐结束事件监听来实现,具体步骤和修改后的代码如下:

核心思路

Pygame提供了pygame.mixer.music.set_endevent()方法,能在音乐播放完毕时触发一个自定义事件。我们只需要在Tkinter的主循环中同时监听这个事件,然后根据播放列表的长度执行不同的逻辑:

  • 如果列表只有1首歌:重新播放当前曲目
  • 如果列表有多首歌:切换到下一首并播放

具体修改步骤

1. 定义自定义音乐结束事件

在变量定义区域添加一行,创建一个唯一的事件标识(避免和系统内置事件冲突):

# 定义音乐结束的自定义事件
MUSIC_END = pygame.USEREVENT + 1

2. 在播放音乐时绑定结束事件

不管是首次播放、切歌还是暂停后恢复,只要开始播放音乐,就设置这个结束事件,确保每次音乐结束都会触发通知。修改start_it、next_musique、previous_musique三个函数:

def start_it(event):
    global pause
    if pause == 1:
        pygame.mixer.music.unpause()
        pause = 0
    else:
        try:
            pygame.mixer.music.load(Liste_musique[index])
            pygame.mixer.music.play()
            # 新增:设置音乐结束时触发自定义事件
            pygame.mixer.music.set_endevent(MUSIC_END)
        except:
            showwarning("Error","PAS DE FICHIER")

def next_musique(event):
    global index
    index += 1
    # 新增:处理循环,超出列表长度时重置为0
    if index >= len(Liste_musique):
        index = 0
    try:
        pygame.mixer.music.load(Liste_musique[index])
        pygame.mixer.music.play()
        pygame.mixer.music.set_endevent(MUSIC_END)
    except:
        showwarning("Error","PAS DE FICHIER")

def previous_musique(event):
    global index
    index -= 1
    # 新增:处理循环,索引小于0时跳转到最后一首
    if index < 0:
        index = len(Liste_musique) - 1
    try:
        pygame.mixer.music.load(Liste_musique[index])
        pygame.mixer.music.play()
        pygame.mixer.music.set_endevent(MUSIC_END)
    except:
        showwarning("Error","PAS DE FICHIER")

3. 替换Tkinter主循环,同时监听Pygame事件

原来的fen1.mainloop()无法同时处理Pygame的事件,所以我们需要改成手动更新Tkinter界面,同时遍历Pygame的事件队列:

# 替换原来的fen1.mainloop()和后面的fen1.destroy()
while True:
    try:
        # 更新Tkinter界面事件
        fen1.update_idletasks()
        fen1.update()
        # 遍历Pygame事件队列
        for event in pygame.event.get():
            if event.type == MUSIC_END:
                # 音乐播放结束,执行切歌逻辑
                if len(Liste_musique) == 1:
                    # 单曲循环:重新加载并播放当前歌曲
                    pygame.mixer.music.load(Liste_musique[index])
                    pygame.mixer.music.play()
                    pygame.mixer.music.set_endevent(MUSIC_END)
                else:
                    # 切换到下一首,处理循环逻辑
                    index += 1
                    if index >= len(Liste_musique):
                        index = 0
                    pygame.mixer.music.load(Liste_musique[index])
                    pygame.mixer.music.play()
                    pygame.mixer.music.set_endevent(MUSIC_END)
    except TclError:
        # 窗口关闭时退出循环
        break
fen1.destroy()

完整修改后的代码

# -*- coding: utf-8 -*-
""" """
from tkinter import *
import pygame
from tkinter.messagebox import *
from tkinter import filedialog

#-------------------------------FONCTIONS---------------------------------
def importer_son(event):
    file = filedialog.askopenfilename()
    pygame.init()
    pygame.mixer.pre_init(44100, -16, 2, 2048)
    pygame.mixer.init()
    Liste_musique.append(file)
    #TEST PRINT
    print(file)
    print (Liste_musique)

def stop_it(event):
    pygame.mixer.music.pause()
    global pause
    pause = 1

def start_it(event):
    global pause
    if pause == 1:
        pygame.mixer.music.unpause()
        pause = 0
    else:
        try:
            pygame.mixer.music.load(Liste_musique[index])
            pygame.mixer.music.play()
            # 设置音乐结束时触发自定义事件
            pygame.mixer.music.set_endevent(MUSIC_END)
        except:
            showwarning("Error","PAS DE FICHIER")

def close():
    pygame.mixer.music.stop()
    fen1.destroy()

def next_musique(event):
    global index
    index += 1
    # 处理循环,超出列表长度时重置为0
    if index >= len(Liste_musique):
        index = 0
    try:
        pygame.mixer.music.load(Liste_musique[index])
        pygame.mixer.music.play()
        pygame.mixer.music.set_endevent(MUSIC_END)
    except:
        showwarning("Error","PAS DE FICHIER")

def previous_musique(event):
    global index
    index -= 1
    # 处理循环,索引小于0时跳转到最后一首
    if index < 0:
        index = len(Liste_musique) - 1
    try:
        pygame.mixer.music.load(Liste_musique[index])
        pygame.mixer.music.play()
        pygame.mixer.music.set_endevent(MUSIC_END)
    except:
        showwarning("Error","PAS DE FICHIER")

def reint():
    pygame.mixer.music.stop()
    Liste_musique[:] = []

#-------------------------------Variables---------------------------------
Liste_musique = []
global index
index = 0
global pause
pause = 0
# 定义音乐结束的自定义事件
MUSIC_END = pygame.USEREVENT + 1

#-------------------------------MAIN---------------------------------
fen1 = Tk()
fen1.title("Lecteur Audio en python")
menubar = Menu(fen1)
menu1 = Menu(menubar, tearoff=0)
menu1.add_command(label="Réinitialiser", command=reint)
menu1.add_command(label="Quitter", command=close)
menubar.add_cascade(label="Option", menu=menu1)
fen1.config(menu=menubar)

can1 = Canvas(fen1, height=500, width=500)
can1.pack(side=LEFT, padx=5, pady=5)

fond_decran = PhotoImage(file="Background.gif")
can1.create_image(0,0, anchor = "nw", image = fond_decran)
can1.image = fond_decran

image_play = PhotoImage(file="Play.gif")
im_play = can1.create_image(50,400, anchor = "nw", image = image_play)
can1.image = image_play

image_stop = PhotoImage(file="stop.gif")
im_stop = can1.create_image(130,400, anchor = "nw", image = image_stop)
can1.image = image_stop

image_prev = PhotoImage(file="prev.gif")
im_prev = can1.create_image(210,400, anchor = "nw", image = image_prev)
can1.image = image_prev

image_next = PhotoImage(file="next.gif")
im_next = can1.create_image(290,400, anchor = "nw", image = image_next)
can1.image = image_next

image_importer = PhotoImage(file="importer.gif")
im_importer = can1.create_image(370,400, anchor = "nw", image = image_importer)
can1.image = image_importer

can1.tag_bind(im_play, '<Button-1>', start_it)
can1.tag_bind(im_stop, '<Button-1>', stop_it)
can1.tag_bind(im_next, '<Button-1>', next_musique)
can1.tag_bind(im_prev, '<Button-1>', previous_musique)
can1.tag_bind(im_importer, '<Button-1>', importer_son)

# 替换原主循环,同时处理Tkinter和Pygame事件
while True:
    try:
        fen1.update_idletasks()
        fen1.update()
        for event in pygame.event.get():
            if event.type == MUSIC_END:
                if len(Liste_musique) == 1:
                    # 单曲循环
                    pygame.mixer.music.load(Liste_musique[index])
                    pygame.mixer.music.play()
                    pygame.mixer.music.set_endevent(MUSIC_END)
                else:
                    # 切换下一首并循环
                    index += 1
                    if index >= len(Liste_musique):
                        index = 0
                    pygame.mixer.music.load(Liste_musique[index])
                    pygame.mixer.music.play()
                    pygame.mixer.music.set_endevent(MUSIC_END)
    except TclError:
        break

fen1.destroy()

这样修改后,你的播放器就能实现:

  • 单首歌曲时自动循环播放
  • 多首歌曲时播放完当前曲目自动切到下一首,最后一首播完回到第一首循环

内容的提问来源于stack exchange,提问作者LeGensBon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 07:30:34