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

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()

请问如何解决时长显示不准确的问题,让音乐结束时能显示正确的完整时长?


解决方案

核心问题分析

  1. 递归调用缩进错误:原代码中root.after仅在MUSIC_END事件触发时才会再次调用check_event,导致播放过程中时长只更新一次,无法实时刷新进度。
  2. 更新频率过低:1000ms的更新间隔太长,进度显示不连贯,误差累积明显。
  3. 结束状态未处理:歌曲播放完毕时,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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 19:20:30