tkinter音乐播放器

介绍

最近闲的没事,在学习tkiner,在网上没有找到合适的音乐播放器,找到的太简单,也不符合我的审美,所以利用自己所学自己写一个美观的。

先放个成果图, 感觉还不错,虽然比较简单
在这里插入图片描述

创建窗口

这是使用ttkbootstrap实现的,自带的tkinter虽然功能多,但是太丑了,就是10年前的风格是在受不了,它是基于tkinter出的一个优化库,所以也算是用tkinter做的吧

安装ttkbootstrap

pip install ttkbootstrap

由于tkinter无法读取音乐,所以需要一个处理mp3文件的库,在网上找了一圈就pygame比较合适,其他的不是停止更新就是不支持Python3,真是麻烦
pygame的功能也不是很多,本来是想用mp3play,功能比较全,但是他不支持Python3,没办法,只能凑合用pygame

安装 pygame

pip install pygame

介绍一下pygame的音乐模块

这个是在网上找的pygame中文文档

在这里插入图片描述

背景的实现

使用canvas布局,并使用pack()进行自适应布局

        self.frame = Frame(self.root)
        self.frame.pack(side=LEFT, anchor=CENTER, fill=BOTH, expand=YES)
        self.canvas = Canvas(self.frame)

        self.canvas.pack(side=LEFT, anchor=CENTER, fill=BOTH, expand=YES)
        self.canvas.bind('<Configure>', lambda event: self.bj_canvas(event))

等比例缩放图片

由于不知道填充方式该怎么弄,只能等比例缩放

from PIL import Image


def image_resize(image, win_size):
    """
    等比例缩放图片
    :param image:
    :param win_size:
    :return:
    """
    screen_width, screen_height = win_size
    raw_width, raw_height = image.size[0], image.size[1]
    max_width, max_height = raw_width, screen_height
    min_width = max(raw_width, max_width)
    # 按照比例缩放
    min_height = int(raw_height * min_width / raw_width)
    # 第1次快速调整
    while min_height > screen_height:
        min_height = int(min_height * .9533)
    # 第2次精确微调
    while min_height < screen_height:
        min_height += 1
    # 按照比例缩放
    min_width = int(raw_width * min_height / raw_height)
    # 适应性调整
    while min_width > screen_width:
        min_width -= 1
    # 按照比例缩放
    min_height = int(raw_height * min_width / raw_width)
    return image.resize((min_width, min_height), Image.Resampling.LANCZOS)

按钮

