【问题标题】:Playing sound using PyGame mixer does not work expected使用 PyGame 混音器播放声音无法正常工作
【发布时间】:2020-10-21 09:01:06
【问题描述】:

我正在做一个使用 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)

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

【问题讨论】:

  • 尝试:command=lambda: press(button_text.get())..) 并在你的函数中删除 word = button_text.get()。
  • 删除time.sleep(5) 后会发生什么?这是休眠线程,并中断主事件循环。我猜这就是窗口没有正确更新的原因。
  • 尝试将from pygame import mixer 更改为from pygame.mixer import *
  • @Kingsley 没有 time.sleep(5),歌曲不会播放。使用 time.sleep(5),歌曲播放 5 秒....
  • 我今天会尝试这些建议,谢谢您的意见!

标签: python tkinter pygame


【解决方案1】:

问题是您的代码在遍历音轨时阻塞了 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

由于它对自身的定时器调用重新排队,它需要在主函数中进行初始调用:

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

【讨论】:

  • 非常感谢!!!现在我明白它是如何工作得更好了。
猜你喜欢
  • 2019-06-23
  • 1970-01-01
  • 2013-09-13
  • 2017-12-28
  • 1970-01-01
  • 2021-09-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多