基于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
相关产品推荐
相关产品推荐