使用bind方法动态调整窗口大小

    def get_win_size(self):
        self.root.update()
        return (self.root.winfo_screenwidth() - 1000) // 2, (
                self.root.winfo_screenheight() - 700) // 2

    def bj_canvas(self, event):
        """按钮"""
        global bj_png, list_image, on_image, next_image, suspended_image, restore_image, high_volume_image
        self.w, self.h = event.width, event.height
        bj_png = ImageTk.PhotoImage(image_resize(image=bg_png, win_size=(self.w, self.h)))
        self.canvas.create_image(self.w // 2, self.h // 2, image=bj_png)

        self.create_progress_meter()

        self.c_text = self.canvas.create_text(self.w // 2, self.h // 2, text='歌曲', font=('黑体', 30), fill='#771a25')

        # 文件夹
        dir_image = ImageTk.PhotoImage(dir_png.resize(size=(50, 50)))
        self.ldir_image = self.canvas.create_image(self.w // 2 - 400, self.h - 100, image=dir_image)
        self.dir_image = dir_image

        # 列表
        list_image = ImageTk.PhotoImage(list_png.resize(size=(50, 50)))
        self.list_image = self.canvas.create_image(self.w // 2 - 300, self.h - 100, image=list_image)
        self.llist_image = list_image

        # 上一首
        on_image = ImageTk.PhotoImage(on_png.resize(size=(50, 50)))
        self.lon_image = self.canvas.create_image(self.w // 2 - 200, self.h - 100, image=on_image)
        self.on_image = on_image

        # 暂停
        suspended_image = ImageTk.PhotoImage(suspended_png.resize(size=(50, 50)))
        self.lsuspended_image = self.canvas.create_image(self.w // 2 - 100, self.h - 100, image=suspended_image)
        self.suspended_image = suspended_image

        # 继续
        restore_image = ImageTk.PhotoImage(restore_png.resize(size=(50, 50)))
        self.lrestore_image = self.canvas.create_image(self.w // 2, self.h - 100, image=restore_image)
        self.restore_image = restore_image

        # 下一首
        next_image = ImageTk.PhotoImage(next_png.resize(size=(50, 50)))
        self.lnext_image = self.canvas.create_image(self.w // 2 + 100, self.h - 100, image=next_image)
        self.next_image = next_image

        # 音量
        high_volume_image = ImageTk.PhotoImage(high_volume.resize(size=(50, 50)))
        self.lhigh_volume_image = self.canvas.create_image(self.w // 2 + 200, self.h - 100, image=high_volume_image)
        self.high_volume_image = high_volume_image
        self.volume_frame()

        # 关联鼠标经过事件
        self.canvas.bind('<Motion>', lambda event: self.motion(event))
        # 关联鼠标点击事件
        self.canvas.bind('<Button-1>', lambda event: self.buttons(event))
       

按钮效果

    def motion(self, event):
        global new_dir_image, new_list_image, new_on_image, new_suspended_image, new_restore_image, new_next_image, new_high_volume_image
        self.canvas.update()
        w, h = self.canvas.winfo_width(), self.canvas.winfo_height()

        # 打开文件
        if w // 2 - 432 <= event.x <= w // 2 - 368 and h - 132 <= event.y <= h - 68:
            new_dir_image = ImageTk.PhotoImage(dir_png.resize(size=(60, 60)))
            self.canvas.itemconfigure(self.ldir_image, image=new_dir_image)
        # 列表
        elif w // 2 - 332 <= event.x <= w // 2 - 268 and h - 132 <= event.y <= h - 68:
            new_list_image = ImageTk.PhotoImage(list_png.resize(size=(60, 60)))
            self.canvas.itemconfigure(self.list_image, image=new_list_image)
        # 上一首
        elif w // 2 - 232 <= event.x <= w // 2 - 168 and h - 132 <= event.y <= h - 68:
            new_on_image = ImageTk.PhotoImage(on_png.resize(size=(60, 60)))
            self.canvas.itemconfigure(self.lon_image, image=new_on_image)
        # 暂停
        elif w // 2 - 132 <= event.x <= w // 2 - 68 and h - 132 <= event.y <= h - 68:
            new_suspended_image = ImageTk.PhotoImage(suspended_png.resize(size=(60, 60)))
            self.canvas.itemconfigure(self.lsuspended_image, image=new_suspended_image)
        # 继续
        elif w // 2 - 32 <= event.x <= w // 2 + 32 and h - 132 <= event.y <= h - 68:
            new_restore_image = ImageTk.PhotoImage(restore_png.resize(size=(60, 60)))
            self.canvas.itemconfigure(self.lrestore_image, image=new_restore_image)
        # 下一首
        elif w // 2 + 68 <= event.x <= w // 2 + 132 and h - 132 <= event.y <= h - 68:
            new_next_image = ImageTk.PhotoImage(next_png.resize(size=(60, 60)))
            self.canvas.itemconfigure(self.lnext_image, image=new_next_image)
        # 音量
        elif w // 2 + 168 <= event.x <= w // 2 + 232 and h - 132 <= event.y <= h - 68:
            new_high_volume_image = ImageTk.PhotoImage(high_volume.resize(size=(60, 60)))
            self.canvas.itemconfigure(self.lhigh_volume_image, image=new_high_volume_image)
        else:
            self.canvas.itemconfigure(self.ldir_image, image=self.dir_image)
            self.canvas.itemconfigure(self.list_image, image=self.llist_image)
            self.canvas.itemconfigure(self.lon_image, image=self.on_image)
            self.canvas.itemconfigure(self.lsuspended_image, image=self.suspended_image)
            self.canvas.itemconfigure(self.lrestore_image, image=self.restore_image)
            self.canvas.itemconfigure(self.lnext_image, image=self.next_image)
            self.canvas.itemconfigure(self.lhigh_volume_image, image=self.high_volume_image)

按钮事件

   def buttons(self, event):
        self.canvas.update()
        w, h = self.canvas.winfo_width(), self.canvas.winfo_height()
        # 打开文件
        if w // 2 - 432 <= event.x <= w // 2 - 368 and h - 132 <= event.y <= h - 68:
            music_file = askdirectory()
            self.get_music(music_file)
        # 列表
        elif w // 2 - 332 <= event.x <= w // 2 - 268 and h - 132 <= event.y <= h - 68:
            if len(self.music_list) == 0:
                Messagebox.show_error(title='错误', message='没有找到数据!!!',
                                      position=(self.root.winfo_screenwidth() // 2 - 100,
                                                self.root.winfo_screenheight() // 2 - 50))
            else:
                toplevel = Toplevel(title='播放列表', size=(250, 400), iconphoto='./ico/列表.png', alpha=0.8)
                toplevel.geometry(
                    f'250x400+{self.root.winfo_screenwidth() // 2 - 100}+{self.root.winfo_screenheight() // 2 - 150}')

                self.tv = Treeview(toplevel, show=HEADINGS, columns=[0], height=5)
                self.vs = Scrollbar(toplevel, orient=VERTICAL, command=self.tv.yview)
                self.vs.pack(side=RIGHT, fill=Y, expand=YES)
                self.tv.pack(side=LEFT, anchor=CENTER, fill=BOTH, expand=YES)
                self.tv.heading(0, text='歌曲名')
                for name in self.music_list:
                    self.tv.insert('', 'end', values=(name.split('\\')[-1].split('.')[0], name))
                self.tv.bind('<Double-1>', handler_adaptor(self.tv_com))
        # 上一首
        elif w // 2 - 232 <= event.x <= w // 2 - 168 and h - 132 <= event.y <= h - 68:
            self.next_music(-1)
        # 暂停
        elif w // 2 - 132 <= event.x <= w // 2 - 68 and h - 132 <= event.y <= h - 68:
            mixer.music.pause()
        # 继续
        elif w // 2 - 32 <= event.x <= w // 2 + 32 and h - 132 <= event.y <= h - 68:
            mixer.music.unpause()
        # 下一首
        elif w // 2 + 68 <= event.x <= w // 2 + 132 and h - 132 <= event.y <= h - 68:
            self.next_music(1)
        # 音量
        elif w // 2 + 168 <= event.x <= w // 2 + 232 and h - 132 <= event.y <= h - 68:
            pass
        else:
            pass

获取文件夹中的所有mp3文件

使用一个迭代器来获取

    def get_music(self, path):
        """获取文件夹中的所有mp3文件"""
        for file in glob(path + '/*'):
            if os.path.isfile(file):
                if os.path.splitext(file)[-1] in ['.mp3']:
                    self.music_list.append(file)
            else:
                self.get_music(file)

声音设置

    def volume_setting(self, event):
        """声音设置"""
        num = float(format(self.scale1.get(), '.1f'))
        mixer.music.set_volume(num)

开始播放音乐

    def play(self, music_path): 
        # 开始播放音乐
        mixer.music.set_volume(self.scale1.get())
        mixer.music.load(music_path)
        mixer.music.play()

音乐切换

列表音乐切换

    def tv_com(self, event):
        """列表音乐切换"""
        mixer.music.stop()
        col1 = event.widget.item(event.widget.selection()[0], 'values')[0]
        col2 = event.widget.item(event.widget.selection()[0], 'values')[1]
        self.music_num = self.music_list.index(col2)
        self.canvas.itemconfigure(self.c_text, text=col1, font=('黑体', 15))
        self.play(col2)
        try:
            stop_thread(self.th1)
        except Exception:
            pass
        self.changeMP3(col2)

上一首/下一首 音乐切换


    def next_music(self, num):
        """
        音乐切换
        :param num:
        :return:
        """
        self.music_num = self.music_num + num
        if self.music_num <= 0:
            mixer.music.stop()
            self.music_num = len(self.music_list) - 1
            self.canvas.itemconfigure(self.c_text, text=self.music_list[self.music_num].split('\\')[-1].split('.')[0],
                                      font=('黑体', 15))
            self.play(self.music_list[self.music_num])
            try:
                stop_thread(self.th1)
            except Exception:
                pass
            self.changeMP3(self.music_list[self.music_num])
        elif self.music_num >= len(self.music_list) - 1:
            mixer.music.stop()
            self.music_num = 0
            self.canvas.itemconfigure(self.c_text, text=self.music_list[self.music_num].split('\\')[-1].split('.')[0],
                                      font=('黑体', 15))
            self.play(self.music_list[self.music_num])
            try:
                stop_thread(self.th1)
            except Exception:
                pass
            self.changeMP3(self.music_list[self.music_num])
        else:
            mixer.music.stop()
            self.canvas.itemconfigure(self.c_text, text=self.music_list[self.music_num].split('\\')[-1].split('.')[0],
                                      font=('黑体', 15))
            self.play(self.music_list[self.music_num])
            try:
                stop_thread(self.th1)
            except Exception:
                pass
            self.changeMP3(self.music_list[self.music_num])

创建进度条模块

    def create_progress_meter(self):
        """
        创建进度条模块
        :return:
        """
        container = Frame(self.canvas)
        container.place(x=20, y=self.h - 160, width=self.w - 40)

        self.elapse = Label(container, text='00:00')
        self.elapse.pack(side=LEFT, padx=10)

        self.scale = Scale(
            master=container,
            command=self.on_progress,
            style=SECONDARY
        )
        self.scale.pack(side=LEFT, fill=X, expand=YES)

        self.remain = Label(container, text='00:00')
        self.remain.pack(side=LEFT, fill=X, padx=10)

更新量表时更新进度标签

    def on_progress(self, val: float):
        """
        更新量表时更新进度标签。
        :param val:
        :return:
        """
        mixer.music.set_pos(self.scale.get())

声音模块

    def volume_frame(self):
        """
        声音模块
        :return:
        """
        global add_png, s_png
        frame = Frame(self.canvas, width=200)
        frame.place(x=self.w // 2 + 230, y=self.h - 110)

        add_png = ImageTk.PhotoImage(subtraction_volume.resize(size=(10, 10)))
        button1 = Button(frame, style='success-link', image=add_png, command=lambda: self.volume_add_or_s(-0.1))
        button1.pack(anchor=N, fill=X, side=LEFT, expand=1)

        self.scale1 = Scale(frame, from_=0, to=1, length=200, orient=HORIZONTAL, style=SECONDARY,
                            command=self.volume_setting)
        self.scale1.set(value=self.volume_num)

        self.scale1.pack(anchor=N, fill=BOTH, side=LEFT, expand=1)

        s_png = ImageTk.PhotoImage(add_volume.resize(size=(10, 10)))
        button2 = Button(frame, style='success-link', image=s_png, command=lambda: self.volume_add_or_s(0.1))
        button2.pack(anchor=N, fill=X, side=LEFT, expand=1)```
### Button修改时间

```python
    def volume_add_or_s(self, num):
        # Button修改时间
        self.volume_num = float(format(self.scale1.get(), '.1f'))
        if 0.0 <= self.volume_num <= 1.0:
            self.volume_num = self.volume_num + num
            self.scale1.set(value=self.volume_num)
            mixer.music.set_volume(self.volume_num)
        elif 1.0 < self.volume_num <= 1.1:
            self.volume_num = 1
        elif self.volume_num < 0:
            self.volume_num = 0
        else:
            pass

音乐进度条的实现

由于tkinter默认是单线程,while 循环会占用tkinter的进程,造成卡退的情况,因此需要创建新的线程来解决这个问题

    def changeMP3(self, path):
        # 初始化
        mp3_size = int(MP3(path).info.length)
        self.remain.configure(text=f'{mp3_size // 60:02d}:{mp3_size % 60:02d}')
        self.scale.configure(to=mp3_size)
        self.th1 = threading.Thread(target=self.CMp3, args=(mp3_size,))
        self.th1.start()

    def CMp3(self, mp3_size):
        # 更改进度条左侧/右侧的数据
        while mixer.music.get_busy():
            sleep(1)
            new = mixer.music.get_pos() // 1000
            self.scale.set(new)
            self.elapse.configure(text=f'{new // 60:02d}:{new % 60:02d}')
            self.remain.configure(text=f'{(mp3_size - new) // 60:02d}:{(mp3_size - new) % 60:02d}')

关闭线程

在上边实现音乐进度条后,在切换音乐时会重复创建线程,因此需要一个关闭线程后,重新打开线程的一个操作,这是关闭线程的一个函数

def _async_raise(tid, exctype):
    """raises the exception, performs cleanup if needed"""
    tid = ctypes.c_long(tid)
    if not inspect.isclass(exctype):
        exctype = type(exctype)
    res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(exctype))
    if res == 0:
        raise ValueError("invalid thread id")
    elif res != 1:
        # """if it returns a number greater than one, you're in trouble,
        # and you should call it again with exc=NULL to revert the effect"""
        ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, None)
        raise SystemError("PyThreadState_SetAsyncExc failed")


def stop_thread(thread):
    _async_raise(thread.ident, SystemExit)

中介函数

中介函数,在command调用函数时会出现值不够的情况,或者参数无法传递,这时需要一个中介,对参数与函数进行处理

def handler_adaptor(fun, **kwds):
    """事件处理函数的适配器,相当于中介"""
    return lambda event, fun=fun, kwds=kwds: fun(event, **kwds)

最后附上完成的代码吧

附件连接
tkinter-music

conf里都是图标连接
from tools.img.imgresize import image_resize是上面的等比例缩放图片
附件里函数CMp3()少写了个self.scale.set(new),在new下面添加就好
没有做循环播放等功能,音乐播放完毕后无法切换下一首

from time import sleep
from mutagen.mp3 import MP3
from pygame import mixer
from tkinter.filedialog import askdirectory
from conf import *
from tools.img.imgresize import image_resize
from ttkbootstrap import *
from glob import glob
from ttkbootstrap.dialogs import Messagebox
import threading
import inspect
import ctypes


def _async_raise(tid, exctype):
    """raises the exception, performs cleanup if needed"""
    tid = ctypes.c_long(tid)
    if not inspect.isclass(exctype):
        exctype = type(exctype)
    res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(exctype))
    if res == 0:
        raise ValueError("invalid thread id")
    elif res != 1:
        # """if it returns a number greater than one, you're in trouble,
        # and you should call it again with exc=NULL to revert the effect"""
        ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, None)
        raise SystemError("PyThreadState_SetAsyncExc failed")


def stop_thread(thread):
    _async_raise(thread.ident, SystemExit)


def handler_adaptor(fun, **kwds):
    """事件处理函数的适配器,相当于中介"""
    return lambda event, fun=fun, kwds=kwds: fun(event, **kwds)


class MusicWindows:
    def __init__(self):
        self.root = Window(size=(1000, 700), resizable=(False, False))
        w, h = self.get_win_size()
        self.root.geometry(f'1000x700+{w}+{h}')

        mixer.init()
        self.music_list = []
        self.volume_num = 0.5
        self.music_num = 0

        self.frame = Frame(self.root)
        self.frame.pack(side=LEFT, anchor=CENTER, fill=BOTH, expand=YES)
        self.canvas = Canvas(self.frame)

        self.canvas.pack(side=LEFT, anchor=CENTER, fill=BOTH, expand=YES)
        self.canvas.bind('<Configure>', lambda event: self.bj_canvas(event))

        self.root.mainloop()

    def get_win_size(self):
        self.root.update()
        return (self.root.winfo_screenwidth() - 1000) // 2, (
                self.root.winfo_screenheight() - 700) // 2

    def bj_canvas(self, event):
        """按钮"""
        global bj_png, list_image, on_image, next_image, suspended_image, restore_image, high_volume_image
        self.w, self.h = event.width, event.height
        bj_png = ImageTk.PhotoImage(image_resize(image=bg_png, win_size=(self.w, self.h)))
        self.canvas.create_image(self.w // 2, self.h // 2, image=bj_png)

        self.create_progress_meter()

        self.c_text = self.canvas.create_text(self.w // 2, self.h // 2, text='歌曲', font=('黑体', 30), fill='#771a25')

        # 文件夹
        dir_image = ImageTk.PhotoImage(dir_png.resize(size=(50, 50)))
        self.ldir_image = self.canvas.create_image(self.w // 2 - 400, self.h - 100, image=dir_image)
        self.dir_image = dir_image

        # 列表
        list_image = ImageTk.PhotoImage(list_png.resize(size=(50, 50)))
        self.list_image = self.canvas.create_image(self.w // 2 - 300, self.h - 100, image=list_image)
        self.llist_image = list_image

        # 上一首
        on_image = ImageTk.PhotoImage(on_png.resize(size=(50, 50)))
        self.lon_image = self.canvas.create_image(self.w // 2 - 200, self.h - 100, image=on_image)
        self.on_image = on_image

        # 暂停
        suspended_image = ImageTk.PhotoImage(suspended_png.resize(size=(50, 50)))
        self.lsuspended_image = self.canvas.create_image(self.w // 2 - 100, self.h - 100, image=suspended_image)
        self.suspended_image = suspended_image

        # 继续
        restore_image = ImageTk.PhotoImage(restore_png.resize(size=(50, 50)))
        self.lrestore_image = self.canvas.create_image(self.w // 2, self.h - 100, image=restore_image)
        self.restore_image = restore_image

        # 下一首
        next_image = ImageTk.PhotoImage(next_png.resize(size=(50, 50)))
        self.lnext_image = self.canvas.create_image(self.w // 2 + 100, self.h - 100, image=next_image)
        self.next_image = next_image

        # 音量
        high_volume_image = ImageTk.PhotoImage(high_volume.resize(size=(50, 50)))
        self.lhigh_volume_image = self.canvas.create_image(self.w // 2 + 200, self.h - 100, image=high_volume_image)
        self.high_volume_image = high_volume_image
        self.volume_frame()

        # 关联鼠标经过事件
        self.canvas.bind('<Motion>', lambda event: self.motion(event))
        # 关联鼠标点击事件
        self.canvas.bind('<Button-1>', lambda event: self.buttons(event))

    def motion(self, event):
        global new_dir_image, new_list_image, new_on_image, new_suspended_image, new_restore_image, new_next_image, new_high_volume_image
        self.canvas.update()
        w, h = self.canvas.winfo_width(), self.canvas.winfo_height()

        # 打开文件
        if w // 2 - 432 <= event.x <= w // 2 - 368 and h - 132 <= event.y <= h - 68:
            new_dir_image = ImageTk.PhotoImage(dir_png.resize(size=(60, 60)))
            self.canvas.itemconfigure(self.ldir_image, image=new_dir_image)
        # 列表
        elif w // 2 - 332 <= event.x <= w // 2 - 268 and h - 132 <= event.y <= h - 68:
            new_list_image = ImageTk.PhotoImage(list_png.resize(size=(60, 60)))
            self.canvas.itemconfigure(self.list_image, image=new_list_image)
        # 上一首
        elif w // 2 - 232 <= event.x <= w // 2 - 168 and h - 132 <= event.y <= h - 68:
            new_on_image = ImageTk.PhotoImage(on_png.resize(size=(60, 60)))
            self.canvas.itemconfigure(self.lon_image, image=new_on_image)
        # 暂停
        elif w // 2 - 132 <= event.x <= w // 2 - 68 and h - 132 <= event.y <= h - 68:
            new_suspended_image = ImageTk.PhotoImage(suspended_png.resize(size=(60, 60)))
            self.canvas.itemconfigure(self.lsuspended_image, image=new_suspended_image)
        # 继续
        elif w // 2 - 32 <= event.x <= w // 2 + 32 and h - 132 <= event.y <= h - 68:
            new_restore_image = ImageTk.PhotoImage(restore_png.resize(size=(60, 60)))
            self.canvas.itemconfigure(self.lrestore_image, image=new_restore_image)
        # 下一首
        elif w // 2 + 68 <= event.x <= w // 2 + 132 and h - 132 <= event.y <= h - 68:
            new_next_image = ImageTk.PhotoImage(next_png.resize(size=(60, 60)))
            self.canvas.itemconfigure(self.lnext_image, image=new_next_image)
        # 音量
        elif w // 2 + 168 <= event.x <= w // 2 + 232 and h - 132 <= event.y <= h - 68:
            new_high_volume_image = ImageTk.PhotoImage(high_volume.resize(size=(60, 60)))
            self.canvas.itemconfigure(self.lhigh_volume_image, image=new_high_volume_image)
        else:
            self.canvas.itemconfigure(self.ldir_image, image=self.dir_image)
            self.canvas.itemconfigure(self.list_image, image=self.llist_image)
            self.canvas.itemconfigure(self.lon_image, image=self.on_image)
            self.canvas.itemconfigure(self.lsuspended_image, image=self.suspended_image)
            self.canvas.itemconfigure(self.lrestore_image, image=self.restore_image)
            self.canvas.itemconfigure(self.lnext_image, image=self.next_image)
            self.canvas.itemconfigure(self.lhigh_volume_image, image=self.high_volume_image)

    def buttons(self, event):
        self.canvas.update()
        w, h = self.canvas.winfo_width(), self.canvas.winfo_height()
        # 打开文件
        if w // 2 - 432 <= event.x <= w // 2 - 368 and h - 132 <= event.y <= h - 68:
            music_file = askdirectory()
            self.get_music(music_file)
        # 列表
        elif w // 2 - 332 <= event.x <= w // 2 - 268 and h - 132 <= event.y <= h - 68:
            if len(self.music_list) == 0:
                Messagebox.show_error(title='错误', message='没有找到数据!!!',
                                      position=(self.root.winfo_screenwidth() // 2 - 100,
                                                self.root.winfo_screenheight() // 2 - 50))
            else:
                toplevel = Toplevel(title='播放列表', size=(250, 400), iconphoto='./ico/列表.png', alpha=0.8)
                toplevel.geometry(
                    f'250x400+{self.root.winfo_screenwidth() // 2 - 100}+{self.root.winfo_screenheight() // 2 - 150}')

                self.tv = Treeview(toplevel, show=HEADINGS, columns=[0], height=5)
                self.vs = Scrollbar(toplevel, orient=VERTICAL, command=self.tv.yview)
                self.vs.pack(side=RIGHT, fill=Y, expand=YES)
                self.tv.pack(side=LEFT, anchor=CENTER, fill=BOTH, expand=YES)
                self.tv.heading(0, text='歌曲名')
                for name in self.music_list:
                    self.tv.insert('', 'end', values=(name.split('\\')[-1].split('.')[0], name))
                self.tv.bind('<Double-1>', handler_adaptor(self.tv_com))
        # 上一首
        elif w // 2 - 232 <= event.x <= w // 2 - 168 and h - 132 <= event.y <= h - 68:
            self.next_music(-1)
        # 暂停
        elif w // 2 - 132 <= event.x <= w // 2 - 68 and h - 132 <= event.y <= h - 68:
            mixer.music.pause()
        # 继续
        elif w // 2 - 32 <= event.x <= w // 2 + 32 and h - 132 <= event.y <= h - 68:
            mixer.music.unpause()
        # 下一首
        elif w // 2 + 68 <= event.x <= w // 2 + 132 and h - 132 <= event.y <= h - 68:
            self.next_music(1)
        # 音量
        elif w // 2 + 168 <= event.x <= w // 2 + 232 and h - 132 <= event.y <= h - 68:
            pass
        else:
            pass

    def get_music(self, path):
        """获取文件夹中的所有mp3文件"""
        for file in glob(path + '/*'):
            if os.path.isfile(file):
                if os.path.splitext(file)[-1] in ['.mp3']:
                    self.music_list.append(file)
            else:
                self.get_music(file)

    def volume_setting(self, event):
        """声音设置"""
        num = float(format(self.scale1.get(), '.1f'))
        mixer.music.set_volume(num)

    def play(self, music_path):
        # 开始播放音乐
        mixer.music.set_volume(self.scale1.get())
        mixer.music.load(music_path)
        mixer.music.play()

    def tv_com(self, event):
        """列表音乐切换"""
        mixer.music.stop()
        col1 = event.widget.item(event.widget.selection()[0], 'values')[0]
        col2 = event.widget.item(event.widget.selection()[0], 'values')[1]
        self.music_num = self.music_list.index(col2)
        self.canvas.itemconfigure(self.c_text, text=col1, font=('黑体', 15))
        self.play(col2)
        try:
            stop_thread(self.th1)
        except Exception:
            pass
        self.changeMP3(col2)

    def next_music(self, num):
        """
        音乐切换
        :param num:
        :return:
        """
        self.music_num = self.music_num + num
        if self.music_num <= 0:
            mixer.music.stop()
            self.music_num = len(self.music_list) - 1
            self.canvas.itemconfigure(self.c_text, text=self.music_list[self.music_num].split('\\')[-1].split('.')[0],
                                      font=('黑体', 15))
            self.play(self.music_list[self.music_num])
            try:
                stop_thread(self.th1)
            except Exception:
                pass
            self.changeMP3(self.music_list[self.music_num])
        elif self.music_num >= len(self.music_list) - 1:
            mixer.music.stop()
            self.music_num = 0
            self.canvas.itemconfigure(self.c_text, text=self.music_list[self.music_num].split('\\')[-1].split('.')[0],
                                      font=('黑体', 15))
            self.play(self.music_list[self.music_num])
            try:
                stop_thread(self.th1)
            except Exception:
                pass
            self.changeMP3(self.music_list[self.music_num])
        else:
            mixer.music.stop()
            self.canvas.itemconfigure(self.c_text, text=self.music_list[self.music_num].split('\\')[-1].split('.')[0],
                                      font=('黑体', 15))
            self.play(self.music_list[self.music_num])
            try:
                stop_thread(self.th1)
            except Exception:
                pass
            self.changeMP3(self.music_list[self.music_num])

    def create_progress_meter(self):
        """
        创建进度条模块
        :return:
        """
        container = Frame(self.canvas)
        container.place(x=20, y=self.h - 160, width=self.w - 40)

        self.elapse = Label(container, text='00:00')
        self.elapse.pack(side=LEFT, padx=10)

        self.scale = Scale(
            master=container,
            command=self.on_progress,
            style=SECONDARY
        )
        self.scale.pack(side=LEFT, fill=X, expand=YES)

        self.remain = Label(container, text='00:00')
        self.remain.pack(side=LEFT, fill=X, padx=10)

    def on_progress(self, val: float):
        """
        更新量表时更新进度标签。
        :param val:
        :return:
        """
        mixer.music.set_pos(self.scale.get())

    def volume_frame(self):
        """
        声音模块
        :return:
        """
        global add_png, s_png
        frame = Frame(self.canvas, width=200)
        frame.place(x=self.w // 2 + 230, y=self.h - 110)

        add_png = ImageTk.PhotoImage(subtraction_volume.resize(size=(10, 10)))
        button1 = Button(frame, style='success-link', image=add_png, command=lambda: self.volume_add_or_s(-0.1))
        button1.pack(anchor=N, fill=X, side=LEFT, expand=1)

        self.scale1 = Scale(frame, from_=0, to=1, length=200, orient=HORIZONTAL, style=SECONDARY,
                            command=self.volume_setting)
        self.scale1.set(value=self.volume_num)

        self.scale1.pack(anchor=N, fill=BOTH, side=LEFT, expand=1)

        s_png = ImageTk.PhotoImage(add_volume.resize(size=(10, 10)))
        button2 = Button(frame, style='success-link', image=s_png, command=lambda: self.volume_add_or_s(0.1))
        button2.pack(anchor=N, fill=X, side=LEFT, expand=1)

    def volume_add_or_s(self, num):
        # Button修改时间
        self.volume_num = float(format(self.scale1.get(), '.1f'))
        if 0.0 <= self.volume_num <= 1.0:
            self.volume_num = self.volume_num + num
            self.scale1.set(value=self.volume_num)
            mixer.music.set_volume(self.volume_num)
        elif 1.0 < self.volume_num <= 1.1:
            self.volume_num = 1
        elif self.volume_num < 0:
            self.volume_num = 0
        else:
            pass

    def changeMP3(self, path):
        # 初始化
        mp3_size = int(MP3(path).info.length)
        self.remain.configure(text=f'{mp3_size // 60:02d}:{mp3_size % 60:02d}')
        self.scale.configure(to=mp3_size)
        self.th1 = threading.Thread(target=self.CMp3, args=(mp3_size,))
        self.th1.start()

    def CMp3(self, mp3_size):
        # 更改进度条左侧/右侧的数据
        while mixer.music.get_busy():
            sleep(1)
            new = mixer.music.get_pos() // 1000
            self.scale.set(new)
            self.elapse.configure(text=f'{new // 60:02d}:{new % 60:02d}')
            self.remain.configure(text=f'{(mp3_size - new) // 60:02d}:{(mp3_size - new) % 60:02d}')


if __name__ == '__main__':
    MusicWindows()

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值