使用 PyGame 混音器播放声音无法正常工作



我正在做一个使用PyGame和Tkinter制作音乐播放器的项目。到目前为止,我已经构建了界面并让播放器播放两首不同的歌曲,但我需要一条关于如何更好地使用 PyGame 混音器的建议。我的代码是:

from tkinter import *
import time, sys
from pygame import mixer
track_names = ['lady maria', 'cleric beast']
current_track = ''

def press(word):
global track_name
global current_track
word = button_text.get()
if word == 'PLAY':
update_button_text('PLAY')
for name in track_names:
window_2.delete(0, 'end')
current_track = name
window_2.configure(state='normal')
window_2.insert('end', name)
mixer.init()
mixer.music.set_volume(100)
mixer.music.load(f'C:/Users/user/Desktop/python/projects/etc/{name}.mp3')
mixer.music.play()
time.sleep(5)
if word == 'PAUSE':
update_button_text('PAUSE')
mixer.music.pause()
time.sleep(5)
if word == 'STOP':
mixer.music.stop()
time.sleep(5)
if word == 'NEXT':
pass
if word == 'PREVIOUS':
pass

def update_button_text(word):
if word == 'PLAY':
button_text.set('PAUSE')
elif word == 'PAUSE':
button_text.set('PLAY')

if __name__ == '__main__':
# create application window
app = Tk()
# title
app.title("Music Players")
# geometry
app.geometry('383x121')
# background color
app.configure(bg='orange')
equation = StringVar()
window_1 = Label(app, textvariable=equation)
window_1.grid(columnspan=4, ipadx=100, ipady=10)
equation.set('music player')
window_2 = Entry(app, width=30)
window_2.grid(columnspan=4, ipadx=100, ipady=10)
window_2.configure(state='disabled')
window_2.grid_columnconfigure((0, 1, 2), uniform="equal", weight=1)
# Create buttons
button_text = StringVar()
button_text.set("PLAY")
button1 = Button(app, textvariable=button_text, fg='yellow', bg='purple',
command=lambda: press(button_text), height=2, width=1)
button1.grid(row=2, column=0, sticky="NSEW")
button2 = Button(app, text='STOP', fg='yellow', bg='purple',
command=lambda: press('STOP'), height=2, width=1)
button2.grid(row=2, column=1, sticky="NSEW")
button3 = Button(app, text='NEXT', fg='yellow', bg='purple',
command=lambda: press('NEXT'), height=2, width=1)
button3.grid(row=2, column=2, sticky="NSEW")
button4 = Button(app, text='PREVIOUS', fg='yellow', bg='purple',
command=lambda: press('PREVIOUS'), height=2, width=1)
button4.grid(row=2, column=3, sticky="NSEW")
# start the GUI
app.mainloop()

