【记录基于Python tkinter的音乐播放器的实现过程】


**引言:**相信大家和我一样喜欢音乐。多年积累下来,我居然在电脑上下载了500多首歌曲。歌曲放在一个文件夹下,平时可以通过音乐软件或平台来播放。然而,作为编程学习者,何不自己开发一款软件对自己下载的音乐进行管理呢?出于兴趣爱好,我利用python tkinter实现了本地音乐播放的界面化。下面记录一下音乐播放器的整个实现过程。

一、播放器的设计概述

功能:
1. 播放功能:播放,暂停/继续,上/下一首,拖拽进度条,调节音量
2. 播放方式:顺序、随机、单曲播放
3. 搜索和播放:输入关键字,列出包含关键字的歌曲,可以选中播放
4. 歌曲显示:主列表列出所有的歌曲,浏览和选择播放歌曲
5. 歌词显示:播放时同步滚动歌词
6. 临时列表:从主列表、搜索列表中选择歌曲添加到临时列表,播放临时列表的歌曲
7. 收藏歌单:创建歌单,添加歌曲,更好分类播放
8. 听歌记录:记录什么时间听了什么歌曲,方便统计听歌次数,听歌时长

思路:
1.音乐播放引擎:选择的是pygame.mixer.music模块,它可以帮助我们实现播放器的基本功能。
pygame music模块功能学习
2.音乐播放器界面:使用python 自带的tkinter库,实现界面化。
3.本地文件夹:歌曲、歌词、歌单、程序。
4.按钮的command为线程封装的函数:pygame播放音乐时,需要设置它的播放时长,也就是延时,如果不使用线程,那么播放时界面上会出现卡死,根本点击不了其他按钮。

界面显示:

目前音乐播放器的功能基本实现,后续如有改进的地方再更新。

文末附带完整代码。

打开播放器的初始化界面

图1 播放器初始化界面

播放界面

图2 播放器播放界面

搜索关键字,然后播放

图3 搜索播放

听歌记录

图4 听歌记录

听歌次数及时长

图5 听歌次数及时长

收藏的歌单

图6 收藏歌单

读取我喜欢的歌单

图7 读取歌单

二、界面组件

这里把需要用到的组件列出来
Listbox 列表组件来容纳歌曲、歌词
Scrollbar 滑动块组件来跟踪浏览Listbox歌曲的位置
Button 按钮实现点击功能
Label 显示播放中的歌曲名称,显示歌曲总数,北京时间
Scale 实现进度条的拖拽,音量的调节
Progressbar 显示歌曲播放进度
Entry 实现关键字的输入

界面组件搭建代码如下:

class MusicPlayer():
    def __init__(self):
		# colors
        self.GlobalColor = '#F1E9D2'     # 羊皮纸颜色
        self.LightYellow = '#FFE57D'     # 浅黄色
        self.LightPurple = '#C748FA'     # 浅紫色
        self.Silvery = '#CFCED6'         # 亮银色

        self.COLOR_SIZE = 16
        self.MyColors = [self.GlobalColor]*self.COLOR_SIZE
        self.WordStyle = '微软雅黑' 
        
        self.screen = tk.Tk()
        self.screen.geometry('600x700')
        self.screen.title('本地音乐播放器')

        #滑动条
        self.Scroll_songOrder = tk.Scrollbar(self.screen)
        self.ListBox_songOrder = tk.Listbox(self.screen, width=61, height=10, bg=self.GlobalColor, fg='black', font=(self.WordStyle, 12),
                             yscrollcommand=self.Scroll_songOrder.set)
        #歌曲名列表框
        self.ListBox_songOrder.config(yscrollcommand=self.Scroll_songOrder.set)
        self.Scroll_songOrder.config(command=self.ListBox_songOrder.yview)
        

        #绑定滑动条
        self.ListBox_songOrder.place(x=10, y=290)
        self.Scroll_songOrder.pack(side='right', fill='both')

        # 显示正在播放
        self.Label_play = tk.Label(self.screen, height=1, width=10, text='正在播放:', bg=self.GlobalColor, fg='black', font=(self.WordStyle, 12))
        self.Label_play.place(x=10, y=30)

        #显示正在播放的歌曲
        self.StrngVar_songName = tk.StringVar()
        self.Label_songName = tk.Label(self.screen, width=45, height=1, fg='black', bg=self.GlobalColor, font=(self.WordStyle, 12),
                            textvariable=self.StrngVar_songName)
        self.Label_songName.place(x=100, y=30)

        #搜索框
        self.Entry_find = tk.Entry(self.screen, width=65, bg=self.GlobalColor, font=(self.WordStyle, 12))
        self.Entry_find.place(x=410, y=520, width=145, height=35)
        self.Entry_find.delete(0, tk.END)
        self.Entry_find.insert(0, "关键字搜索")
        self.Entry_find.bind('<Return>', self.findSong)

        #搜索的内容
        self.Listbox_find = tk.Listbox(self.screen, width=61, height=5, bg=self.GlobalColor, fg='black', font=(self.WordStyle, 12))
        self.Listbox_find.place(x=10, y=570)

        #歌词的内容
        self.Listbox_LRC = tk.Listbox(self.screen, width=61, height=3, bg=self.GlobalColor, fg='blue', font=(self.WordStyle, 12))
        self.Listbox_LRC.place(x=10, y=60)

        # 显示进度条
        self.ProgressBar = ttk.Progressbar(self.screen, length=430, mode="determinate", orient=HORIZONTAL)
        self.ProgressBar.place(x=130, y=140)
        self.ProgressBar["maximum"] = 99
        self.ProgressBar["value"] = 0
        self.CreateToolTip(self.ProgressBar, "显示进度条")
        
        # 更改进度条
        self.ProgressScale = tk.Scale(self.screen, from_=0, to=100, bd=1, orient=tk.HORIZONTAL,
                     length=430, showvalue=False, resolution=1, command=self.songProgressAdjust) #tickinterval=20
        self.ProgressScale.set(0)
        self.ProgressScale.place(x=130, y=190)
        self.CreateToolTip(self.ProgressScale, "拖拽进度条")
        

        self.Label_songTime = tk.Label(self.screen, text="00:00", width=10,height=1, bg="white", fg='black', font=(self.WordStyle, 12))
        self.Label_songTime.place(x=10, y=140)

        
        self.Label_offset = tk.Label(self.screen, text="offset:", width=10,height=1, bg="white", fg='black', font=(self.WordStyle, 12))
        self.Label_offset.place(x=10, y=190)
        
        #self.screen.after(100, self.songProgressbar)

        self.Button_lastSong = tk.Button(self.screen, text='◀', width=3, bg=self.GlobalColor, fg=self.LightPurple, font=(self.WordStyle, 12), command=lambda: self.Thread_NULL(self.lastSong),
                            relief=tk.RAISED)
        self.Button_lastSong.place(x=10, y=250)
        self.CreateToolTip(self.Button_lastSong, "上一首")


        self.Button_playSong = tk.Button(self.screen, text='♫', width=3, bg=self.MyColors[0], fg=self.LightPurple, font=(self.WordStyle, 12),
                            command=lambda: self.Thread_NULL(self.playSongByOrder) )
        self.Button_playSong.place(x=160, y=250)
        self.CreateToolTip(self.Button_playSong, "播放音乐")

        self.Button_pause = tk.Button(self.screen, text='‖', width=3, bg=self.GlobalColor, fg=self.LightPurple, font=(self.WordStyle, 12),
                            command=lambda: self.Thread_NULL(self.playPause))
        self.Button_pause.place(x=60, y=250)
        self.CreateToolTip(self.Button_pause, "暂停与恢复")

        self.Button_nextSong = tk.Button(self.screen, text='▶', width=3, bg=self.GlobalColor, fg=self.LightPurple, font=(self.WordStyle, 12),
                            command=lambda: self.Thread_NULL(self.nextSong))
        self.Button_nextSong.place(x=110, y=250)
        self.CreateToolTip(self.Button_nextSong, "下一首")

        # 调节音量
        self.Scale_Sound = tk.Scale(self.screen, from_=0, to=100, orient=tk.HORIZONTAL,
                     length=130, showvalue=False, resolution=10, command=self.playSetVolume) #tickinterval=20,self.playSetVolume
        self.Scale_Sound.set(100)
        self.Scale_Sound.place(x=420, y=250)
        self.CreateToolTip(self.Scale_Sound, "调节音量")

        # 东八区时间,即北京时间
        self.Label_UTC8 = tk.Label(self.screen, text="%s%d" % (
            datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S:'), datetime.datetime.now().microsecond // 100000), width=20,
                             height=1, bg=self.GlobalColor, fg='black', font=(self.WordStyle, 8))
        self.Label_UTC8.place(x=410, y=0)
        self.screen.after(100, self.uptime)

        #乐库路径
        self.Button_SongLibPath = tk.Button(self.screen, text='乐库路径', bg=self.GlobalColor, fg='black', font=(self.WordStyle, 8), 
            command=lambda: self.Thread_NULL(self.chooseSongsFloder))
        self.Button_SongLibPath.place(x=10, y=0)

        # 歌词LRC库路径
        self.Button_songRlcPath = tk.Button(self.screen, text='歌词路径', bg=self.GlobalColor, fg='black', font=(self.WordStyle, 8), 
            command=lambda: self.Thread_NULL(self.chooseRlcPath))
        self.Button_songRlcPath.place(x=80, y=0)

        # 更新乐库
        self.Button_updateLibrary = tk.Button(self.screen, text='更新乐库', bg=self.GlobalColor, fg='black', font=(self.WordStyle, 8), 
            command=lambda: self.Thread_NULL(self.updateSongs))
        self.Button_updateLibrary.place(x=150, y=0)

        # 随机播放
        self.Button_randomPlay = tk.Button(self.screen, text='∞', width=3, bg=self.GlobalColor, fg=self.LightPurple, font=(self.WordStyle, 12),
                            command=lambda: self.Thread_NULL(self.randomSong))
        self.Button_randomPlay.place(x=210, y=250)
        self.CreateToolTip(self.Button_randomPlay, "随机播放")

        # 显示歌曲数目
        self.Label_numbers = tk.Label(self.screen, bg=self.GlobalColor, fg='black', font=(self.WordStyle, 12), width=8)
        self.Label_numbers.place(x=475, y=480)

        # 单曲播放
        self.Button_cyclePlay = tk.Button(self.screen, text='①', width=3, bg=self.GlobalColor, fg=self.LightPurple, font=(self.WordStyle, 12),
                            command=lambda: self.Thread_NULL(self.cyclePlaySong))
        self.Button_cyclePlay.place(x=260, y=250)
        self.CreateToolTip(self.Button_cyclePlay, "单曲循环")

        # 统计音乐播放次数
        self.Button_CountSongs = tk.Button(self.screen, text='cnt', width=3, bg=self.GlobalColor, fg=self.LightPurple, font=(self.WordStyle, 12),
                            command=self.countSongInfo)
        self.Button_CountSongs.place(x=310, y=250)
        self.CreateToolTip(self.Button_CountSongs, "听歌汇")

        # 听歌历史痕迹
        self.Button_ListenHistory = tk.Button(self.screen, text='rec', width=3, bg=self.GlobalColor, fg=self.LightPurple, font=(self.WordStyle, 12),
                            command=self.listenHistory)
        self.Button_ListenHistory.place(x=360, y=250)
        self.CreateToolTip(self.Button_ListenHistory, "听歌历史")

        # 从主歌单中添加至待播放列表中
        self.Button_Add = tk.Button(self.screen, text='+', width=3, bg=self.GlobalColor, fg=self.LightPurple, font=(self.WordStyle, 12), relief=RAISED,
                            command=lambda: self.Thread_NULL(self.addSongs))
        self.Button_Add.place(x=10, y=520)
        self.CreateToolTip(self.Button_Add, "从主歌单中添加")

        # 从搜索列表中添加至待播放列表中
        self.Button_AddFind = tk.Button(self.screen, text='++', width=3, bg=self.GlobalColor, fg=self.LightPurple, font=(self.WordStyle, 12), relief=RAISED,
                            command=lambda: self.Thread_NULL(self.addFindSongs))
        self.Button_AddFind.place(x=60, y=520)
        self.CreateToolTip(self.Button_AddFind, "从搜索中添加")

        # 清空待播放列表
        self.Button_Clear = tk.Button(self.screen, text='∅', width=3, bg=self.GlobalColor, fg=self.LightPurple, font=(self.WordStyle, 12), relief=RAISED,
                            command=lambda: self.Thread_NULL(self.clearSongs))
        self.Button_Clear.place(x=110, y=520)
        self.CreateToolTip(self.Button_Clear, "清空添加歌单")

        # 播放搜索列表中的歌曲
        self.Button_FindPlay = tk.Button(self.screen, text='♪', width=3, bg=self.GlobalColor, fg=self.LightPurple, font=(self.WordStyle, 12), relief=RAISED,
                            command=lambda: self.Thread_NULL(self.playSong_find))
        self.Button_FindPlay.place(x=160, y=520)
        self.CreateToolTip(self.Button_FindPlay, "搜索播放")

        # 移除待播放列表的选中歌曲
        self.Button_RemoveSong = tk.Button(self.screen, text='×', width=3, bg=self.GlobalColor, fg=self.LightPurple, font=(self.WordStyle, 12), relief=RAISED,
                            command=lambda: self.Thread_NULL(self.removeSong))
        self.Button_RemoveSong.place(x=210, y=520)
        self.CreateToolTip(self.Button_RemoveSong, "删除添加歌曲")

        # 显示待播放列表中的歌曲
        self.Button_ShowSongs = tk.Button(self.screen, text='≡', width=3, bg=self.GlobalColor, fg=self.LightPurple, font=(self.WordStyle, 12), relief=RAISED,
                            command=lambda: self.Thread_NULL(self.showSongs))
        self.Button_ShowSongs.place(x=260, y=520)
        self.CreateToolTip(self.Button_ShowSongs, "列出添加歌曲")

        # 收藏的歌单
        self.Button_LoveOrders = tk.Button(self.screen, text='♥', width=3, bg=self.GlobalColor, fg=self.LightPurple, font=(self.WordStyle, 12), relief=RAISED,
                            command=lambda: self.Thread_NULL(self.loveSongs))
        self.Button_LoveOrders.place(x=310, y=520)
        self.CreateToolTip(self.Button_LoveOrders, "收藏歌单")

        # 读取选择中的收藏歌单
        self.Button_ReadOrders = tk.Button(self.screen, text='✔', width=3, bg=self.GlobalColor, fg=self.LightPurple, font=(self.WordStyle, 12), relief=RAISED,
                            command=lambda: self.Thread_NULL(self.readOrders))
        self.Button_ReadOrders.place(x=360, y=520)
        self.CreateToolTip(self.Button_ReadOrders, "读取歌单")

        self.insertSongs()

        self.screen.mainloop()

三、功能实现思路

3.1.播放功能
用 pygame.mixer.music.load()和pygame.mixer.music.play()来实现。
pygame.mixer.music.load() 传入歌曲的路径,如果路径有效pygame.mixer.music.play()就会播放。
play(loops=0, start=0.0) -> None
loops表示重复播放的次数,如play(3)重复播放3次,play(1)和play()表示播放1次,play(-1)表示无限循环播放。
start,表示播放的起始时间,这个参数在拖拽进度条时用到。

import pygame

pygame.mixer.init()		# 初始化
pygame.mixer.music.load(music_path)
pygame.mixer.music.play()

3.2.歌曲信息
播放音乐时,需要计算音乐的时长传给延时,因此导入mutagen库的MP3可以帮助我们获取到歌曲的时长信息,从而实现指定延时。

from mutagen.mp3 import MP3

songInfo = MP3(music_path)
songTimelong = songInfo.info.length
time.sleep(songTimelong) 

3.3.歌曲显示
通过tkinter的filedialog模块,打开歌曲文件夹,获取文件路径;
然后通过os模块,读取文件夹下面的文件,插入到主歌单Listbox中;

from tkinter import filedialog
import os

songDirPath = filedialog.askdiretory()
songList = os.listdir(songDirPath)
Listbox_songOrder.delete(0,tk.END)
for song in songList:
	if song.endwith(".mp3"):
		Listbox_songOrder.insert("end", song)

3.4.播放方式的实现思路
1) 选中歌曲,点击播放:可以通过Listbox.curselection()获取当前选中的歌曲,与文件夹路径拼接,就可以得到歌曲的绝对路径了。

