简体   繁体   English

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

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

I'm trying to put some music in my game by making a playlist and when a music ends the next one starts... and after hours searching for a solution i still can't find one... I know how to play one music and how to play the next one by using next_source , but i don't know how to make it do it automatically at the end of a music.我试图通过制作一个播放列表在我的游戏中加入一些音乐,当音乐结束时,下一个音乐开始......经过几个小时寻找解决方案我仍然找不到......我知道如何玩一个音乐以及如何使用next_source播放下一个,但我不知道如何让它在音乐结束时自动播放。 And that's why i'm here.这就是我在这里的原因。 I found many websites/forums that tell you to use on_eos but i just can't make it work.我发现许多网站/论坛告诉您使用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()
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)

And that give me the error: TypeError: queue() missing 1 required positional argument: 'source' #edit: no more(solved), the problem was somewhere else.这给了我错误: TypeError: queue() missing 1 required positional argument: 'source' #edit: no more(solved),问题出在其他地方。

The play button:播放按钮:

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

Note: If you had keys binded to functions (using tkinter) like i did and they do not work anymore, use window.focus_force() it will solve your problems.注意:如果您像我一样将键绑定到功能(使用 tkinter)并且它们不再起作用,请使用window.focus_force()它将解决您的问题。

It run it correctly you would have to use pyglet.app.run() at the end of code.它可以正确运行,您必须在代码末尾使用pyglet.app.run() It will run event loop which will run code which play next music.它将运行event loop ,该循环将运行播放下一首音乐的代码。 It will also run code assigned to on_eos , etc.它还将运行分配给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` 

If you want to run it with tkinter then it can be problem because pyglet runs event loop pyglet.app.run() which blocks code and tkinter has to run own event loop root.mainloop() which also blocks code.如果您想使用tkinter运行它,那么它可能会出现问题,因为pyglet运行事件循环pyglet.app.run()会阻塞代码,而tkinter必须运行自己的事件循环root.mainloop()也会阻塞代码。 One of this loop would have to run in separated thread - something similar to answer in Playing music with Pyglet and Tkinter in Python此循环之一必须在单独的线程中运行-类似于在Python 中使用 Pyglet 和 Tkinter 播放音乐中的答案

I tried use thread for this code but I played only first music when I press button in tkinter .我尝试对此代码使用线程,但是当我按下tkinter中的按钮时,我只播放了第一首音乐。 So I resigned.所以我辞职了。

I tried also build own loop in piglet (see我也尝试在piglet中建立自己的循环(见 and add root.update() instead of root.mainloop() but it would need something more to work correctly.并添加root.update()而不是root.mainloop()但它需要更多的东西才能正常工作。 So I resigned.所以我辞职了。

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

As for me the best solution is to use PyQt .对我来说,最好的解决方案是使用PyQt It is used to create GUI and it has many wigdets and many classes for other task.它用于创建GUI ,它有许多 wigdets 和许多用于其他任务的类。

I used some example code with QMediaPlayer and QMediaPlaylist to run all music one by one after clicking button.单击按钮后,我使用了一些示例代码与QMediaPlayerQMediaPlaylist一起运行所有音乐。 It has also function to change volume but I didn't test it.它也有 function 来改变volume ,但我没有测试它。

It can load not only sound but also video which it can display in QVideoWidget .它不仅可以加载声音,还可以加载可以在QVideoWidget中显示的视频。 And it can load files directly from internet (URLs).它可以直接从互联网(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_())

EDIT:编辑:

I created working code with Pyglet and tkinter using pyglet.clock.schedule() to runs root.update() periodically.我用Pyglettkinter创建了工作代码,使用pyglet.clock.schedule() 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` 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM