之前完成了一个较为简陋音乐播放器,这几天对其界面进行重置,让这个播放器界面稍微能高大上一点。新界面效果如下:
一、使用方法
界面上显示选择文件夹时指针放置该区域点击鼠标右键选择文件夹,选择成功后左键点击歌名暂停和播放,键盘左右键切换上一曲下一曲。
二、新增模块
2.1 歌词模块
歌词显示主要需要提前对歌词文件进行读取,然后将歌词与时间戳根据时间戳为Key键建立为字典,在之后需要的地方根据时间戳对字典值进行访问。
# 歌词载入字典文件
def getGeCi(path):
global lrc_dict
# 打开文件
lrc_dict = {}
if os.path.exists(path):
file = open(path, "r")
# 读取文件全部内容
lrc_list = file.readlines()
# '[01:48.00],[00:24.00],怀著冷却了的心窝飘远方'
# 遍历所有元素,干掉方括号
for i in lrc_list:
# 取出方括号并切割歌词字符串
lrc_word = i.replace("[", "]").strip().split("]")
# 得到的结果: lrc_word = ['', '01:40.00', '', '00:16.00', '今天我寒夜里看雪飘过']
for j in range(len(lrc_word)-1):
if lrc_word[j] and lrc_word[j].find('.') > 0:
jj = lrc_word[j][0:lrc_word[j].find('.')]
lrc_dict[jj] = lrc_word[-1]
file.close()
else:
lrc_dict = {}
显示时访问对应字典值:
if len(lrc_dict) != 0:
if str(lrc_dict.get(str_time)).strip() == "None":
pass
else:
gecivar.set(lrc_dict.get(str_time))
else:
gecivar.set("该歌曲无歌词文件")
2.2 圆环显示
创建这个圆环显示的初衷是将歌曲的节奏根据圆环的大小来显示出来,但是实在是本人在这方面的知识很是匮乏,所以仅仅是虎头蛇尾的完成随机大小的圆环显示,和歌曲节奏没有任何关系,比较遗憾。但是此处还是贴出我完成此功能的代码。
# 自建画圆函数
def darwCircle(cv,i,color,bian):
global mySize
cv.create_oval(mySize/2-100-bian*i, mySize/2-100-bian*i, mySize/2+100+bian*i, mySize/2+100+bian*i,
outline = color, # 边框颜色
width = bian # 边框宽
)
# 自建画圆线程
def musicDraw():
global startFlag
# 随机画圆
while True:
if startFlag:
i = random.randint(0, 20)
j = random.randint(0, 18)
darwCircle(cv, i, cssColor[j], 7)
time.sleep(0.2)
darwCircle(cv, i, "white", 7)
整体代码奉上:
import random
import time
import tkinter
from tkinter import filedialog
import threading
import os
import pygame
from mutagen.mp3 import MP3
cssColor = ["black","silver","gray","maroon","red", "purple","fuchsia",\
"green","lime", "olive" ,"yellow","navy","blue","teal","aqua","hotpink" ,"deeppink","lightpink","mistyrose"]
mySize = 500 # 方形窗口大小
musicPathList = [] # 音乐路径元组
musicNameList = [] # 音乐名称元组
startFlag = False # 开始标志位 False为暂停 True为开始
threadFlag = 0 # 标志线程是否已经在执行
musicNum = 0 # 当前播放第几首
nextFlag = 0 # 上一曲 下一曲标记 1为下一曲 -1为上一曲
lrc_dict = {} # 创建字典,存放歌词和时间,key表示时间,value表示歌词
lrcIndex = 0 # 歌词下标
lock = threading.Lock()
# 歌词载入字典文件
def getGeCi(path):
global lrc_dict
# 打开文件
lrc_dict = {}
if os.path.exists(path):
file = open(path, "r")
# 读取文件全部内容
lrc_list = file.readlines()
# '[01:48.00],[00:24.00],怀著冷却了的心窝飘远方'
# 遍历所有元素,干掉方括号
for i in lrc_list:
# 取出方括号并切割歌词字符串
lrc_word = i.replace("[", "]").strip().split("]")
# 得到的结果: lrc_word = ['', '01:40.00', '', '00:16.00', '今天我寒夜里看雪飘过']
for j in range(len(lrc_word)-1):
if lrc_word[j] and lrc_word[j].find('.') > 0:
jj = lrc_word[j][0:lrc_word[j].find('.')]
lrc_dict[jj] = lrc_word[-1]
file.close()
else:
lrc_dict = {}
# 自建画圆函数
def darwCircle(cv,i,color,bian):
global mySize
cv.create_oval(mySize/2-100-bian*i, mySize/2-100-bian*i, mySize/2+100+bian*i, mySize/2+100+bian*i,
outline = color, # 边框颜色
width = bian # 边框宽
)
# 自建画圆线程
def musicDraw():
global startFlag
# 随机画圆
while True:
if startFlag:
i = random.randint(0, 20)
j = random.randint(0, 18)
darwCircle(cv, i, cssColor[j], 7)
time.sleep(0.2)
darwCircle(cv, i, "white", 7)
# 将十进制转化为时间
def to_time(a):
m = int(a / 60)
s = int(a % 60)
if s < 10:
str_s = '0' + str(s);
else:
str_s = str(s);
if m < 10:
str_m = '0' + str(m)
else:
str_m = str(m)
return str_m,str_s
# 播放线程
def startMusic():
global musicPathList,musicNameList,musicNum,startFlag,threadFlag,lock,nextFlag,lrcIndex
pygame.mixer.init()
threadFlag = 1 # 标记线程开始
# 画图线程开始工作
drawMusic = threading.Thread(target=musicDraw, args=())
drawMusic.setDaemon(True) # 守护线程
drawMusic.start()
# 加载音乐
while True:
# 加载歌词
path = musicPathList[musicNum]
path = path.replace("mp3","lrc")
getGeCi(path)
# 初始化标志位
fflag = 0 # 标记是否为正常下一曲
str_time = "00:00"
audio = MP3(musicPathList[musicNum])
musiclen = int(audio.info.length) # 音乐长度
pygame.mixer.music.load(musicPathList[musicNum])
# 准备开始播放
pygame.mixer.music.play(start=0.0)
for i in range(1,musiclen+1):
# 播放时长,没有此延时,音乐不会播放,会一次性加载完
time.sleep(1)
# 先获取对应标志位 查看是否按下暂停 下一曲 上一曲等
lock.acquire()
flag = nextFlag
nextFlag = 0
staflag = startFlag # 播放标志位
lock.release()
if not staflag:
pygame.mixer.music.pause() # 暂停
while True:
if startFlag: # 再次开始播放
pygame.mixer.music.unpause()
break
if nextFlag != 0: # 点击了上一曲或者下一曲
flag = nextFlag
nextFlag = 0
startFlag = True
break
if flag != 0: # 点击了下一曲或者上一曲 直接切换歌曲
fflag = 1 # 设置为异常切歌标志位
break
# 显示对应时间 并且能将进度条推进
m,s = to_time(int(pygame.mixer.music.get_pos()/1000))
str_time = m + ':' + s;
jinduvar.set(str_time)
if len(lrc_dict) != 0:
if str(lrc_dict.get(str_time)).strip() == "None":
pass
else:
gecivar.set(lrc_dict.get(str_time))
else:
gecivar.set("该歌曲无歌词文件")
pygame.mixer.music.stop()
if fflag == 0 : # 当前歌曲播放完毕 自动切换下一曲
musicNum += 1
if musicNum >= len(musicNameList):
musicNum = 0
def Tonext(event):
global musicNum,musicNameList,nextFlag,lock
if len(musicNameList) > 0:
musicNum += 1
if musicNum >= len(musicNameList):
musicNum=0
namevar.set(musicNameList[musicNum]) # 显示歌名
gecivar.set('此处显示的是歌词')
lock.acquire()
nextFlag = 1
lock.release()
def Tobefore(event):
global musicNum, musicNameList,nextFlag,lock
if len(musicNameList)>0:
musicNum -= 1
if musicNum < 0:
musicNum = len(musicNameList)-1
namevar.set(musicNameList[musicNum]) # 显示歌
gecivar.set('此处显示的是歌词')
lock.acquire()
nextFlag = -1
lock.release()
# 获取音乐文件夹音乐
def getMusicList(event):
global musicPathList,musicNameList
musicPathList = [] # 音乐路径元组
musicNameList = [] # 音乐名称元组
Folderpath = filedialog.askdirectory() # 获得选择好的文件夹
if len(Folderpath) != 0:
list = os.listdir(Folderpath)
for music in list:
if "lrc" not in music:
musicpath = Folderpath + '\\' + music
if os.path.isfile(musicpath):
musicPathList.append(musicpath)
musicNameList.append(music)
namevar.set(musicNameList[0]) # 显示歌名
else:
namevar.set("请点击此处选择音乐所在文件夹")
print(musicNameList)
# 开始暂停函数
def toStart(event):
global startFlag,threadFlag
thmusic = threading.Thread(target=startMusic, args=())
thmusic.setDaemon(True) # 守护线程
if not startFlag and len(musicPathList) != 0: # 首先判断音乐文件已经导入
startFlag = True
if threadFlag == 0: # 线程未在运行时开始运行
thmusic.start()
else:
startFlag = False
if __name__ == "__main__":
dog = tkinter.Tk()
# 设置图片描绘的坐标,注意乘号是字母x
winW = dog.winfo_screenwidth()
winH = dog.winfo_screenheight()
w = mySize
h = mySize+80
size = str(w)+'x'+str(h) + '+' + str(int(winW / 2 - mySize / 2)) + '+' + str(int(winH / 2 - mySize / 2))
dog.geometry(size)
# 不允许修改大小
dog.resizable(False, False)
# 不显示标题栏
dog.overrideredirect(True)
# 设置白色透明色,这样图片中所有白色区域都被认为是透明的了
dog.wm_attributes('-transparentcolor', 'white')
dog.wm_attributes('-topmost', 1)
dog.configure(bg='white') # 设置主窗口背景颜色
# 歌名相关
namevar = tkinter.StringVar() # 将label标签的内容设置为字符类型,用var来接收hit_me函数的传出内容用以显示在标签上
namevar.set('请点击此处选择音乐所在文件夹')
nameLabel = tkinter.Label(dog, textvariable=namevar, font=('宋体', 16), bg='#99FFFF', fg='#000000') # bg为背景色,fg为前景色)
nameLabel.pack(side='top') # Label内容content区域放置位
nameLabel.bind("<3>",getMusicList) # 鼠标右键点击唤起歌曲文件夹
nameLabel.bind("<1>",toStart) # 鼠标左键点击开始 暂停
nameLabel.bind_all("<KeyPress-Left>",Tobefore) # 鼠标右键点击唤起歌曲文件夹
nameLabel.bind_all("<KeyPress-Right>",Tonext) # 鼠标左键点击开始 暂停
# 歌词显示
gecivar = tkinter.StringVar() # 将label标签的内容设置为字符类型,用var来接收hit_me函数的传出内容用以显示在标签上
gecivar.set('此处显示的是歌词')
geciLabel = tkinter.Label(dog, textvariable=gecivar, font=('宋体', 14), bg='#99FF99',fg='#FF44AA') # bg为背景色,fg为前景色
geciLabel.pack(pady='2') # Label内容content区域放置位
# 节奏绘图相关
cv = tkinter.Canvas(dog, width=mySize, height=mySize, bg='white')
cv.configure(highlightthickness=0) # 取消canvas边框
cv.pack()
# 进度显示相关
jinduvar = tkinter.StringVar() # 将label标签的内容设置为字符类型,用var来接收hit_me函数的传出内容用以显示在标签上
jinduvar.set('00:00')
jinduLabel = tkinter.Label(dog, textvariable=jinduvar, font=('宋体', 12), bg='#99FF99', fg='#FF44AA') # bg为背景色,fg为前景色
jinduLabel.pack(side="bottom") # Label内容content区域放置位
dog.mainloop()