2)通过获取当前歌曲的下标索引,然后对索引进行操作,可以先实现上一首,下一首,顺序播放了。

3)随机播放:通过Listbox.size()得到所有歌曲的数目,然后使用随机函数得到随机索引,song_id =random.randint(0,size-1),再通过Listbox.get(Listbox.index(song_id))得到歌曲,拼接为绝对路径。

4)单曲循环:获取选中歌曲的路径,使用pygame.mixer.music.play(-1),实现单曲循环。

3.5 信令与播放线程实现思路
个人理解线程:多线程好比不同时期或同时分布的多个任务,一个线程执行一个任务,如果没有信令去控制线程,那么这个线程直到任务执行完才结束。

在播放器里,如果没有信令控制线程,就会出现互相干扰的情况。比如,正在播放音乐,然后点了另一首歌曲播放,那么这两首歌曲都在播放,就会觉得很混乱,因为我想听的是最新点击的歌曲,前一首歌曲应该结束才对。因此当新点击一首歌曲时,需要信令去控制线程,让它结束前一首歌曲的播放。

因此,定义一个数字发生函数,每当点击歌曲播放时(实质是延时),就会产生一个新数字,姑且把这数字叫作信令,在这个新数字产生前播放的音乐权且叫作“旧歌曲”,相比“旧歌曲”,在最新点击的歌曲叫作“新歌曲”,那么“旧歌曲”的信令数字与最新的信令数字不匹配,那么我们就结束这个“旧歌曲”的播放任务。

	# 播放线程切换最新标志
	self.CHANGE_NEW = 0
	
	# 播放线程切换标志
	self.FLAGNO = 0

	# 线程标志号,每点一次播放都会触发一个新的标志号,用于区分线程
    def threadChangeFlag(self, *args):
        if self.FLAGNO >= 100:
            self.FLAGNO = 0
        else:
            self.FLAGNO += 1

        self.CHANGE_NEW = self.FLAGNO
        return self.FLAGNO

    # 自定义的线程函数类
    # 无参数传递的线程封装,按钮函数
    def Thread_NULL(self, func, *args):
        '''将函数放入线程中执行'''
        # 创建线程
        t = threading.Thread(target=func, args=args)
        # 守护线程
        t.setDaemon = True
        # 启动线程
        t.start()
        # 等待结束
        # t.join()

    # 有两个参数传递的线程封装,传递时长、最新信令
    def Thread_TWO(self, func, time, flagNo, *args):
        '''将函数放入线程中执行'''
        # 创建线程
        t = threading.Thread(target=func, args=(time, flagNo,))
        # 守护线程
        t.setDaemon = True
        # 启动线程
        t.start()
        # 等待结束
        # t.join()
        

3.6 定时器
或者叫时间计数器比较合适,这个其实没有必要写的,因为定位歌曲播放了多少时间,我们可以通过pygame.mixer.music.get_pos()来获取。这个在显示播放了多少时长,进度条显示进度,和同步歌词滚动时需要用到。
那么如果不用get_pos(),怎么实现定时器呢?见代码:

	'''
    # 定时器,时间计数发生器
    当开始播放时,启动计时,从零开始每秒加一;
    当播放暂停时,停止计时;
    当恢复播放时,恢复计时,每秒加一;
    signal 播放的线程信令
    playNo 播放的操作
    '''
    def timer(self, signal, playNo):
        while(signal == self.CHANGE_NEW):            
            if(playNo == self.PUASE or self.PUASEFLAG == True):
                self.TIME_CNT += 0
                
            elif(playNo == self.PLAY and self.PUASEFLAG == False):
                self.TIME_CNT += 1
            time.sleep(1)
            

3.7 歌词同步思路
同步的原理:lrc的歌词每行都包含时间和歌词(空行),当歌曲播放时间与歌词的时间一致时就显示该歌词。如下面就是一段lrc歌词。

[ar:刘珂矣]
[ti:半壶纱]
[00:00.41]刘珂矣 - 半壶纱
[00:01.52]作词:刘珂矣、百慕三石
[00:02.88]作曲:刘珂矣、百慕三石
[00:04.45]编曲:百慕三石
[00:26.21]墨已入水渡一池青花
[00:32.19]揽五分红霞采竹回家
[00:38.22]悠悠风来 埋一地桑麻
[00:44.38]一身袈裟 把相思放下
[00:50.45]十里桃花待嫁的年华
[00:56.57]凤冠的珍珠 挽进头发
[01:02.70]檀香拂过玉镯弄轻纱
[01:08.78]空留一盏 芽色的清茶
[01:14.96]倘若我心中的山水
[01:18.75]你眼中都看到
[01:22.24]我便一步一莲花祈祷
[01:27.15]怎知那浮生一片草
[01:31.30]岁月催人老

当然,就算get_pos()也不能得到与lyc歌词时间的一致时间,毫秒级别的不够精确。所以在读取歌词时间时,将歌词时间取整秒,定时器的时间计数也是整秒,按整秒去匹配歌词时间,从而实现歌词同步匹配。另外注重歌词毫秒是没有意义的,因为在看歌词和听歌,根本分辨不了毫秒的时间差。

另外,歌词显示比播放时间早一两秒比较合适,这样提前看到歌词,方便跟着看或跟着唱。

歌词滚动的思路:界面比较小,显示歌词的Listbox只有3行,因此当插入的行数大于3时,就删除第1行。可以用Listbox.size()获取插入的行数。

3.8 进度条思路
假设进度条的范围是0至100,那么通过get_pos()获取到播放时间,与歌曲时长的百分比就是进度条了。实现随着歌曲播放,进度条慢慢增涨。

3.9 收藏歌单思路
在歌单文件夹下,建文本歌单,在里面写下喜欢的歌曲,或根据歌单将歌曲分类。
那么os.listdir()就可以把歌单文件夹下的歌单列出来了,然后读取歌单,得到歌曲。

3.10 听歌记录
每播放一首歌曲时就把当前时间及歌名、时长,保存到一个log文本文件中。
方便统计听歌次数,听歌时长。当听歌一段时间后,回过头来看,最喜欢的一首歌竟然是这首,或许会惊讶不已,也许是理所当然。

ps: 当点击歌曲记录按钮或统计按钮时,会将记录数据插入到主列表中;想要回到主列表是歌曲的情形,只需点击上方的更新乐库即可。

3.11 临时列表
将选中的歌曲添加至临时列表中,或者移除。临时列表是一个列表,使用列表的方法就实现添加,移除等操作。

四、功能代码展示

下面是这个播放器的完整代码;

# encoding=utf-8

import tkinter as tk
from tkinter import *
from tkinter import filedialog
from tkinter import ttk
import os
import pygame
import time
import random
import math
import datetime
from mutagen.mp3 import MP3
import threading

