Python Pygame+Tkinter音乐播放时长显示错误问题求助
问题:Pygame+Tkinter音乐播放器时长显示不准确
我用Python结合Pygame和Tkinter写了一个音乐播放器,但屏幕上显示的播放时长存在误差——比如一首3秒的歌曲,播放结束时仅显示2.074秒,差了近1秒。以下是我的代码:
def check_event(welcome_msg,time_left): global name_songs global dir_songs #time_left['text'] = (pygame.mixer.music.get_length() - pygame.mixer.music.get_pos())*-1 if pygame.mixer.music.get_pos()>0: time_left['text'] = pygame.mixer.music.get_pos()/1000 for event in pygame.event.get(): if event.type == MUSIC_END and len(name_songs) > 0: #print(len(name_songs)) welcome_msg['text'] = name_songs[0] if len(name_songs)!=1:#last song pygame.mixer.music.queue(dir_songs[0]) dir_songs.remove(dir_songs[0]) name_songs.remove(name_songs[0]) #print(pygame.mixer.music.get_pos()/1000) #print(pygame.mixer.music.get_pos()/1000) root.after(1000,lambda:check_event(welcome_msg,time_left)) def play(music_label,time_left): global name_songs global dir_songs directory=r'C:\Users\Boris\Desktop\songs' first_file=0 pygame.mixer.music.set_endevent(MUSIC_END) for filename in os.listdir(directory): f = os.path.join(directory, filename) # checking if it is a file splited_dir=f.split("\\") splited_format=splited_dir[len(splited_dir)-1].split('.') music_name=splited_format[0] name_songs.append(music_name) dir_songs.append(f) #print(not(first_file),os.path.isfile(f)) if os.path.isfile(f) and first_file==0: pygame.mixer.music.load(f) pygame.mixer.music.play(loops=0) music_label.configure(text=music_name) name_songs.remove(name_songs[0]) dir_songs.remove(dir_songs[0]) elif os.path.isfile(f) and first_file==1: #queqe next song pygame.mixer.music.queue(dir_songs[0]) dir_songs.remove(dir_songs[0]) first_file+=1 pygame.mixer.music.set_endevent(MUSIC_END) check_event(music_label,time_left) def pause(): #print(tk._default_root,root) if pygame.mixer.music.get_busy(): pygame.mixer.music.pause() elif tk._default_root: #check if window was destroyed pygame.mixer.music.unpause()
请问如何解决时长显示不准确的问题,让音乐结束时能显示正确的完整时长?
解决方案
核心问题分析
- 递归调用缩进错误:原代码中
root.after仅在MUSIC_END事件触发时才会再次调用check_event,导致播放过程中时长只更新一次,无法实时刷新进度。 - 更新频率过低:1000ms的更新间隔太长,进度显示不连贯,误差累积明显。
- 结束状态未处理:歌曲播放完毕时,
get_pos()会返回-1,此时未设置正确的完整时长显示。
修改后的代码及说明
1. 修正全局变量与初始化
先在代码开头补充必要的全局变量和初始化逻辑:
import pygame import tkinter as tk import os pygame.init() pygame.mixer.init() MUSIC_END = pygame.USEREVENT + 1 name_songs = [] dir_songs = [] current_song_length = 0.0 # 缓存当前歌曲总长度 root = tk.Tk() # 主窗口实例
2. 修复check_event函数
def check_event(welcome_msg, time_left): global name_songs global dir_songs global current_song_length # 获取播放位置,处理暂停/结束时返回-1的情况 pos_ms = pygame.mixer.music.get_pos() if pos_ms > 0: # 保留三位小数,显示更精准 time_left['text'] = round(pos_ms / 1000, 3) elif pos_ms == -1 and not pygame.mixer.music.get_busy(): # 歌曲已结束,直接显示总长度 time_left['text'] = round(current_song_length, 3) for event in pygame.event.get(): if event.type == MUSIC_END and len(name_songs) > 0: welcome_msg['text'] = name_songs[0] if len(name_songs) != 1: pygame.mixer.music.queue(dir_songs[0]) dir_songs.pop(0) name_songs.pop(0) # 切换歌曲后更新总长度缓存 current_song_length = pygame.mixer.music.get_length() # 修正缩进:每次调用后100ms再次执行,持续更新时长 root.after(100, lambda: check_event(welcome_msg, time_left))
3. 优化play函数
def play(music_label, time_left): global name_songs global dir_songs global current_song_length directory = r'C:\Users\Boris\Desktop\songs' first_file = 0 pygame.mixer.music.set_endevent(MUSIC_END) # 清空列表,避免重复添加歌曲 name_songs.clear() dir_songs.clear() for filename in os.listdir(directory): f = os.path.join(directory, filename) if not os.path.isfile(f): continue # 跳过非文件项 splited_dir = f.split("\\") splited_format = splited_dir[-1].split('.') music_name = splited_format[0] name_songs.append(music_name) dir_songs.append(f) if first_file == 0: pygame.mixer.music.load(f) pygame.mixer.music.play(loops=0) music_label.configure(text=music_name) # 初始化当前歌曲总长度 current_song_length = pygame.mixer.music.get_length() name_songs.pop(0) dir_songs.pop(0) elif first_file == 1: pygame.mixer.music.queue(dir_songs[0]) dir_songs.pop(0) first_file += 1 # 启动时长更新循环 check_event(music_label, time_left)
关键修改点说明
- 修复递归调用:将
root.after移到函数末尾,确保每100ms自动调用一次check_event,实时刷新时长。 - 提升更新频率:从1000ms改为100ms,缩短显示间隔,减少误差。
- 缓存总长度:新增
current_song_length变量存储当前歌曲总时长,歌曲结束时直接显示该值,保证结束状态的准确性。 - 完善边界处理:判断
get_pos()返回-1的情况,区分暂停和播放结束状态,避免显示异常。
内容的提问来源于stack exchange,提问作者michael
相关产品推荐
相关产品推荐

