我试图用Pygame和Tkinter播放声音文件(.mp3),但当我启动它时,我从来没有听到任何声音。这是代码:
from tkinter import StringVar, filedialog
import os
import tkinter
import pygame
from pygame import *
root = tkinter.Tk()
path = filedialog.askdirectory()
pygame.init()
pygame.mixer.init()
def startm():
if clicked.get() == mp3[0]:
pygame.mixer.music.load(mp3[0])
if not path:
print('Canceled')
else:
mp3 = [fn for fn in os.listdir(path) if fn.lower().endswith('.mp3') and os.path.isfile(os.path.join(path, fn))]
clicked = StringVar()
tkinter.OptionMenu(root, clicked, mp3[0], *mp3).pack()
w = tkinter.Button(root,text="start",command=startm)
w.pack()
root.mainloop()
程序运行但声音不工作
这里有几件事要注意,为什么音频不播放,为什么逻辑是有缺陷的。
-
没有播放音频的原因是因为你只是在加载音乐,而不是在播放它。要播放音乐,你必须说
pygame.mixer.music.play()
。 -
现在,即使您添加
pygame.mixer.music.play()
,您将注意到没有播放。这是因为if
条件有点毫无意义。行if clicked.get() == mp3[0]:
表示:只有当选项菜单中的选择与mp3
列表中的第一个项目相同时,才播放该歌曲。但是用户应该能够播放他们选择的任何歌曲,而不仅仅是列表中的第一首歌。所以你应该把它去掉。 -
现在您将注意到,尽管删除了这个,您将得到另一个错误,因为文件路径不存在。基本上,
clicked.get()
只是文件名,而不是文件路径。使用这样的文件名将强制python检查歌曲存在的相对路径。但是没有一条相对的路径使得这条路径是正确的。所以你必须使用绝对路径 -
现在使用绝对路径,你会注意到歌曲的绝对路径将显示在选项菜单中。但你不需要那样,你只需要歌曲名称显示在选项菜单上,但你想用绝对路径播放歌曲,所以必须有某种映射,比如:
{ "song_name": "D:/path/to/dir/song_name.mp3" } # Key will be displayed in optionmenu but value will be used in `pygame` to load the music.
因此,上面提到的修改代码是:
from glob import glob # (5) Mentioned in extra changes
from pathlib import Path # (6)
...
...
def startm():
song_name = clicked.get()
song_abs_path = songs[song_name] # (4) Get the absolute path from the songs dictionary
# if clicked.get() == mp3[0]: # (2) Getting rid of this line
pygame.mixer.music.load(song_abs_path) # (3) Loading song with its absolute path and not relative path
pygame.mixer.music.play() # (1) Play the song after loading it
if not path:
print('Canceled')
else:
mp3_abs_paths = glob(f"{path}/*.mp3") # (5) Choose all the mp3 files from the given path
mp3_names = [Path(path).name for path in mp3_abs_paths] # (6) List of all the song names
songs = {k:v for k,v in zip(mp3_names,mp3_abs_paths)} # (4) A dictionary in the format mentioned above above
clicked = StringVar()
tkinter.OptionMenu(root, clicked, mp3_names[0], *mp3_names).pack()
w = tkinter.Button(root,text="start",command=startm)
w.pack()
我在上面提到的修改在上面代码的注释中做了标记,使其更容易理解。
我做了一些额外的改变:
我介绍了一个模块,它将使您的生活更容易,
glob
。它基本上是mp3
的列表推导式的替代品。我从
pathlib
中使用Path
,因为它在许多方面比os.path
好,而且它也使将来与路径相关的操作比os.path
更简单。
我要做的另一件事是替换OptionMenu
,因为它有点俗气IMO。一个很好的替代品是ttk.Combobox
:
from tkinter import ttk
clicked = StringVar() # You can also get rid of this and start using `combo.get()` as it gives same value as `clicked.get()` and `clicked` becomes useless then.
combo = ttk.Combobox(root, values=mp3_names, state='readonly',textvariable=clicked)
combo.current(0) # Make the first item of the list as the current selection
combo.pack()