如何 link 带有滑块的 mp3 文件,以便滑块根据 mp3 文件长度移动?
How do I link an mp3 file with a slider so that the slider moves in relation to the mp3 file length?
如何 link 带有滑块的 mp3 文件,以便滑块根据 mp3 文件的长度移动?
在我的代码中,只有当我将鼠标放在滑块槽上时,滑块才会移动。
如何在不一直按住鼠标的情况下完成这项工作?
#using python3.6
from tkinter import *
from pygame import mixer
root = Tk()
from mutagen.mp3 import MP3
fLen =MP3('A Message to you Bit.mp3')
FileLength = fLen.info.length * 1000
mixer.init()
#load & play an mp3 file from root dir
def Play():
mixer.music.load('A Message to you Bit.mp3')
mixer.music.set_volume(.25)
mixer.music.play()
#set slider to mp3 file position
def ProgressBar(event):
slider.set(mixer.music.get_pos())
#create widgets
playBut = Button(text='Play',command = Play)
playBut.pack()
slider = Scale(to=FileLength, orient=HORIZONTAL, command=ProgressBar)
slider.pack()
我已经修改了您的代码以演示如何 link 您的滑块到轨道。见下文。
要更新滑块的位置,您需要熟悉 :
- tkinter 的
.get()
和 .set()
方法(参见 here) and use the variable option of the tkinter Scale
widget (see here)。
- tkinter 的
.after()
方法(参见 here)创建一个事件循环来跟踪 mp3 文件的播放。我创建的 .PlayTrack()
函数演示了如何执行此操作。
- 正在阅读Pygame的混音器
文档,
我意识到
.get_pos()
和 .set_pos()
以毫秒为单位运行并且
秒分别,因此你需要小心
转换。
第 1 点: 请注意,修改后的代码中存在问题。该代码能够从一开始就播放音乐文件。但是,如果幻灯片不在起点,按播放按钮将引发 exception/error
line 24, in Play mixer.music.set_pos( playtime )
pygame.error: set_pos unsupported for this codec
。
我不确定为什么 .set_pos
不起作用。我会让你解决这个问题。请在解决问题后分享您的答案。祝一切顺利。
第 2 点: 我将 mixer.init()
放在 Play()
函数中而不是放在主代码中,因为我注意到一旦它被激活,整个CPU 已消耗。我认为在按下播放按钮后激活它可以帮助您节省计算资源。
修改后的代码:
#using python3.6
from tkinter import *
from pygame import mixer
root = Tk()
from mutagen.mp3 import MP3
#fLen =MP3('A Message to you Bit.mp3')
musicfile='Test.mp3'
fLen =MP3( musicfile )
FileLength = fLen.info.length
print('FileLength = ', FileLength, ' sec')
#load & play an mp3 file from root dir
def Play():
print('\ndef Play():')
mixer.init()
mixer.music.load( musicfile )
mixer.music.set_volume( .25 )
playtime = slider_value.get()
if playtime > 0:
print( 'playtime = ', playtime, type(playtime) )
mixer.music.rewind()
mixer.music.set_pos( playtime )
mixer.music.play()
TrackPlay()
def TrackPlay():
if mixer.music.get_busy():
current = mixer.music.get_pos() #.get_pos() returns integer in milliseconds
print( 'current = ', current, type(current) )
slider_value.set( current/1000 ) #.set_pos() works in seconds
print( 'slider_value = ', slider_value.get(), type(slider_value.get()) )
root.after(1000, lambda:TrackPlay() ) # Loop every sec
#set slider to mp3 file position
def ProgressBar( value ):
print('\ndef ProgressBar( value ):')
print('value = ', value, type(value))
slider_value.set( value )
print('slider_value.get() = ', slider_value.get(), type(slider_value.get()) )
print('value = ', value, type(value) )
#slider.configure(from_=slider_value.get())
#create widgets
playBut = Button(text='Play',command=Play)
playBut.pack()
slider_value = DoubleVar()
slider = Scale( to=FileLength, orient=HORIZONTAL, length=500, resolution=1,
showvalue=True, tickinterval=30, variable=slider_value,
command=ProgressBar)
slider.pack()
root.mainloop()
更新 1:
我花时间仔细研究了未解决的问题。在这样做的同时,我意识到了一些等待我们的问题。
要在定义的时间播放曲目,我们可以使用pygame.mixer.music.play( start=time )
。 time
是我们需要提供的参数。不需要使用给我们带来问题的 pygame.mixer.music.set_pos()
方法。
-
Be aware that MP3 support is limited. On some systems an unsupported
format can crash the program, e.g. Debian Linux. Consider using OGG
instead.
MP3 与 pygame 不兼容,所以我们应该考虑使用 OGG。
要使音乐跟踪器有用,它应该允许用户在播放曲目时重新定位滑块,播放将从新的滑块位置开始。为此,我们必须取消由 .after
方法创建的回调。为了解决这个问题,以面向对象的方式编写 python 应用程序将使我们的事情变得更容易。
我们需要一种在整个 Tk window 被销毁之前退出 pygame.mixer 的方法。如果不是,它将继续在后台 运行 并消耗整个 CPU 核心。
基于以上,我写了一个新的脚本。我发现它非常适用于 ogg vorbis 音轨,但不适用于 mp3 音轨。我已将 python 脚本评论为我在脚本中所做的事情。希望它能帮助您学习如何使用 tkinter 和 python 做您想做的事。这是我第一次使用 pygame,所以如果我对 pygame 的回答不充分,请原谅我。
要使用此文件,请对第 42、44 和 146 行进行必要的更改。
新代码:
#!/usr/bin/python3
# -*- coding: utf-8 -*-
from mutagen.mp3 import MP3
from mutagen.oggvorbis import OggVorbis
from mutagen import MutagenError
from pygame import mixer
import tkinter as tk
import tkinter.messagebox as tkMessageBox
class MusicPlayer( tk.Frame ):
def __init__(self, master, tracktype='ogg', *args, **kwargs):
super().__init__(master) #initilizes self, which is a tk.Frame
self.pack()
# MusicPlayer's Atrributes
self.master = master # Tk window
self.track = None # Audio file
self.trackLength = None # Audio file length
self.player = None # Music player
self.playBut = None # Play Button
self.stopBut = None # Stop Button
self.slider = None # Progress Bar
self.slider_value = None # Progress Bar value
# Call these methods
self.get_AudioFile_MetaData( tracktype )
self.load_AudioFile()
self.create_Widgets()
def get_AudioFile_MetaData( self, tracktype ):
'''Get audio file and it's meta data (e.g. tracklength).'''
print( '\ndef get_AudioFileMetaData( self, audiofile ):' )
try:
if tracktype == 'mp3':
audiofile='Test.mp3' # In current directory
f = MP3( audiofile )
elif tracktype == 'ogg':
audiofile='Test.ogg' # In current directory
f = OggVorbis( audiofile )
else:
raise print( 'Track type not supported.' )
except MutagenError:
print( "Fail to load audio file ({}) metadata".format(audiofile) )
else:
trackLength = f.info.length
self.track = audiofile
self.trackLength = trackLength; print( 'self.trackLength',type(self.trackLength),self.trackLength,' sec' )
def load_AudioFile( self ):
'''Initialise pygame mixer, load audio file and set volume.'''
print( '\ndef load_AudioFile( self, audiofile ):' )
player = mixer
player.init()
player.music.load( self.track )
player.music.set_volume( .25 )
self.player = player
print('self.player ', self.player)
def create_Widgets ( self ):
'''Create Buttons (e.g. Start & Stop ) and Progress Bar.'''
print( '\ndef create_Widgets ( self ):' )
self.playBut = tk.Button( self, text='Play', command=self.Play )
self.playBut.pack()
self.stopBut = tk.Button( self, text='Stop', command=self.Stop )
self.stopBut.pack()
self.slider_value = tk.DoubleVar()
self.slider = tk.Scale( self, to=self.trackLength, orient=tk.HORIZONTAL, length=700,
resolution=0.5, showvalue=True, tickinterval=30, digit=4,
variable=self.slider_value, command=self.UpdateSlider )
self.slider.pack()
def Play( self ):
'''Play track from slider location.'''
print('\ndef Play():')
#1. Get slider location.
#2. Play music from slider location.
#3. Update slider location (use tk's .after loop)
playtime = self.slider_value.get(); print( type(playtime),'playtime = ',playtime,'sec' )
self.player.music.play( start=playtime ); print( 'Play Started' )
self.TrackPlay( playtime )
def TrackPlay( self, playtime ):
'''Slider to track the playing of the track.'''
print('\ndef TrackPlay():')
#1.When track is playing
# 1. Set slider position to playtime
# 2. Increase playtime by interval (1 sec)
# 3. start TrackPlay loop
#2.When track is not playing
# 1. Print 'Track Ended'
if self.player.music.get_busy():
self.slider_value.set( playtime ); print( type(self.slider_value.get()),'slider_value = ',self.slider_value.get() )
playtime += 1.0
self.loopID = self.after(1000, lambda:self.TrackPlay( playtime ) );\
print( 'self.loopID = ', self.loopID )
else:
print('Track Ended')
def UpdateSlider( self, value ):
'''Move slider position when tk.Scale's trough is clicked or when slider is clicked.'''
print( '\ndef UpdateSlider():' ); print(type(value),'value = ',value,' sec')
if self.player.music.get_busy():
print("Track Playing")
self.after_cancel( self.loopID ) #Cancel PlayTrack loop
self.slider_value.set( value ) #Move slider to new position
self.Play( ) #Play track from new postion
else:
print("Track Not Playing")
self.slider_value.set( value ) #Move slider to new position
def Stop( self ):
'''Stop the playing of the track.'''
print('\ndef Stop():')
if self.player.music.get_busy():
self.player.music.stop()
print('Play Stopped')
def ask_quit():
'''Confirmation to quit application.'''
if tkMessageBox.askokcancel("Quit", "Exit MusicPlayer"):
app.Stop() #Stop playing track
app.player.quit() #Quit pygame.mixer
root.destroy() #Destroy the Tk Window instance.
# Note: After initialzing pygame.mixer, it will preoccupy an entire CPU core.
# Before destroying the Tk Window, ensure pygame.mixer is quitted too else
# pygame.mixer will still be running in the background despite destroying the
# Tk Window instance.
if __name__ == "__main__":
root = tk.Tk() #Initialize an instance of Tk window.
app = MusicPlayer( root, tracktype='ogg' ) #Initialize an instance of MusicPlayer object and passing Tk window instance into it as it's master.
root.protocol("WM_DELETE_WINDOW", ask_quit) #Tell Tk window instance what to do before it is destroyed.
root.mainloop() #Start Tk window instance's mainloop.
如何 link 带有滑块的 mp3 文件,以便滑块根据 mp3 文件的长度移动? 在我的代码中,只有当我将鼠标放在滑块槽上时,滑块才会移动。 如何在不一直按住鼠标的情况下完成这项工作?
#using python3.6
from tkinter import *
from pygame import mixer
root = Tk()
from mutagen.mp3 import MP3
fLen =MP3('A Message to you Bit.mp3')
FileLength = fLen.info.length * 1000
mixer.init()
#load & play an mp3 file from root dir
def Play():
mixer.music.load('A Message to you Bit.mp3')
mixer.music.set_volume(.25)
mixer.music.play()
#set slider to mp3 file position
def ProgressBar(event):
slider.set(mixer.music.get_pos())
#create widgets
playBut = Button(text='Play',command = Play)
playBut.pack()
slider = Scale(to=FileLength, orient=HORIZONTAL, command=ProgressBar)
slider.pack()
我已经修改了您的代码以演示如何 link 您的滑块到轨道。见下文。 要更新滑块的位置,您需要熟悉 :
- tkinter 的
.get()
和.set()
方法(参见 here) and use the variable option of the tkinterScale
widget (see here)。 - tkinter 的
.after()
方法(参见 here)创建一个事件循环来跟踪 mp3 文件的播放。我创建的.PlayTrack()
函数演示了如何执行此操作。 - 正在阅读Pygame的混音器
文档,
我意识到
.get_pos()
和.set_pos()
以毫秒为单位运行并且 秒分别,因此你需要小心 转换。
第 1 点: 请注意,修改后的代码中存在问题。该代码能够从一开始就播放音乐文件。但是,如果幻灯片不在起点,按播放按钮将引发 exception/error
line 24, in Play mixer.music.set_pos( playtime )
pygame.error: set_pos unsupported for this codec
。
我不确定为什么 .set_pos
不起作用。我会让你解决这个问题。请在解决问题后分享您的答案。祝一切顺利。
第 2 点: 我将 mixer.init()
放在 Play()
函数中而不是放在主代码中,因为我注意到一旦它被激活,整个CPU 已消耗。我认为在按下播放按钮后激活它可以帮助您节省计算资源。
修改后的代码:
#using python3.6
from tkinter import *
from pygame import mixer
root = Tk()
from mutagen.mp3 import MP3
#fLen =MP3('A Message to you Bit.mp3')
musicfile='Test.mp3'
fLen =MP3( musicfile )
FileLength = fLen.info.length
print('FileLength = ', FileLength, ' sec')
#load & play an mp3 file from root dir
def Play():
print('\ndef Play():')
mixer.init()
mixer.music.load( musicfile )
mixer.music.set_volume( .25 )
playtime = slider_value.get()
if playtime > 0:
print( 'playtime = ', playtime, type(playtime) )
mixer.music.rewind()
mixer.music.set_pos( playtime )
mixer.music.play()
TrackPlay()
def TrackPlay():
if mixer.music.get_busy():
current = mixer.music.get_pos() #.get_pos() returns integer in milliseconds
print( 'current = ', current, type(current) )
slider_value.set( current/1000 ) #.set_pos() works in seconds
print( 'slider_value = ', slider_value.get(), type(slider_value.get()) )
root.after(1000, lambda:TrackPlay() ) # Loop every sec
#set slider to mp3 file position
def ProgressBar( value ):
print('\ndef ProgressBar( value ):')
print('value = ', value, type(value))
slider_value.set( value )
print('slider_value.get() = ', slider_value.get(), type(slider_value.get()) )
print('value = ', value, type(value) )
#slider.configure(from_=slider_value.get())
#create widgets
playBut = Button(text='Play',command=Play)
playBut.pack()
slider_value = DoubleVar()
slider = Scale( to=FileLength, orient=HORIZONTAL, length=500, resolution=1,
showvalue=True, tickinterval=30, variable=slider_value,
command=ProgressBar)
slider.pack()
root.mainloop()
更新 1:
我花时间仔细研究了未解决的问题。在这样做的同时,我意识到了一些等待我们的问题。
要在定义的时间播放曲目,我们可以使用
pygame.mixer.music.play( start=time )
。time
是我们需要提供的参数。不需要使用给我们带来问题的pygame.mixer.music.set_pos()
方法。-
Be aware that MP3 support is limited. On some systems an unsupported format can crash the program, e.g. Debian Linux. Consider using OGG instead.
MP3 与 pygame 不兼容,所以我们应该考虑使用 OGG。
要使音乐跟踪器有用,它应该允许用户在播放曲目时重新定位滑块,播放将从新的滑块位置开始。为此,我们必须取消由
.after
方法创建的回调。为了解决这个问题,以面向对象的方式编写 python 应用程序将使我们的事情变得更容易。我们需要一种在整个 Tk window 被销毁之前退出 pygame.mixer 的方法。如果不是,它将继续在后台 运行 并消耗整个 CPU 核心。
基于以上,我写了一个新的脚本。我发现它非常适用于 ogg vorbis 音轨,但不适用于 mp3 音轨。我已将 python 脚本评论为我在脚本中所做的事情。希望它能帮助您学习如何使用 tkinter 和 python 做您想做的事。这是我第一次使用 pygame,所以如果我对 pygame 的回答不充分,请原谅我。
要使用此文件,请对第 42、44 和 146 行进行必要的更改。
新代码:
#!/usr/bin/python3
# -*- coding: utf-8 -*-
from mutagen.mp3 import MP3
from mutagen.oggvorbis import OggVorbis
from mutagen import MutagenError
from pygame import mixer
import tkinter as tk
import tkinter.messagebox as tkMessageBox
class MusicPlayer( tk.Frame ):
def __init__(self, master, tracktype='ogg', *args, **kwargs):
super().__init__(master) #initilizes self, which is a tk.Frame
self.pack()
# MusicPlayer's Atrributes
self.master = master # Tk window
self.track = None # Audio file
self.trackLength = None # Audio file length
self.player = None # Music player
self.playBut = None # Play Button
self.stopBut = None # Stop Button
self.slider = None # Progress Bar
self.slider_value = None # Progress Bar value
# Call these methods
self.get_AudioFile_MetaData( tracktype )
self.load_AudioFile()
self.create_Widgets()
def get_AudioFile_MetaData( self, tracktype ):
'''Get audio file and it's meta data (e.g. tracklength).'''
print( '\ndef get_AudioFileMetaData( self, audiofile ):' )
try:
if tracktype == 'mp3':
audiofile='Test.mp3' # In current directory
f = MP3( audiofile )
elif tracktype == 'ogg':
audiofile='Test.ogg' # In current directory
f = OggVorbis( audiofile )
else:
raise print( 'Track type not supported.' )
except MutagenError:
print( "Fail to load audio file ({}) metadata".format(audiofile) )
else:
trackLength = f.info.length
self.track = audiofile
self.trackLength = trackLength; print( 'self.trackLength',type(self.trackLength),self.trackLength,' sec' )
def load_AudioFile( self ):
'''Initialise pygame mixer, load audio file and set volume.'''
print( '\ndef load_AudioFile( self, audiofile ):' )
player = mixer
player.init()
player.music.load( self.track )
player.music.set_volume( .25 )
self.player = player
print('self.player ', self.player)
def create_Widgets ( self ):
'''Create Buttons (e.g. Start & Stop ) and Progress Bar.'''
print( '\ndef create_Widgets ( self ):' )
self.playBut = tk.Button( self, text='Play', command=self.Play )
self.playBut.pack()
self.stopBut = tk.Button( self, text='Stop', command=self.Stop )
self.stopBut.pack()
self.slider_value = tk.DoubleVar()
self.slider = tk.Scale( self, to=self.trackLength, orient=tk.HORIZONTAL, length=700,
resolution=0.5, showvalue=True, tickinterval=30, digit=4,
variable=self.slider_value, command=self.UpdateSlider )
self.slider.pack()
def Play( self ):
'''Play track from slider location.'''
print('\ndef Play():')
#1. Get slider location.
#2. Play music from slider location.
#3. Update slider location (use tk's .after loop)
playtime = self.slider_value.get(); print( type(playtime),'playtime = ',playtime,'sec' )
self.player.music.play( start=playtime ); print( 'Play Started' )
self.TrackPlay( playtime )
def TrackPlay( self, playtime ):
'''Slider to track the playing of the track.'''
print('\ndef TrackPlay():')
#1.When track is playing
# 1. Set slider position to playtime
# 2. Increase playtime by interval (1 sec)
# 3. start TrackPlay loop
#2.When track is not playing
# 1. Print 'Track Ended'
if self.player.music.get_busy():
self.slider_value.set( playtime ); print( type(self.slider_value.get()),'slider_value = ',self.slider_value.get() )
playtime += 1.0
self.loopID = self.after(1000, lambda:self.TrackPlay( playtime ) );\
print( 'self.loopID = ', self.loopID )
else:
print('Track Ended')
def UpdateSlider( self, value ):
'''Move slider position when tk.Scale's trough is clicked or when slider is clicked.'''
print( '\ndef UpdateSlider():' ); print(type(value),'value = ',value,' sec')
if self.player.music.get_busy():
print("Track Playing")
self.after_cancel( self.loopID ) #Cancel PlayTrack loop
self.slider_value.set( value ) #Move slider to new position
self.Play( ) #Play track from new postion
else:
print("Track Not Playing")
self.slider_value.set( value ) #Move slider to new position
def Stop( self ):
'''Stop the playing of the track.'''
print('\ndef Stop():')
if self.player.music.get_busy():
self.player.music.stop()
print('Play Stopped')
def ask_quit():
'''Confirmation to quit application.'''
if tkMessageBox.askokcancel("Quit", "Exit MusicPlayer"):
app.Stop() #Stop playing track
app.player.quit() #Quit pygame.mixer
root.destroy() #Destroy the Tk Window instance.
# Note: After initialzing pygame.mixer, it will preoccupy an entire CPU core.
# Before destroying the Tk Window, ensure pygame.mixer is quitted too else
# pygame.mixer will still be running in the background despite destroying the
# Tk Window instance.
if __name__ == "__main__":
root = tk.Tk() #Initialize an instance of Tk window.
app = MusicPlayer( root, tracktype='ogg' ) #Initialize an instance of MusicPlayer object and passing Tk window instance into it as it's master.
root.protocol("WM_DELETE_WINDOW", ask_quit) #Tell Tk window instance what to do before it is destroyed.
root.mainloop() #Start Tk window instance's mainloop.