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

调用player.delete()后Pyglet媒体队列未清空问题求助

问题描述

我正在实现媒体播放与停止功能:点击停止按钮后,播放器暂停且列表框选中项被清除,但再次点击播放按钮时,媒体会从上次暂停的位置继续播放——当前停止功能仅实现了暂停效果,未能清空播放队列。希望在不修改Pyglet库的前提下解决该问题。

解决方案

要彻底实现停止后重新播放从头开始的效果,需要调整播放器重置逻辑和播放逻辑:

  • 停止时不仅要暂停和清空队列,还要重置播放器的播放位置到起始点
  • 播放时避免重复将媒体源加入队列,确保每次播放都是加载新的媒体实例并从头开始

以下是修改后的完整代码:

import tkinter as tk
from tkinter import filedialog
import pyglet
import os
import itertools

class Seekbar(tk.Canvas):

    def __init__(self, parent, **options):
        tk.Canvas.__init__(self, parent, options)
        self.parent = parent
        self.width = options['width']
        self.red_rectangle = self.create_rectangle(0, 0, 0, 0, fill="red")
        self.seekbar_knob_image = tk.PhotoImage(file="icons/seekbar_knob.gif")
        self.seekbar_knob = self.create_image(
            0, 0, image=self.seekbar_knob_image)
        self.bind_mouse_button()


    def bind_mouse_button(self):
        self.bind('<Button-1>', self.on_seekbar_clicked)
        self.bind('<B1-Motion>', self.on_seekbar_clicked)
        self.tag_bind(
            self.red_rectangle, '<B1-Motion>', self.on_seekbar_clicked)
        self.tag_bind(
            self.seekbar_knob, '<B1-Motion>', self.on_seekbar_clicked)

    def on_seekbar_clicked(self, event=None):
        if event.x > 0 and event.x < self.width:
            self.slide_to_position(event.x)

    def slide_to_position(self, new_position):
        self.coords(self.red_rectangle, 0, 0, new_position, self.winfo_height())
        self.coords(self.seekbar_knob, new_position, self.winfo_height()//2)
        self.event_generate("<<SeekbarPositionChanged>>", x=new_position)


class TestSeekBar():
    toggle_play_stop = itertools.cycle(["play", "stop"])

    def __init__(self):
        root = tk.Tk()
        root.bind("<<SeekbarPositionChanged>>", self.seek_new_position)
        frame = tk.Frame(root)
        frame.grid(row=1, pady=10, padx=10)
        c = Seekbar(
            frame, background="blue", width=360, height=10)
        c.grid(row=2, columnspan=10, sticky='ew', padx=5)
        self.add_file_image = tk.PhotoImage(file="icons/add_file.gif")
        self.add_file = tk.Button(frame, image=self.add_file_image, command=self.add_file)
        self.add_file.grid(row=3)
        self.audio_list_box = tk.Listbox(frame)
        self.audio_list_box.grid(row=4)
        self.play_image = tk.PhotoImage(file="icons/play.gif")
        self.stop_image = tk.PhotoImage(file="icons/stop.gif")
        self.play_stop_btn = tk.Button(frame, image=self.play_image, command=self.play_stop)
        self.play_stop_btn.image = self.play_image
        self.play_stop_btn.grid(row=3, column=2)
        self.audio_time = tk.Label(frame, text="00.00")
        self.audio_time.grid(row=1, column=2)
        of_label = tk.Label(frame, text="of")
        of_label.grid(row=1, column=3)
        self.audio_total_length = tk.Label(frame, text="00.00")
        self.audio_total_length.grid(row=1, column=4)
        self.player = pyglet.media.Player()
        # 记录当前加载的媒体源,避免重复添加
        self.current_source = None
        root.mainloop()

    def seek_new_position(self, event):
        print("Dragged to x:", event.x)
        # 拖动进度条时同步播放器位置
        if self.current_source:
            duration = self.current_source.duration
            seek_position = (event.x / 360) * duration
            self.player.seek(seek_position)

    def add_file(self):
        audios = filedialog.askopenfilenames()
        for audio in audios:
            path = os.path.split(audio)
            audio_filename = path[1]
            self.audio_list_box.insert(tk.END, audio_filename)
            self.path = f'{path[0]}/'
            print(self.path)

    def play_stop(self):
        action = next(self.toggle_play_stop)
        if action == "play":
            try:
                self.current_track_index = self.audio_list_box.curselection()[0]
            except IndexError:
                self.current_track_index = 0
            self.play()
        elif action == "stop":
            self.stop_play()

    def play(self):
        # 获取选中的音频文件名并拼接完整路径
        audio_filename = self.audio_list_box.get(self.current_track_index)
        full_path = os.path.join(self.path, audio_filename)
        
        self.play_stop_btn.config(image=self.stop_image)
        # 播放前先重置播放器状态
        self.reset_player()
        self.current_source = pyglet.media.load(full_path)
        self.player.queue(self.current_source)
        self.get_audio_length()
        self.player.play()

    def stop_play(self):
        self.play_stop_btn.config(image=self.play_image)
        self.audio_list_box.selection_clear(0, tk.END)
        self.audio_total_length.config(text="00.00")
        self.audio_time.config(text="00.00")
        self.reset_player()

    def reset_player(self):
        # 彻底重置播放器状态
        self.player.pause()
        self.player.delete()
        # 重置播放位置到起始点
        self.player.seek(0.0)
        self.current_source = None

    def get_audio_length(self):
        self.get_audio_total_length = self.current_source.duration
        minutes, seconds = self.get_time_in_minute_seconds(self.get_audio_total_length)
        self.track_length_string = '{0:02d}:{1:02d}'.format(minutes, seconds)
        self.audio_total_length.config(text=self.track_length_string)
        # 启动定时器更新当前播放时间
        self.update_audio_time()

    def update_audio_time(self):
        if self.current_source and self.player.playing:
            current_time = self.player.time
            minutes, seconds = self.get_time_in_minute_seconds(current_time)
            self.audio_time.config(text='{0:02d}:{1:02d}'.format(minutes, seconds))
            # 每隔100ms更新一次播放时长
            self.play_stop_btn.after(100, self.update_audio_time)

    def get_time_in_minute_seconds(self, time_in_seconds):
        minutes = int(time_in_seconds / 60)
        seconds = int(time_in_seconds % 60)
        return (minutes, seconds)


if __name__ == '__main__':
    TestSeekBar()

修改要点说明

  • 重置播放器逻辑:在reset_player中新增self.player.seek(0.0),确保停止后播放位置回到起始点;同时清空current_source标记,避免残留引用
  • 播放逻辑优化:每次播放前先调用reset_player清空队列,再加载新的媒体源,彻底避免重复添加到播放队列的问题
  • 路径修复:修复了原代码中缺少音频文件完整路径的问题,确保能正确加载选中的音频
  • 新增播放时间更新:添加update_audio_time方法实时更新当前播放时长,增强播放器的交互体验
  • 进度条拖动同步:在seek_new_position中新增播放器进度同步逻辑,拖动进度条时可直接跳转播放位置

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 12:17:06