class MusicPlayer():
    def __init__(self):
        # colors
        self.GlobalColor = '#F1E9D2'     # 羊皮纸颜色
        self.LightYellow = '#FFE57D'     # 浅黄色
        self.LightPurple = '#C748FA'     # 浅紫色

        self.COLOR_SIZE = 16             # 按钮数,点击时高亮   
        self.MyColors = [self.GlobalColor]*self.COLOR_SIZE

        self.WordStyle = '微软雅黑' 

        self.SWITCH = True      # 暂停/继续 的切换

        self.START = 1   # 启动播放
        self.PUASE = 2   # 暂停播放
        self.PLAY = 3    # 恢复播放
        self.STOP = 0
        self.EXIT = 0

        self.STEP = 0    # 拖拽进度条的时间步长

        # 暂停标志
        self.PUASEFLAG = False

        # 单曲循环标志
        self.CYCLEFLAG = False

        # 播放线程切换最新标志
        self.CHANGE_NEW = 0

        # 播放线程切换标志
        self.FLAGNO = 0

        # 计时器
        self.TIME_CNT = 0


        # 线程的退出标志
        # 0-上一首、1-下一首、2-顺序播放、3-随机播放、4-单曲循环、5-搜索播放、6-进度条播放
        self.flagNum = 7
        self.ExitFlag = [0]*self.flagNum

        self.OrientPath = r'D:\\Music\\CloudMusic\\歌曲'
        self.LrcPath = r'D:\\Music\\CloudMusic\\歌词'
        self.RecordPath = r'D:\\Music\\CloudMusic\\程序\\songsLog.txt'
        self.LoveSongsPath = r'D:\\Music\\CloudMusic\\歌单'
        self.SONGID = 0
        self.PATH = ''
        
        # 临时列表
        self.AddList = list()

        #音乐初始化
        pygame.mixer.init()

        self.screen = tk.Tk()
        self.screen.geometry('600x700')
        self.screen.title('本地音乐播放器')

        #滑动条
        self.Scroll_songOrder = tk.Scrollbar(self.screen)
        self.ListBox_songOrder = tk.Listbox(self.screen, width=61, height=10, bg=self.GlobalColor, fg='black', font=(self.WordStyle, 12),
                             yscrollcommand=self.Scroll_songOrder.set)
        #歌曲名列表框
        self.ListBox_songOrder.config(yscrollcommand=self.Scroll_songOrder.set)
        self.Scroll_songOrder.config(command=self.ListBox_songOrder.yview)
        
        #绑定滑动条
        self.ListBox_songOrder.place(x=10, y=290)
        self.Scroll_songOrder.pack(side='right', fill='both')

        # 显示正在播放
        self.Label_play = tk.Label(self.screen, height=1, width=10, text='正在播放:', bg=self.GlobalColor, fg='black', font=(self.WordStyle, 12))
        self.Label_play.place(x=10, y=30)

        #显示正在播放的歌曲
        self.StrngVar_songName = tk.StringVar()
        self.Label_songName = tk.Label(self.screen, width=45, height=1, fg='black', bg=self.GlobalColor, font=(self.WordStyle, 12),
                            textvariable=self.StrngVar_songName)
        self.Label_songName.place(x=100, y=30)

        #搜索框
        self.Entry_find = tk.Entry(self.screen, width=65, bg=self.GlobalColor, font=(self.WordStyle, 12))
        self.Entry_find.place(x=410, y=520, width=145, height=35)
        self.Entry_find.delete(0, tk.END)
        self.Entry_find.insert(0, "关键字搜索")
        self.Entry_find.bind('<Return>', self.findSong)

        #搜索的内容
        self.Listbox_find = tk.Listbox(self.screen, width=61, height=5, bg=self.GlobalColor, fg='black', font=(self.WordStyle, 12))
        self.Listbox_find.place(x=10, y=570)

        #歌词的内容
        self.Listbox_LRC = tk.Listbox(self.screen, width=61, height=3, bg=self.GlobalColor, fg='blue', font=(self.WordStyle, 12))
        self.Listbox_LRC.place(x=10, y=60)

        # 显示进度条
        self.ProgressBar = ttk.Progressbar(self.screen, length=430, mode="determinate", orient=HORIZONTAL)
        self.ProgressBar.place(x=130, y=140)
        self.ProgressBar["maximum"] = 99
        self.ProgressBar["value"] = 0
        self.CreateToolTip(self.ProgressBar, "显示进度条")
        
        # 更改进度条
        self.ProgressScale = tk.Scale(self.screen, from_=0, to=100, bd=1, orient=tk.HORIZONTAL,
                     length=430, showvalue=False, resolution=1, command=self.songProgressAdjust) #tickinterval=20
        self.ProgressScale.set(0)
        self.ProgressScale.place(x=130, y=190)
        self.CreateToolTip(self.ProgressScale, "拖拽进度条")
        

        self.Label_songTime = tk.Label(self.screen, text="00:00", width=10,height=1, bg="white", fg='black', font=(self.WordStyle, 12))
        self.Label_songTime.place(x=10, y=140)

        
        self.Label_offset = tk.Label(self.screen, text="offset:", width=10,height=1, bg="white", fg='black', font=(self.WordStyle, 12))
        self.Label_offset.place(x=10, y=190)
        
        self.Button_lastSong = tk.Button(self.screen, text='◀', width=3, bg=self.GlobalColor, fg=self.LightPurple, font=(self.WordStyle, 12), command=lambda: self.Thread_NULL(self.lastSong),
                            relief=tk.RAISED)
        self.Button_lastSong.place(x=10, y=250)
        self.CreateToolTip(self.Button_lastSong, "上一首")

        self.Button_playSong = tk.Button(self.screen, text='♫', width=3, bg=self.MyColors[0], fg=self.LightPurple, font=(self.WordStyle, 12),
                            command=lambda: self.Thread_NULL(self.playSongByOrder) )
        self.Button_playSong.place(x=160, y=250)
        self.CreateToolTip(self.Button_playSong, "播放音乐")

        self.Button_pause = tk.Button(self.screen, text='‖', width=3, bg=self.GlobalColor, fg=self.LightPurple, font=(self.WordStyle, 12),
                            command=lambda: self.Thread_NULL(self.playPause))
        self.Button_pause.place(x=60, y=250)
        self.CreateToolTip(self.Button_pause, "暂停与恢复")

        self.Button_nextSong = tk.Button(self.screen, text='▶', width=3, bg=self.GlobalColor, fg=self.LightPurple, font=(self.WordStyle, 12),
                            command=lambda: self.Thread_NULL(self.nextSong))
        self.Button_nextSong.place(x=110, y=250)
        self.CreateToolTip(self.Button_nextSong, "下一首")

        # 调节音量
        self.Scale_Sound = tk.Scale(self.screen, from_=0, to=100, orient=tk.HORIZONTAL,
                     length=130, showvalue=False, resolution=10, command=self.playSetVolume) #tickinterval=20,self.playSetVolume
        self.Scale_Sound.set(100)
        self.Scale_Sound.place(x=420, y=250)
        self.CreateToolTip(self.Scale_Sound, "调节音量")

        # 东八区时间,即北京时间
        self.Label_UTC8 = tk.Label(self.screen, text="%s%d" % (
            datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S:'), datetime.datetime.now().microsecond // 100000), width=20,
                             height=1, bg=self.GlobalColor, fg='black', font=(self.WordStyle, 8))
        self.Label_UTC8.place(x=410, y=0)
        self.screen.after(100, self.uptime)

        #乐库路径
        self.Button_SongLibPath = tk.Button(self.screen, text='乐库路径', bg=self.GlobalColor, fg='black', font=(self.WordStyle, 8), 
            command=lambda: self.Thread_NULL(self.chooseSongsFloder))
        self.Button_SongLibPath.place(x=10, y=0)

        # 歌词LRC库路径
        self.Button_songRlcPath = tk.Button(self.screen, text='歌词路径', bg=self.GlobalColor, fg='black', font=(self.WordStyle, 8), 
            command=lambda: self.Thread_NULL(self.chooseLrcPath))
        self.Button_songRlcPath.place(x=80, y=0)

        # 更新乐库
        self.Button_updateLibrary = tk.Button(self.screen, text='更新乐库', bg=self.GlobalColor, fg='black', font=(self.WordStyle, 8), 
            command=lambda: self.Thread_NULL(self.updateSongs))
        self.Button_updateLibrary.place(x=150, y=0)

        # 随机播放
        self.Button_randomPlay = tk.Button(self.screen, text='∞', width=3, bg=self.GlobalColor, fg=self.LightPurple, font=(self.WordStyle, 12),
                            command=lambda: self.Thread_NULL(self.randomSong))
        self.Button_randomPlay.place(x=210, y=250)
        self.CreateToolTip(self.Button_randomPlay, "随机播放")

        # 显示歌曲数目
        self.Label_numbers = tk.Label(self.screen, bg=self.GlobalColor, fg='black', font=(self.WordStyle, 12), width=8)
        self.Label_numbers.place(x=475, y=480)

        # 单曲播放
        self.Button_cyclePlay = tk.Button(self.screen, text='①', width=3, bg=self.GlobalColor, fg=self.LightPurple, font=(self.WordStyle, 12),
                            command=lambda: self.Thread_NULL(self.cyclePlaySong))
        self.Button_cyclePlay.place(x=260, y=250)
        self.CreateToolTip(self.Button_cyclePlay, "单曲循环")

        # 统计音乐播放次数
        self.Button_CountSongs = tk.Button(self.screen, text='cnt', width=3, bg=self.GlobalColor, fg=self.LightPurple, font=(self.WordStyle, 12),
                            command=self.countSongInfo)
        self.Button_CountSongs.place(x=310, y=250)
        self.CreateToolTip(self.Button_CountSongs, "听歌汇")

        # 听歌历史痕迹
        self.Button_ListenHistory = tk.Button(self.screen, text='rec', width=3, bg=self.GlobalColor, fg=self.LightPurple, font=(self.WordStyle, 12),
                            command=self.listenHistory)
        self.Button_ListenHistory.place(x=360, y=250)
        self.CreateToolTip(self.Button_ListenHistory, "听歌历史")

        # 从主歌单中添加至待播放列表中
        self.Button_Add = tk.Button(self.screen, text='+', width=3, bg=self.GlobalColor, fg=self.LightPurple, font=(self.WordStyle, 12), relief=RAISED,
                            command=lambda: self.Thread_NULL(self.addSongs))
        self.Button_Add.place(x=10, y=520)
        self.CreateToolTip(self.Button_Add, "从主歌单中添加")

        # 从搜索列表中添加至待播放列表中
        self.Button_AddFind = tk.Button(self.screen, text='++', width=3, bg=self.GlobalColor, fg=self.LightPurple, font=(self.WordStyle, 12), relief=RAISED,
                            command=lambda: self.Thread_NULL(self.addFindSongs))
        self.Button_AddFind.place(x=60, y=520)
        self.CreateToolTip(self.Button_AddFind, "从搜索中添加")

        # 清空待播放列表
        self.Button_Clear = tk.Button(self.screen, text='∅', width=3, bg=self.GlobalColor, fg=self.LightPurple, font=(self.WordStyle, 12), relief=RAISED,
                            command=lambda: self.Thread_NULL(self.clearSongs))
        self.Button_Clear.place(x=110, y=520)
        self.CreateToolTip(self.Button_Clear, "清空添加歌单")

        # 播放搜索列表中的歌曲
        self.Button_FindPlay = tk.Button(self.screen, text='♪', width=3, bg=self.GlobalColor, fg=self.LightPurple, font=(self.WordStyle, 12), relief=RAISED,
                            command=lambda: self.Thread_NULL(self.playSong_find))
        self.Button_FindPlay.place(x=160, y=520)
        self.CreateToolTip(self.Button_FindPlay, "搜索播放")

        # 移除待播放列表的选中歌曲
        self.Button_RemoveSong = tk.Button(self.screen, text='×', width=3, bg=self.GlobalColor, fg=self.LightPurple, font=(self.WordStyle, 12), relief=RAISED,
                            command=lambda: self.Thread_NULL(self.removeSong))
        self.Button_RemoveSong.place(x=210, y=520)
        self.CreateToolTip(self.Button_RemoveSong, "删除添加歌曲")

        # 显示待播放列表中的歌曲
        self.Button_ShowSongs = tk.Button(self.screen, text='≡', width=3, bg=self.GlobalColor, fg=self.LightPurple, font=(self.WordStyle, 12), relief=RAISED,
                            command=lambda: self.Thread_NULL(self.showSongs))
        self.Button_ShowSongs.place(x=260, y=520)
        self.CreateToolTip(self.Button_ShowSongs, "列出添加歌曲")

        # 收藏的歌单
        self.Button_LoveOrders = tk.Button(self.screen, text='♥', width=3, bg=self.GlobalColor, fg=self.LightPurple, font=(self.WordStyle, 12), relief=RAISED,
                            command=lambda: self.Thread_NULL(self.loveSongs))
        self.Button_LoveOrders.place(x=310, y=520)
        self.CreateToolTip(self.Button_LoveOrders, "歌单")

        # 读取选择中的收藏歌单
        self.Button_ReadOrders = tk.Button(self.screen, text='✔', width=3, bg=self.GlobalColor, fg=self.LightPurple, font=(self.WordStyle, 12), relief=RAISED,
                            command=lambda: self.Thread_NULL(self.readOrders))
        self.Button_ReadOrders.place(x=360, y=520)
        self.CreateToolTip(self.Button_ReadOrders, "读取歌单")

        self.insertSongs()

        self.screen.mainloop()

    # 线程标志号,每点一次播放都会触发一个新的标志号,用于区分线程
    def threadChangeFlag(self, *args):
        if self.FLAGNO >= 100:
            self.FLAGNO = 0
        else:
            self.FLAGNO += 1

        self.CHANGE_NEW = self.FLAGNO
        return self.FLAGNO

    # 自定义的线程函数类
    # 无参数传递的线程封装,按钮函数
    def Thread_NULL(self, func, *args):
        '''将函数放入线程中执行'''
        # 创建线程
        t = threading.Thread(target=func, args=args)
        # 守护线程
        t.setDaemon = True
        # 启动线程
        t.start()
        # 等待结束
        # t.join()


    # 有两个参数传递的线程封装,进度条、滚动歌词等函数
    def Thread_TWO(self, func, time, flagNo, *args):
        '''将函数放入线程中执行'''
        # 创建线程
        t = threading.Thread(target=func, args=(time, flagNo,))
        # 守护线程
        t.setDaemon = True
        # 启动线程
        t.start()
        # 等待结束
        # t.join()

    
    '''
    # 定时器,时间计数发生器
    当开始播放时,启动计时,从零开始每秒加一;
    当播放暂停时,停止计时;
    当恢复播放时,恢复计时,每秒加一;
    signal 播放的线程信号量
    playNo 播放的操作
    '''
    def timer(self, signal, playNo):

        while(signal == self.CHANGE_NEW):            
            if(playNo == self.PUASE or self.PUASEFLAG == True):
                self.TIME_CNT += 0;
                
            elif(playNo == self.PLAY and self.PUASEFLAG == False):
                self.TIME_CNT += 1;
            # print(f"TIME {self.TIME_CNT}")   
            time.sleep(1)

    # 播放记录   
    def songPlayRecord(self, name, timelong, *args):
        time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S ')
        info = f"{time}播放 {name} 时长:{round(timelong,2)}秒.\n"
        print(info)
        with open(self.RecordPath,"a+") as f:
            f.write(info)
            f.close()

    # 播放函数的封装
    def playFunction(self, exitFlg):

        if(exitFlg == self.ExitFlag[5]):
            listBox = self.Listbox_find
        else:
            listBox = self.ListBox_songOrder

        if(exitFlg == self.ExitFlag[2]) or (exitFlg == self.ExitFlag[5]):
            self.SONGID = listBox.index(listBox.curselection())
            self.PATH = self.OrientPath + '\\' + listBox.get(listBox.curselection()) + '.mp3'
            self.StrngVar_songName.set(listBox.get(listBox.curselection()))
        else:
            self.PATH = self.OrientPath + '\\' + listBox.get(self.SONGID) + '.mp3'
            self.StrngVar_songName.set(listBox.get(self.SONGID))
     
        pygame.mixer.music.load(self.PATH)
        pygame.mixer.music.set_volume(100)
        pygame.mixer.music.play()
        
        
        self.Label_numbers.config(text=str(self.SONGID + 1) + '/' + str(listBox.size()))
        self.Button_pause.config(text='‖', bg = self.GlobalColor)
        listBox.selection_clear(0, 'end')
        listBox.selection_set(self.SONGID)
        listBox.see(self.SONGID)

        songInfo = MP3(self.PATH)
        timelong = songInfo.info.length
        self.ProgressScale.config(from_ = 0, to = timelong)
        self.ProgressScale.set(0)
        self.Label_offset['text'] = "00:00"
        self.songPlayRecord(listBox.get(self.SONGID), timelong)
        self.TIME_CNT = 0
        self.Thread_TWO( self.timer, exitFlg, self.PLAY)
        self.Thread_TWO( self.songProgressbar, timelong, exitFlg)
        self.Thread_TWO( self.showLRC, timelong, exitFlg)
        
        if( self.PUASEFLAG == True or self.CYCLEFLAG == True):
            return self.EXIT
        if(exitFlg != self.CHANGE_NEW):
            return self.EXIT
        time.sleep(int(timelong))
        self.screen.update()

        while(exitFlg == self.CHANGE_NEW):
            try:
                
                if ( self.PUASEFLAG == True or self.CYCLEFLAG == True):
                    return self.EXIT
                if(exitFlg == self.ExitFlag[3]):
                    self.SONGID = random.randint(0, listBox.size() - 1)
                
                else:
                    if(self.SONGID < listBox.size()-1):
                        self.SONGID += 1
                    else:
                        self.SONGID = 0
         
                self.PATH = self.OrientPath + '\\' + listBox.get(self.SONGID) + '.mp3'
                self.StrngVar_songName.set(listBox.get(self.SONGID))
                
                pygame.mixer.music.load(self.PATH)
                pygame.mixer.music.set_volume(100)
                pygame.mixer.music.play()
                
                self.Label_numbers.config(text=str(self.SONGID + 1) + '/' + str(listBox.size()))
                self.Button_pause.config(text='‖', bg = self.GlobalColor)
                listBox.selection_clear(0, 'end')
                listBox.selection_set(self.SONGID)
                listBox.see(self.SONGID)

                songInfo = MP3(self.PATH)
                timelong = songInfo.info.length
                self.songPlayRecord(listBox.get(self.SONGID), timelong)
                self.STEP = 0
                self.ProgressScale.config(from_ = 0, to = timelong)
                self.ProgressScale.set(0)
                self.Label_offset['text'] = "00:00"
                self.Thread_TWO( self.songProgressbar, timelong, exitFlg)
                self.Thread_TWO( self.showLRC, timelong, exitFlg)
                self.TIME_CNT = 0
                
                time.sleep(int(timelong))
                self.screen.update()
                
            except:
                break

    # 顺序播放
    def playSongByOrder(self, *args):

        self.SWITCH = True
        self.PUASEFLAG = False
        self.CYCLEFLAG = False
        self.STEP = 0
        self.setColor(3)
        self.ExitFlag[2] = self.threadChangeFlag()
        exitFlg = self.ExitFlag[2]
        if(exitFlg != self.CHANGE_NEW):
            return self.EXIT
        
        self.playFunction(exitFlg)

        

    # 单曲循环播放
    def cyclePlaySong(self, *args):

        self.SWITCH = True
        self.PUASEFLAG = False
        self.CYCLEFLAG = True
        self.STEP = 0
        self.setColor(5)

        self.PATH = self.OrientPath + '\\' + self.ListBox_songOrder.get(self.ListBox_songOrder.curselection()) + '.mp3'
        self.StrngVar_songName.set(self.ListBox_songOrder.get(self.ListBox_songOrder.curselection()))
        self.ExitFlag[4] = self.threadChangeFlag()
        if(self.ExitFlag[4] != self.CHANGE_NEW):
            return self.EXIT
        pygame.mixer.music.load(self.PATH)
        pygame.mixer.music.set_volume(100)
        pygame.mixer.music.play(-1)
        
        self.SONGID = self.ListBox_songOrder.index(self.ListBox_songOrder.curselection())
        self.Label_numbers.config(text=str(self.SONGID + 1) + '/' + str(self.ListBox_songOrder.size()))
        self.Button_pause.config(text='‖', bg = self.GlobalColor)
        self.ListBox_songOrder.selection_clear(0, 'end')
        self.ListBox_songOrder.selection_set(self.SONGID)
        self.ListBox_songOrder.see(self.SONGID)

        songInfo = MP3(self.PATH)
        timelong = songInfo.info.length
        self.songPlayRecord(self.ListBox_songOrder.get(self.ListBox_songOrder.curselection()), timelong)
        self.ProgressScale.config(from_ = 0, to = timelong)
        self.ProgressScale.set(0)
        self.Label_offset['text'] = "00:00"
        self.TIME_CNT = 0
        self.Thread_TWO( self.timer, self.ExitFlag[4], self.PLAY)
        self.Thread_TWO( self.songProgressbar, timelong, self.ExitFlag[4])
        self.Thread_TWO( self.showLRC, timelong, self.ExitFlag[4])
        if( self.PUASEFLAG == True or self.CYCLEFLAG == True):
            return self.EXIT

    # 下一首歌曲
    def nextSong(self, *args):

        self.SWITCH = True
        self.PUASEFLAG = False
        self.CYCLEFLAG = False
        self.setColor(2)
        self.ExitFlag[1] = self.threadChangeFlag()
        exitFlg = self.ExitFlag[1]
        if(exitFlg != self.CHANGE_NEW):
            return self.EXIT

        if self.SONGID < self.ListBox_songOrder.size()-1:
            self.SONGID += 1
        else:
            self.SONGID = 0
        self.playFunction(exitFlg)
        

    # 上一首歌曲
    def lastSong(self, *args):

        self.SWITCH = True
        self.PUASEFLAG = False
        self.setColor(0)
        self.ExitFlag[0] = self.threadChangeFlag()
        exitFlg = self.ExitFlag[0]
        if(exitFlg != self.CHANGE_NEW):
            return self.EXIT
        
        if self.SONGID > 0:
            self.SONGID -= 1
        else:
            self.SONGID = self.ListBox_songOrder.size()-1
        self.playFunction(exitFlg)

    # 暂停播放
    def playPause(self, *args):

        self.CYCLEFLAG = False
        self.setColor(1)

        if self.SWITCH:
            self.SWITCH = False
            self.PUASEFLAG = True
            self.Button_pause.config(text='■', bg = self.LightYellow)
            pygame.mixer.music.pause()
            
        else:
            self.SWITCH = True
            self.PUASEFLAG = False
            self.Button_pause.config(text='‖', bg = self.GlobalColor)
            pygame.mixer.music.unpause()

    # 随机播放函数
    def randomSong(self, *args):

        self.SWITCH = True
        self.PUASEFLAG = False
        self.CYCLEFLAG = False
        self.setColor(4)
        self.ExitFlag[3] = self.threadChangeFlag()
        exitFlg = self.ExitFlag[3]
        if(exitFlg != self.CHANGE_NEW):
            return self.EXIT
        
        self.SONGID = random.randint(0, self.ListBox_songOrder.size() - 1)
        self.playFunction(exitFlg)

    # 关键字搜索歌曲
    def findSong(self, *args):

        self.SWITCH = True
        flag = False

        find = self.Entry_find.get()
        songList1 = os.listdir(self.OrientPath)
        songList2 = list()
        self.Listbox_find.delete(0, tk.END)
        for song in songList1:
            if song[-4:] == '.mp3' and (find.casefold() in song.casefold()):
                songList2.append(song)
                flag = True
        if(flag):
            for one in songList2:
                self.Listbox_find.insert('end', one[:-4])

    # 播放搜索框音乐
    def playSong_find(self, *args):

        self.SWITCH = True
        self.PUASEFLAG = False
        self.CYCLEFLAG = False
        self.setColor(11)
        self.ExitFlag[5] = self.threadChangeFlag()        # 播放方式之间的线程的区分
        exitFlg = self.ExitFlag[5]                # 同种播放方式,多首音乐之间的区分
        
        if(exitFlg != self.CHANGE_NEW):
            return self.EXIT

        self.playFunction(exitFlg)

        
    # 从主歌单列表从添加喜欢的歌曲
    def addSongs(self, *args):
        self.setColor(8)
        try:  
            self.AddList.append(self.ListBox_songOrder.get(self.ListBox_songOrder.curselection()))
        except:
            print("anchor error!")
        self.Listbox_find.delete(0, tk.END)
        for song in self.AddList:
            self.Listbox_find.insert("end", song)

    # 从搜索列表中添加歌曲
    def addFindSongs(self, *args):
        self.setColor(9)
        try:
             self.AddList.append(self.Listbox_find.get(self.Listbox_find.curselection()))
        except:
            print("anchor error!")
        self.Listbox_find.delete(0, tk.END)
        for song in self.AddList:
            self.Listbox_find.insert("end", song)

    # 清空添加歌曲的列表
    def clearSongs(self, *args):
        self.AddList = []
        self.setColor(10)
        self.Listbox_find.delete(0, tk.END)

    # 移除添加歌曲的列表
    def removeSong(self, *args):
        self.setColor(12)
        self.AddList.remove(self.Listbox_find.get(self.Listbox_find.curselection()))
        self.Listbox_find.delete(0, tk.END)
        for song in self.AddList:
            self.Listbox_find.insert("end", song)

    # 列出添加歌曲
    def showSongs(self, *args):
        self.setColor(13)
        self.Listbox_find.delete(0, tk.END)
        for song in self.AddList:
            self.Listbox_find.insert("end", song)

    # 列出喜欢的歌单
    def loveSongs(self, *args):
        self.setColor(14)
        try:
            songOrders = os.listdir(self.LoveSongsPath)
            self.Listbox_find.delete(0,tk.END)
            for order in songOrders:
                order = order[:-4].strip()
                if(len(order)>0):
                    self.Listbox_find.insert("end", order)
        except:
            pass

    #读取收藏的歌单
    def readOrders(self, *args):
        self.setColor(15)
        try:
            choosrOrderPath = self.LoveSongsPath + '\\' + self.Listbox_find.get(self.Listbox_find.curselection()) + '.txt'
            self.Listbox_find.delete(0,tk.END)
            with open(choosrOrderPath,"r+") as f:
                lines = f.readlines()
                for line in lines:
                    line = line.strip()
                    if len(line) > 0:
                        self.Listbox_find.insert("end", line)
                f.close()
        except:
            pass


    # 调节音量大小
    def playSetVolume(self, voice):
        voice = float(voice)/100.0
        pygame.mixer.music.set_volume(float(voice))

 
    # 从歌曲文件夹中读取歌曲
    def insertSongs(self):
        songList1 = os.listdir(self.OrientPath)
        songList2 = list()
        self.ListBox_songOrder.delete(0, tk.END)
        for song in songList1:
            if song[-4:] == '.mp3':
                songList2.append(song)
        songList2.sort()
        for one in songList2:
            self.ListBox_songOrder.insert('end', one[:-4])
        self.Label_numbers.config(text=self.ListBox_songOrder.size())

    # 选中乐库路径
    def chooseSongsFloder(self, *args):
        self.SWITCH = True
        try:
            self.OrientPath = filedialog.askdirectory()
            self.insertSongs()
        except:
            self.OrientPath = r'D:\\Music\\CloudMusic\\歌曲'


    # 更新乐库
    def updateSongs(self, *args):
        self.SWITCH = True
        songList1 = os.listdir(self.OrientPath)
        songList2 = list()
        self.ListBox_songOrder.delete(0, tk.END)
        for song in songList1:
            if song[-4:] == '.mp3':
                songList2.append(song)
        songList2.sort()
        for one in songList2:
            self.ListBox_songOrder.insert('end', one[:-4])
        self.Label_numbers.config(text=self.ListBox_songOrder.size())

    
    # 获取每行记录中的歌名
    def get_song(self, string):
        
        start = string.index("播放")
        end = string.index("时长")
        name = string[start+2:end-1]

        return name

    # 获取每行记录中的时长
    def get_time(self, string):
        
        start = string.index("时长")
        end = len(string) - string[::-1].index("秒")
        time = string[start+3:end-1]

        return time

    # 读取听歌记录
    def readtxt(self):
        songList = []
        timeList = []
        filetxt = "songsLog.txt"
        with open(filetxt, "r+") as f:
            lines = f.readlines()
            for line in lines:
                songName = self.get_song(line)
                songList.append(songName)
                songTime = float(self.get_time(line))
                timeList.append(songTime)

        return songList,timeList

    # 将听歌记录的所有歌曲去重,以字典的键值对返回,初始化听歌次数为0
    def countInit(self):
        songDict = dict()
        songs,times = self.readtxt()
        
        songTuple = tuple(songs)
        for v in songTuple:
            songDict[v] = 0
        return songDict

    # 统计听歌记录里的歌曲听歌次数,及总时长
    def countSongs(self):
        songDict = self.countInit()
        songList,timeList = self.readtxt()
        totalTime = sum(timeList)
        for song_L in songList:
            for song_D,song_N in songDict.items():
                if song_L == song_D:
                    song_N += 1
                    songDict[song_D] = song_N

        return songDict,totalTime


    # 听歌汇总
    def countSongInfo(self, *args):
        self.setColor(6)
        songDict,totalTime = self.countSongs()
        sortDict = dict(sorted(songDict.items(), key = lambda x:x[1], reverse = True))
        total = sum(songDict.values())
        totalTime = int(totalTime)
        h = totalTime // 3600
        m = (totalTime - h*3600) // 60
        s = totalTime - h*3600 - m*60
        self.ListBox_songOrder.delete(0, tk.END)

        info1 = f" "*20 + "听歌汇报"
        info2 = " 听歌时长 {} 秒,等于 {} 小时 {:.2f} 分钟 {:.2f} 秒.".format(totalTime,h,m,s)
        self.ListBox_songOrder.insert('end', info1)
        self.ListBox_songOrder.insert('end', info2)
        self.ListBox_songOrder.insert('end', " ")
        for k,v in sortDict.items():
            rate = (v/total)*100
            rate = round(rate,2)
            info = f"{k}    播放了 {v} 次"
            self.ListBox_songOrder.insert('end', info)
        self.Label_numbers.config(text='')
        self.Label_numbers.pack_forget()

    # 听歌历史
    def listenHistory(self, *args):
        self.setColor(7)
        try:
            songList = []
            filetxt = "songsLog.txt"
            self.ListBox_songOrder.delete(0, tk.END)
            with open(filetxt, "r+") as f:
                lines = f.readlines()
                lines.reverse()
                for line in lines:
                    self.ListBox_songOrder.insert('end', line)
            self.Label_numbers.config(text='')
            self.Label_numbers.pack_forget()
        except:
            pass

    # 显示北京时间
    def showtime(self, *args):
        self.Label_UTC8.config(text=time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())))


    # 显示时间
    def uptime(self):
            self.Label_UTC8["text"] = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S:') + "%d" % (
                    datetime.datetime.now().microsecond // 100000)
            self.screen.after(100, self.uptime)


    # 颜色变换函数,button的背景颜色
    def setColorWay(self, way):
        for i in range(self.COLOR_SIZE):
            if way == i:
                self.MyColors[i] = self.LightYellow
            else:
                self.MyColors[i] = self.GlobalColor
       

    # button颜色变换封装
    def buttonColor(self):
        self.Button_lastSong.config( bg=self.MyColors[0])
        self.Button_pause.config( bg=self.MyColors[1])
        self.Button_nextSong.config( bg=self.MyColors[2])
        self.Button_playSong.config( bg=self.MyColors[3])
        self.Button_randomPlay.config( bg=self.MyColors[4])
        self.Button_cyclePlay.config( bg=self.MyColors[5])
        self.Button_CountSongs.config( bg=self.MyColors[6])
        self.Button_ListenHistory.config( bg=self.MyColors[7])
        self.Button_Add.config( bg=self.MyColors[8])
        self.Button_AddFind.config( bg=self.MyColors[9])
        self.Button_Clear.config( bg=self.MyColors[10])
        self.Button_FindPlay.config( bg=self.MyColors[11])
        self.Button_RemoveSong.config( bg=self.MyColors[12])
        self.Button_ShowSongs.config( bg=self.MyColors[13])
        self.Button_LoveOrders.config( bg=self.MyColors[14])
        self.Button_ReadOrders.config( bg=self.MyColors[15])

    # 按钮被点击时变色,封装为一个函数
    def setColor(self, num):
        self.setColorWay(num)
        self.buttonColor()


    # 秒数转分钟数
    def SecondToMinute(self, seconds):
        round(seconds)
        m = int(seconds // 60)
        s = int(seconds % 60)
        if(m<10):
            m = "0"+str(m)
        if(s<10):
            s = "0"+str(s)
        return m,s

    # 进度条函数
    def songProgressbar(self, timelong, flagNo):

        time = 0
        rate = 0
        m2,s2 = self.SecondToMinute(timelong)

        # flagNo == self.CHANGE_NEW 确保点击播放歌曲时的线程不会冲突
        # rate <= 98 确保上一首歌曲播放完,退出进度条
        while (flagNo == self.CHANGE_NEW and  rate <= 98):
            try:
                time = (pygame.mixer.music.get_pos()) / 1000 + self.STEP
                time %= timelong 
                rate = round((time / timelong),4)*100
                self.ProgressBar["value"] = rate
                self.screen.update()
                m1,s1 = self.SecondToMinute(time)
                self.Label_songTime["text"] = "{}:{}/{}:{}".format(m1, s1, m2, s2)

                self.screen.after(100)
            except:
                break

        # self.CYCLEFLAG == True 确保单曲循环时,可以重复启动进度条
        while (flagNo == self.CHANGE_NEW and  self.CYCLEFLAG == True):
            try:
                time = (pygame.mixer.music.get_pos()) / 1000 + self.STEP
                time %= timelong 
                rate = round((time / timelong),4)*100
                self.ProgressBar["value"] = rate
                self.screen.update()
                m1,s1 = self.SecondToMinute(time)
                self.Label_songTime["text"] = "{}:{}/{}:{}".format(m1, s1, m2, s2)
                self.screen.after(100)
            except:
                break

    def progressNextAdjust(self, seconds, flagNo, *args):

        self.SWITCH = True
        songInfo = MP3(self.PATH)
        timelong = int(songInfo.info.length)
        time.sleep(timelong - seconds)

        while(flagNo == self.CHANGE_NEW ):
            try:
                
                if ( self.PUASEFLAG == True or self.CYCLEFLAG == True):
                    return self.EXIT
                
                if(self.SONGID < self.ListBox_songOrder.size()-1):
                    self.SONGID += 1
                else:
                    self.SONGID = 0
         
                self.PATH = self.OrientPath + '\\' + self.ListBox_songOrder.get(self.SONGID) + '.mp3'
                self.StrngVar_songName.set(self.ListBox_songOrder.get(self.SONGID))
                
                pygame.mixer.music.load(self.PATH)
                pygame.mixer.music.set_volume(100)
                pygame.mixer.music.play()
                
                self.Label_numbers.config(text=str(self.SONGID + 1) + '/' + str(self.ListBox_songOrder.size()))
                self.Button_pause.config(text='‖', bg = self.GlobalColor)

                songInfo = MP3(self.PATH)
                timelong = songInfo.info.length
                self.songPlayRecord(self.ListBox_songOrder.get(self.SONGID), timelong)
                self.STEP = 0
                self.ProgressScale.config(from_ = 0, to = timelong)
                self.ProgressScale.set(0)
                self.Label_offset['text'] = "00:00"
                self.Thread_TWO( self.songProgressbar, timelong, flagNo)
                self.Thread_TWO( self.showLRC, timelong, flagNo)
                time.sleep(int(timelong))
                self.screen.update()
                
            except:
                break

    def songProgressAdjust(self, seconds, *args):

        self.SWITCH = True

        seconds = int(seconds)
        self.STEP = seconds
        
        self.ExitFlag[6] = self.threadChangeFlag()
        clearFlag = self.ExitFlag[6]
        songInfo = MP3(self.PATH)
        timelong = int(songInfo.info.length)
        self.ProgressScale.config(from_ = 0, to = timelong)
        m1,s1 = self.SecondToMinute(seconds)
        self.Label_offset["text"] = "{}:{}".format(m1, s1)
        self.TIME_CNT = 0
        self.Thread_TWO( self.timer, self.ExitFlag[6], self.PLAY)
        self.Thread_TWO( self.songProgressbar, timelong, self.ExitFlag[6])
        self.Thread_TWO( self.showLRC, timelong, self.ExitFlag[6])
        
        pygame.mixer.music.play(1, seconds)

        self.Thread_TWO(self.progressNextAdjust, seconds, self.ExitFlag[6])
        

    def SongLrcProgress(self, filePath, timelong):
        timeList = []
        wordList = []

        with open(filePath,"r+", encoding = "utf-8") as f:
            linelists = f.readlines()
            for line in linelists:
                if("ar:" in line or "ti:" in line):
                    continue
                if("]" in line):
                    index = line.index("]")
                    stime = line[1:index]
                    word =  line[index+1:-1]

                    keytime = stime.split(":")
                    realtime = int(float(keytime[0])*60 + float(keytime[1]))
                    if(realtime < timelong):
                        timeList.append(realtime)
                        wordList.append(word)
        return timeList,wordList

    def chooseLrcPath(self, *args):
        try:
            self.LrcPath = filedialog.askdirectory()
        except:
            self.LrcPath = r'D:\\Music\\CloudMusic\\歌词'

    def showLRC(self, timelong, flagNo, *args):

        self.TIME_CNT = 0
        songList = []
        timeList = []
        wordList = []
        
        word = " "
        oldword = " "
        insertFlg = 1

        # 本地歌曲在酷狗音乐软件播放后会在酷狗目录下生成krc文件,手动通过krc2lrc软件转换成lrc文件
        #self.LrcPath = r'D:\\Music\\CloudMusic\\Lyric'

        self.Listbox_LRC.delete(0, tk.END)

        songOrderPath = os.listdir(self.LrcPath)
        for file in songOrderPath:
            if file.endswith(".lrc") or file.endswith(".txt"):
                songList.append(file)

        songName = self.Label_songName["text"]
        for song in songList:
            if( songName == song[:-4]):
                songtxtPath = self.LrcPath + '\\' + str(song)
                timeList,wordList = self.SongLrcProgress(songtxtPath, timelong)

        if(len(timeList) == 0):
            self.Listbox_LRC.insert("end", "    ")
            self.Listbox_LRC.insert("end", "    "*5 + "纯音乐,请欣赏!")
            self.Listbox_LRC.insert("end", "    ")
            return self.EXIT
       

        songTime =  self.TIME_CNT 
        while(flagNo == self.CHANGE_NEW  and  self.CYCLEFLAG == False 
                and songTime < (timeList[-1]-1) and songTime < timelong):
                
            songTime = self.TIME_CNT
            for i in range(len(timeList)):
                if(songTime+1 == timeList[i]):
                    word = wordList[i]
                
                if(oldword != word):
                    insertFlg = 1

                if(insertFlg == 1):
                    oldword = word         
                    self.Listbox_LRC.insert("end", "    "*4+word)
                    if (self.Listbox_LRC.size() > 3):
                        self.Listbox_LRC.delete(0)
                    insertFlg = 0
            
            time.sleep(1)

        #单曲循环
        while(flagNo == self.CHANGE_NEW and  self.CYCLEFLAG == True 
                and songTime < (timeList[-1]-1) and songTime < timelong ):
                
            songTime = self.TIME_CNT
            songTime %= int(timelong)
            for i in range(len(timeList)):
                if(songTime+1 == timeList[i]):
                    word = wordList[i]
                
                if(oldword != word):
                    insertFlg = 1

                if(insertFlg == 1):
                    oldword = word          
                    self.Listbox_LRC.insert("end", "    "*4+word)
                    if (self.Listbox_LRC.size() > 3):
                        self.Listbox_LRC.delete(0)
                    insertFlg = 0
            
            time.sleep(1)

        
    #创建该控件的函数
    """
    第一个参数:是定义的控件的名称
    第二个参数,是要显示的文字信息
    """
    def CreateToolTip(self, widget, text):
        toolTip = ToolTip(widget)
        def enter(event):
            toolTip.showtip(text)
        def leave(event):
            toolTip.hidetip()
        widget.bind('<Enter>', enter)
        widget.bind('<Leave>', leave)



#对提示信息控件的定义
class ToolTip(object):

    def __init__(self, widget):
        self.widget = widget
        self.tipwindow = None
        self.id = None
        self.x = self.y = 0
    #当光标移动指定控件是显示消息
    def showtip(self, text):
        "Display text in tooltip window"
        self.text = text
        if self.tipwindow or not self.text:
            return
        x, y, cx, cy = self.widget.bbox("insert")
        x = x + self.widget.winfo_rootx()+30
        y = y + cy + self.widget.winfo_rooty()+30
        self.tipwindow = tw = Toplevel(self.widget)
        tw.wm_overrideredirect(True)
        tw.wm_geometry("+%d+%d" % (x, y))
        label = Label(tw, text=self.text,justify=LEFT,
                      background="white", relief=SOLID, borderwidth=1,
                      font=("雅黑", "10"))
        label.pack(side=BOTTOM)
    #当光标移开时提示消息隐藏
    def hidetip(self):
        tw = self.tipwindow
        self.tipwindow = None
        if tw:
            tw.destroy()

      
if __name__=='__main__':
    musicApp = MusicPlayer()







  • 9
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值