如何在Pyglet中连续播放音乐(同时使用tkinter)?

How to play music continuously in Pyglet (using tkinter at the same time)?

我试图通过制作一个播放列表在我的游戏中加入一些音乐,当一首音乐结束时下一首音乐开始...在寻找解决方案数小时后我仍然找不到... 我知道如何播放一首音乐以及如何使用 next_source 播放下一首音乐,但我不知道如何让它在结束时自动播放音乐。这就是我来这里的原因。 我发现很多 websites/forums 告诉你使用 on_eos 但我就是不能让它工作。

player = pyglet.media.Player()
music1 = pyglet.media.load('somemusic.wav')
music2 = pyglet.media.load('someothermusic.wav')
player.queue(music1)
player.queue(music2)

#the part i'm struggling with (I've tried that)

player.on_eos = pyglet.media.Player.on_eos
@player.event
def on_eos():
    #some function
    print("on player eos")

player.push_handlers(on_eos)

#

player.play()

它 运行 它是正确的,您必须在代码末尾使用 pyglet.app.run()。它将 运行 event loop 将 运行 编码播放下一首音乐。它还将运行代码分配给on_eos,等等

import pyglet

files = [
    'somemusic1.wav',
    'somemusic2.wav',
    'somemusic3.wav',
]
    
player = pyglet.media.Player()

@player.event
def on_eos():
    print("[event] on_eos: end of file")

@player.event
def on_player_eos():
    print("[event] on_player_eos: end of queue")

musics = []

for filename in files:
    item = pyglet.media.load(filename) 
    musics.append( item )
    print('load:', filename)
    
    player.queue(item)
    print('queue:', item)

player.play()

pyglet.app.run()  # need it to run `event loop` 

如果你想 运行 它与 tkinter 那么它可能是问题,因为 pyglet 运行s 事件循环 pyglet.app.run() 阻塞代码和 tkinter 必须 运行 拥有事件循环 root.mainloop(),这也会阻塞代码。此循环之一必须在单独的线程中 运行 - 类似于 Playing music with Pyglet and Tkinter in Python

中的答案

我尝试对这段代码使用线程,但当我按下 tkinter 中的按钮时,我只播放了第一首音乐。所以我辞职了。

我也尝试在 piglet 中构建自己的循环(参见 并添加 root.update() 而不是 root.mainloop() 但它需要更多的东西才能正常工作。所以我辞职了。

while True:
    pyglet.clock.tick()

    for window in pyglet.app.windows:
        window.switch_to()
        window.dispatch_events()
        window.dispatch_event('on_draw')
        window.flip()
    root.update()

对我来说最好的解决办法是使用PyQt。它用于创建 GUI,它有许多小部件和许多用于其他任务的 类。

单击按钮后,我使用了一些带有 QMediaPlayerQMediaPlaylist 的示例代码来 运行 所有音乐。它也有改变volume的功能,但我没有测试它。

它不仅可以加载声音,还可以加载可以在 QVideoWidget 中显示的视频。它可以直接从 Internet (URL) 加载文件。

from PyQt5.QtMultimediaWidgets import QVideoWidget
from PyQt5.QtCore import QUrl
from PyQt5.QtWidgets import QApplication, QPushButton
from PyQt5.QtMultimedia import QMediaContent, QMediaPlayer, QMediaPlaylist
import sys

files = [
    'somemusic1.wav',
    'somemusic2.wav',
    'somemusic3.wav',    
]
    
class VideoPlayer:

    def __init__(self):
        #self.video = QVideoWidget()
        #self.video.resize(300, 300)
        #self.video.move(0, 0)
        
        self.playlist = QMediaPlaylist()
        
        for item in files:
            self.playlist.addMedia(QMediaContent(QUrl.fromLocalFile(item)))
        
        self.playlist.setCurrentIndex(1)
        
        self.player = QMediaPlayer()
        #self.player.setVideoOutput(self.video)      
        self.player.setPlaylist(self.playlist)

    def callback(self):
        self.player.setPosition(0) # to start at the beginning of the video every time
        #self.video.show()
        self.player.play()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    v = VideoPlayer()
    b = QPushButton('start')
    b.clicked.connect(v.callback)
    b.show()
    sys.exit(app.exec_())

编辑:

我使用 Pyglettkinter 定期使用 pyglet.clock.schedule() 到 运行s root.update() 创建了工作代码。

import pyglet
from pyglet import clock
import tkinter as tk

files = [
    'somemusic1.wav',
    'somemusic2.wav',
    'somemusic3.wav',    
]
    
player = pyglet.media.Player()

@player.event
def on_eos():
    print("[event] on_eos: end of file")

@player.event
def on_player_eos():
    print("[event] on_player_eos: end of queue")

media = []

# load files
for filename in files:
    print('load:', filename)
    item = pyglet.media.load(filename) 
    media.append( item )


def create_queue():
    # create queue
    for item in media:
        print('queue:', item)
        player.queue(item)

# --- tkitner ---

def play():
    create_queue()
    player.play()

def update(event): # `schedule` sends `event` but `root.update` doesn't get any arguments
    root.update() 

def on_close():
    clock.unschedule(update)
    root.destroy()
    pyglet.app.exit()
            
is_paused = False

root = tk.Tk()

root.protocol("WM_DELETE_WINDOW", on_close)

tk.Button(root, text="Play", command=play).pack()
tk.Button(root, text="Exit", command=on_close).pack()

clock.schedule(update)

pyglet.app.run()  # need it to run `event loop` 
player = pyglet.media.Player()
music1 = 'somemusic.wav'
music2 = 'someothermusic.wav'
music = [music1, music2]

@player.event
def on_eos():
    print("[event] on_eos: end of file")

@player.event
def on_player_eos():
    print("[event] on_player_eos: end of queue")

media = []
for filename in music:
    print('load:', filename)
    item = pyglet.media.load(filename)
    media.append(item)

def create_queue():
    for item in media:
        print('queue:', item)
        player.queue(item)

def play():
    create_queue()
    player.play()

def update(event):
    root.update()

def on_close():
    clock.unschedule(update)
    root.destroy()  #edit:useless --> It even creates problems!
    pyglet.app.exit()  #this line does the work without the previous one

is_paused = False

root.protocol("WM_DELETE_WINDOW", on_close)

clock.schedule(update)

这给了我错误:TypeError: queue() missing 1 required positional argument: 'source' #edit: no more(solved), 问题出在其他地方。

播放按钮:

Button(StartPage, text="Play", command=play).pack()

注意:如果您像我一样将键绑定到函数(使用 tkinter)并且它们不再起作用,请使用 window.focus_force() 它将解决您的问题问题。