因此,当我运行代码并单击PLAY时,看起来播放器已停止响应。虽然它会立即开始播放音乐,但它不显示标题。我有代码首先将歌曲的标题插入 Tkinter 条目,然后是混音器功能,但似乎混音器函数总是排在第一位。这是我遇到问题的部分(我认为(:

def press(word):
global track_name
global current_track
word = button_text.get()
if word == 'PLAY':
update_button_text('PLAY')
for name in track_names:
window_2.delete(0, 'end')
current_track = name
window_2.configure(state='normal')
window_2.insert('end', name)
mixer.init()
mixer.music.set_volume(100)
mixer.music.load(f'C:/Users/user/Desktop/python/projects/etc/{name}.mp3')
mixer.music.play()
time.sleep(5)

为什么会这样?我应该如何修复代码以制作更好的音乐播放器?对此的任何建议将不胜感激!!

问题是您的代码在循环遍历音轨时阻塞了 TKinter 主事件循环。 事件驱动的程序不能阻止其事件,这就是为什么您看到窗口"锁定"的原因 - 没有重新绘制,大小调整,关闭等。 控制这些内容的窗口代码永远不会收到消息,并且永远无法对它们执行操作。

应用程序代码的阻塞此循环不得超过一瞬间。

那你能做什么... 使用计时器线程来完成应用程序的工作。 计时器易于使用,它们只是在主循环中的队列中添加函数调用。X毫秒后,将调用该函数。

例如:

window = tk.Tk()
[...]
window.after( 1000, myFunction, arg1 )

将导致主事件循环在 1000 毫秒后调用您的myFunction( arg1 )。 然后在myFunction中,您可以再次将调用排队以myFunction形成周期循环。

PyGame 混音器函数已经在单独的线程中播放音乐。 因此,在您的情况下,除了投票以查看音乐是否已完成之外,实际上不需要做任何事情。 所有"PLAY"代码需要做的就是开始播放下一个声音文件,然后返回。

在主循环中,我们可以创建一个新的计时器函数来检查声音文件是否仍在播放,或者是否需要对下一个文件进行排队:

def checkPlaying( main_window ):
global current_track
if ( mixer.music.get_busy() == False ):
# Playing has finished
print("Sound finished, playing next" )
track_index, track_name = current_track    
current_track = ( track_index + 1, '' )     # Advance to next track
press( 'PLAY' )                             # Start playing
# Queue the next call to this function
main_window.after( 250, checkPlaying, main_window )   # call again later

由于它会对自身重新排队计时器调用,因此它需要在 main 函数中调用该初始调用:

# Start the Music Playing Check
app.after( 1000, checkPlaying, app )

需要修改press()函数以删除连续循环,只需播放当前的声音文件:

mixer.init()                 
current_track = ( 0, '' )    # track-index and name

def press(word):
global track_names
global current_track
word = button_text.get()
if word == 'PLAY':
update_button_text('PLAY')
track_index, track_name = current_track
if ( track_index >= len( track_names ) ):  # if out of music, re-start
track_index = 0
# Start Playing the current track
name = track_names[ track_index ]
current_track = ( track_index, name )
mixer.music.set_volume(100)
mixer.music.load(f'C:/Users/user/Desktop/python/projects/etc/{name}.mp3')
mixer.music.play()

# Update the GUI
window_2.delete(0, 'end')
window_2.configure(state='normal')
window_2.insert('end', name)

仅此而已。

参考代码:

from tkinter import *
import time, sys
from pygame import mixer
mixer.init()
track_names = [ 'car-horn2', 'car-horn', 'cash-register', 'dog-bark', 'duck-quack', 'rain-falling', 'single-ding', 'turkey-gobble' ]
current_track = ( 0, '' )

def press(word):
global track_names
global current_track
word = button_text.get()
if word == 'PLAY':
update_button_text('PLAY')
track_index, track_name = current_track
if ( track_index >= len( track_names ) ):  # if out of music, re-start
track_index = 0   
# Play the current track
name = track_names[ track_index ]
current_track = ( track_index, name )
mixer.music.set_volume(100)
#mixer.music.load(f'C:/Users/user/Desktop/python/projects/etc/{name}.mp3')
mixer.music.load(f'{name}.mp3')
mixer.music.play()
window_2.delete(0, 'end')
window_2.configure(state='normal')
window_2.insert('end', name)
if word == 'PAUSE':
update_button_text('PAUSE')
mixer.music.pause()
time.sleep(5)
if word == 'STOP':
mixer.music.stop()
time.sleep(5)
if word == 'NEXT':
pass
if word == 'PREVIOUS':
pass

def checkPlaying( main_window ):
global track_names
global current_track
result = False
if ( mixer.music.get_busy() == True ):
# Still playing
result = True
else:
# Playing has finished
# TODO: Change button states, whatever
print("Sound finished, playing next" )
track_index, track_name = current_track
current_track = ( track_index + 1, '' )
press( 'PLAY' )        # start next track
result = False
# Queue the next call to this function
main_window.after( 250, checkPlaying, main_window )
return result

def update_button_text(word):
if word == 'PLAY':
button_text.set('PAUSE')
elif word == 'PAUSE':
button_text.set('PLAY')

if __name__ == '__main__':
# create application window
app = Tk()
# title
app.title("Music Players")
# geometry
app.geometry('383x121')
# background color
app.configure(bg='orange')
equation = StringVar()
window_1 = Label(app, textvariable=equation)
window_1.grid(columnspan=4, ipadx=100, ipady=10)
equation.set('music player')
window_2 = Entry(app, width=30)
window_2.grid(columnspan=4, ipadx=100, ipady=10)
window_2.configure(state='disabled')
window_2.grid_columnconfigure((0, 1, 2), uniform="equal", weight=1)
# Create buttons
button_text = StringVar()
button_text.set("PLAY")
button1 = Button(app, textvariable=button_text, fg='yellow', bg='purple',
command=lambda: press(button_text), height=2, width=1)
button1.grid(row=2, column=0, sticky="NSEW")
button2 = Button(app, text='STOP', fg='yellow', bg='purple',
command=lambda: press('STOP'), height=2, width=1)
button2.grid(row=2, column=1, sticky="NSEW")
button3 = Button(app, text='NEXT', fg='yellow', bg='purple',
command=lambda: press('NEXT'), height=2, width=1)
button3.grid(row=2, column=2, sticky="NSEW")
button4 = Button(app, text='PREVIOUS', fg='yellow', bg='purple',
command=lambda: press('PREVIOUS'), height=2, width=1)
button4.grid(row=2, column=3, sticky="NSEW")
# Start the Music Playing Check
app.after( 1000, checkPlaying, app )
# start the GUI
app.mainloop()

最新更新