Python GUI编程:音乐播放器(多线程、爬虫、进度条、文件)

news2024/11/15 21:33:34

文章目录

        • 1. 程序运行结果
        • 2.程序实现原理
        • 3. GUI布局
        • 4. 功能介绍
        • 5. 代码实现

1. 程序运行结果

Python实现音乐播放器(爬虫、多线程、进度条、文件)

2.程序实现原理

本音乐播放器GUI方面运用Python的tkinter实现,播放的音乐来自网络爬虫和本电脑已经有的。为了使整个程序运行起来不卡顿,运用多线程[不运用多线程会出现卡顿现象,读者可以看看小编之前的文章,里面有一篇是关于Python实现音乐播放器的,当时效果就是出现卡顿]。

3. GUI布局

说到布局,tkinter提供的有3种,小编使用的是grid布局,这种布局很容易让人理解自己做的一些控件、标签等在整个窗口的位置。

布局说明
pack()按照控件的添加顺序其进行排列,遗憾的是此方法灵活性较差
grid()以行和列(网格)形式对控件进行排列,此种方法使用起来较为灵活
place()可以指定组件大小以及摆放位置,三个方法中最为灵活的布局方法

4. 功能介绍

请添加图片描述
整个音乐播放器的效果就是上述那样。

  1. 如果要搜索线上音乐,直接在搜索框里输入歌名,然后按电脑键盘的Enter,然后搜索框下面的列表框里就会给出搜索的结果,如果想听其中的一首,鼠标点击到那一首歌曲那然后双击鼠标左键,即可播放音乐。如果我们想把播放的音乐收藏,直接点击鼠标右键即可;
  2. 如果我们想听我的收藏里的歌曲,直接点击我的收藏,然后列表框里的内容就会变成已经收藏的音乐,此时不是双击鼠标左键进行播放,而是选中其中一首,然后点击下面的播放按钮进行播放,如果我们想要歌曲循环播放,点击下面的循环播放即可(注:循环播放只支持收藏的音乐);
  3. 调节音量的大小,使用Scale控件(循环播放前面那个玩意);
  4. 设置按钮可以用来设置歌词的字体颜色、字体大小、字体格式和背景颜色,用的是下拉框,设置之后,保存即可。

5. 代码实现

代码有点多,且涉及到文件读写,就不不把代码一一粘贴出来了,想要的读者直接去资源下载即可。
这是其中一部分。

import tkinter as tk
import tkinter.ttk as ttk
from Crawl2.music_player.MusicCrawl import MusicCrawl
import threading
import time
from pydub import AudioSegment as pd
from pygame import mixer
import json

class MusicTk:

    def __init__(self):
        self.__top = tk.Tk()
        self.__size = [1150,700]
        # 窗口大小 宽 高
        self.__search2 = None
        self.__listbox = None
        self.__listMusic = None
        self.__mixer = None
        self.__canvas = None
        # 画布
        self.__progressBar = None
        # 进度条
        self.__time1 = None
        self.__time2 = None
        self.__scale = None
        self.__temp = None
        self.__listInfo = None
        self.__myCollections = None

        self.__data = ['微软雅黑',12,'white','black']

        self.__cbox = None
        self.__cbox2 = None
        self.__cbox3 = None
        self.__cbox4 = None
        self.__radioBtn = None
        self.__radioBtnV = None
        self.__num1 = 0
        self.__state = 0
        self.__keyword = ''
        self.win2 = None

    # 布局
    def sectionOne(self):
        win = self.__top
        win.geometry(f'{self.__size[0]}x{self.__size[1]}')
        win.resizable(height=0,width=0)

        v = tk.StringVar(value="搜索音乐")
        label_1 = tk.Entry(win,textvariable=v, font=('微软雅黑', 12),width=24)
        label_1.grid(row=0, column=0,ipady=2,ipadx=2,padx=20,sticky=tk.W)
        # 搜索框
        label_1.bind('<ButtonPress-1>',self.fun1)

        label_1.bind('<Return>',self.fun2)

        self.__search2 = label_1

        frame = tk.Frame(win)
        frame.grid(row=0, column=1, sticky=tk.W)

        tk.Button(frame, text='我的收藏',font=('', 12),command=self.__myCollection).grid(row=0, column=0, ipadx=1, ipady=1, sticky=tk.W)
        # 我的收藏 按钮

        tk.Button(frame,text='设置',font=('', 12),command=self.__setButton).grid(row=0,column=1,ipadx=1,ipady=1,padx=2,sticky=tk.W)
        # 设置按钮

        frameMiddle = tk.Frame(win)
        frameMiddle.grid(row=1,column=0,columnspan=2)

        list_box = tk.Listbox(frameMiddle, font=('', 12,'bold'), width=30,height=35,selectmode="single",selectbackground='red')
        list_box.grid(row=0, column=0)
        self.__listbox = list_box

        self.__listbox.bind('<Double-Button-1>',func=self.__fun4)
        # 双击鼠标左键播放音乐
        self.__listbox.bind('<ButtonPress-3>',func=self.__restore)
        # 单击鼠标右键收藏音乐

        canvas = tk.Canvas(frameMiddle,bg=self.__data[3],width=900,height=600,confine=True,relief='groove')
        canvas.grid(row=0,column=1)
        # 画布
        self.__canvas = canvas

        sv = tk.Scrollbar(frameMiddle)
        sv.grid(row=0, column=2)
        canvas.config(yscrollcommand=sv.set)
        canvas.config(scrollregion=(0, 0, 900, 3000))
        sv.config(command=canvas.yview)
        canvas.config(yscrollincrement=1)
        canvas.bind("<MouseWheel>", self.event1)

        progressBar = ttk.Progressbar(win)
        # 进度条
        progressBar.grid(row=2,column=0,columnspan=2)
        progressBar['length'] = self.__size[0]-400
        progressBar['value'] = 0
        # 进度条赋初值为0
        self.__progressBar = progressBar

        frame2 = tk.Frame(win)
        frame2.grid(row=3,column=0,columnspan=2)

        label_2 = tk.Label(frame2,text='00:00',font=('',12))
        label_2.grid(row=0,column=0,sticky=tk.W)
        self.__time1 = label_2

        tk.Button(frame2,text='上一首',font=('',12)).grid(row=0,column=1)
        tk.Button(frame2, text='播放',font=('',12),command=self.__play).grid(row=0, column=2)
        tk.Button(frame2, text='暂停',font=('',12),command=self.__stop).grid(row=0, column=3)
        tk.Button(frame2, text='下一首', font=('', 12)).grid(row=0, column=4)

        label_3 = tk.Label(frame2, text='00:00', font=('', 12))
        label_3.grid(row=0, column=5, sticky=tk.E)
        self.__time2 = label_3

        self.__scale = tk.Scale(frame2,from_=0,to=100,resolution=2,length=150,orient=tk.HORIZONTAL,font=('',12),command=self.fun6)
        self.__scale.grid(row=0,column=6,sticky=tk.E,padx=10)
        self.__scale.set(100)

        self.__radioBtnV = tk.IntVar()
        self.__radioBtn = tk.Checkbutton(frame2,variable=self.__radioBtnV,text='循环播放',onvalue=1,command=self.__getRadioBtn)
        self.__radioBtn.grid(row=0,column=7,sticky=tk.E,padx=10)
        # 复选框

        tk.mainloop()


    def __getRadioBtn(self):
        self.__num1 = self.__radioBtnV.get()

    # 设置按钮
    def __setButton(self):


        win2 = tk.Tk()

        self.win2 = win2

        tk.Label(win2,text='播放字体',font=('微软雅黑',10)).grid(row=0,column=0)
        # 下拉框
        cbox = ttk.Combobox(win2)
        cbox.grid(row=0,column=1)
        cbox['value'] = ('微软雅黑','华文宋体','华文宋体')
        cbox.current(0)

        tk.Label(win2,text='字体大小',font=('微软雅黑',10)).grid(row=1,column=0)

        cbox2 = ttk.Combobox(win2)
        cbox2.grid(row=1, column=1)
        cbox2['value'] = tuple([str(i) for i in range(12,19)])
        cbox2.current(0)

        tk.Label(win2, text='字体颜色', font=('微软雅黑', 10)).grid(row=2, column=0)
        cbox3 = ttk.Combobox(win2)
        cbox3.grid(row=2, column=1)
        cbox3['value'] = ('white','red','black')
        cbox3.current(0)

        tk.Label(win2, text='背景颜色', font=('微软雅黑', 10)).grid(row=3, column=0)
        cbox4 = ttk.Combobox(win2)
        cbox4.grid(row=3, column=1)
        cbox4['value'] = ('black','white')
        cbox4.current(0)

        self.__cbox = cbox
        self.__cbox2 = cbox2
        self.__cbox3 = cbox3
        self.__cbox4 = cbox4


        saveBtn = tk.Button(win2,text='保存',font=('微软雅黑',10),command=self.__reset)
        saveBtn.grid(row=4,column=0,columnspan=2)

        win2.mainloop()

    def __reset(self):
        font = self.__cbox.get()
        size = self.__cbox2.get()
        fontColor = self.__cbox3.get()
        bgColor = self.__cbox4.get()

        self.__data = [font,size,fontColor,bgColor]

        dict2 = dict()
        dict2['font'] = font
        dict2['size'] = size
        dict2['fontColor'] = fontColor
        dict2['bgColor'] = bgColor
        str2 = json.dumps(dict2)

        with open(file='data.json',mode='w',encoding='utf-8') as f:
            f.write(str2)

        self.win2.destroy()

    # 我的收藏按钮
    def __myCollection(self):
        with open(file='Collection.json',mode='r',encoding='utf-8') as f:
            str_1 = f.read()

        dict1 = json.loads(str_1)

        self.__myCollections = dict1

        self.__listbox.delete(0, tk.END)
        # 清空列表框里的内容
        for key in dict1:
            self.__listbox.insert(tk.END, f'{key}')

        self.__state = 1


    def __restore(self,event):
        with open(file='Collection.json',mode='r',encoding='utf-8') as f:
            str1 = f.read()
        if str1 != '':
            dict1 = json.loads(str1)
        else:
            dict1 = {}
        listBox = self.__listbox
        v = listBox.get(listBox.curselection())
        if v not in dict1:
            dict1[v] = [self.__listInfo[1],self.__listInfo[2],self.__listInfo[3]]

        str1 = json.dumps(dict1)
        with open(file='Collection.json',mode='w',encoding='utf-8') as f:
            f.write(str1)


    def fun6(self,value):
        if self.__mixer is not None:
            mixer = self.__mixer
            mixer.music.set_volume(int(value)/100)


    def event1(self,event):
        number = int(-event.delta / 60)
        self.__canvas.yview_scroll(number, 'units')

    def __play(self):
        # if self.__mixer is not None:
        #     self.__mixer.music.unpause()
        #     self.__temp = None
        listBox = self.__listbox
        v = listBox.get(listBox.curselection())

        dict1 = self.__myCollections
        list1 = dict1[v]
        self.__keyword = v


        musicPlayThread = threading.Thread(target=self.threadFun1, args=([True,0,0,list1[2]],))
        lyricsThread = threading.Thread(target=self.threadFun2, args=(list1[0],))
        progressThread = threading.Thread(target=self.threadFun3, args=(list1[1],))
        timeThread = threading.Thread(target=self.threadFun4, args=(list1[1],))

        musicPlayThread.start()
        lyricsThread.start()
        progressThread.start()
        timeThread.start()


    def __stop(self):
        if self.__mixer is not None:
            # self.__mixer.music.stop()
            self.__mixer.music.pause()

            self.__temp = 'stop'

    def __fun4(self,event):
        tuple2 = self.__listbox.curselection()
        if len(tuple2) != 0:
            index = tuple2[0]
            url = self.__listMusic[index][-1]

            # thread2 = threading.Thread(target=self.fun5,args=(url,))
            # thread2.start()
            self.fun5(url)


    def fun5(self,url):
        mC = MusicCrawl('我的梦')
        listInfo = mC.DownLoad(url)

        # mp3Time = self.__getMp3Time(listInfo[3])

        self.__listInfo = listInfo

        musicPlayThread = threading.Thread(target=self.threadFun1,args=(listInfo,))
        lyricsThread = threading.Thread(target=self.threadFun2,args=(listInfo[1],))
        progressThread = threading.Thread(target=self.threadFun3,args=(listInfo[2],))
        timeThread = threading.Thread(target=self.threadFun4,args=(listInfo[2],))

        musicPlayThread.start()
        lyricsThread.start()
        progressThread.start()
        timeThread.start()

    # 线程函数
    def threadFun1(self,listInfo:list):
        if listInfo[0]:
            mixer.init()
            mixer.music.load(f'./musics/{listInfo[3]}.mp3')
            mixer.music.play()
            #
            self.__mixer = mixer


    def threadFun2(self,lyrics):
        lyrics = lyrics.split('\r\n')
        canvas = self.__canvas

        canvas.delete(tk.ALL)
        canvas.yview_scroll(-2000, 'units')

        start_time = 0
        for i in range(len(lyrics)):
            l = lyrics[i]
            if l != '' :
                time1 = l[:10]
                minute = int(time1[1:3])*60*1000
                second = int(float(time1[4:9])*1000)
                sumTime = minute + second

                timeDifference = sumTime - start_time
                start_time = sumTime

                str1 = l[10:]
                time.sleep(timeDifference/1000)
                canvas.create_text(30, 20 + i * 20, text=f'{str1}', fill=self.__data[2], anchor=tk.W,
                                   font=(self.__data[0], self.__data[1], 'bold'))

                if 20+i*20 > 550:
                    for j in range(10):
                        canvas.yview_scroll(2, 'units')


    def threadFun3(self,zTime):
        progressBar = self.__progressBar
        progressBar['maximum'] = zTime
        progressBar['value'] = 0
        for i in range(zTime):
            if self.__temp is None:
                progressBar['value'] += 1
                time.sleep(1)


    def threadFun4(self,zTime):
        minute = zTime//60
        second = zTime - minute*60
        self.__time2['text'] = '{:02d}:{:02d}'.format(minute,second)

        minute1 = 0
        second1 = 0

        for i in range(zTime+1):
            if self.__temp is None:
                if second1!= 0 and second1 == 60:
                    minute1 += 1
                    second1 = 0
                time.sleep(1)
                self.__time1['text'] = '{:02d}:{:02d}'.format(minute1,second1)
                second1 += 1

        if self.__num1 == 1 and self.__state == 1:
            dict2 = self.__myCollections
            l = list(dict2.keys())
            index = l.index(self.__keyword)
            keyword = l[(index+1)%len(l)]
            self.__listbox.selection_clear(0, tk.END)
            self.__listbox.selection_set((index+1)%len(l))
            self.__keyword = keyword
            self.__play()


    def __getMp3Time(self,randomNum):
        filePath = f'./musics/{randomNum}.mp3'
        song = pd.from_file(file = filePath,format = 'mp3')
        return len(song)//1000


    def fun2(self,event):
        if self.__search2 is not None:
            entry_2 = self.__search2
            entry_value = entry_2.get()
            # 输入框里的值
            if len(entry_value.strip()) != 0:
                self.fun3(entry_value)
                # thread1 = threading.Thread(target=self.fun3,args=(entry_value,))
                # thread1.start()


    def fun3(self,keyWord):
        mC = MusicCrawl(keyWord)
        lists = mC.crawlSection()
        # print(lists)

        self.__listMusic = lists

        self.__listbox.delete(0, tk.END)
        # 清空列表框里的内容
        for list2 in lists:
            self.__listbox.insert(tk.END, f'{list2[1]}-{list2[0]}')

        self.__state = 0




    def fun1(self,event):
        if self.__search2 is not None:
            self.__search2.delete(0,'end')

    # 一个监听事件的函数


    def sectionTwo(self):
    # 操作
        with open(file='data.json',mode='r',encoding='utf-8') as f:
            str2 = f.read()
        dict2 = json.loads(str2)

        self.__data = [dict2['font'],dict2['size'],dict2['fontColor'],dict2['bgColor']]
        self.sectionOne()


if __name__ == '__main__':
    a = MusicTk()
    a.sectionTwo()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/130796.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Android studio设置全屏显示的两种方式

两种在Androidstudio中设置全屏的方式&#xff0c;推荐第二种 第一种&#xff08;Java文件中设置&#xff09; 直接在onCreate()函数中设置 getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);package com.exa…

MARKETS AND MARKET LOGIC——The Market‘s Principles (6)_3

市场的组成——对行为观察的反思 制定市场理解 理解市场逻辑将有助于每个参与者提高其在市场上成功的可能性&#xff0c;因为他将能够阅读市场活动并接收市场生成的信息&#xff0c;这些信息很少有参与者承认或理解。这一信息特别涉及市场如何接受或拒绝随着时间的推移而升高或…

Linux用户标识符UID与GID和用户账号

1.用户标识符UID和GID UID 表示的是用户的标识&#xff08;User Identification&#xff09; GID 表示的是用户组的标识&#xff08;Group Identification&#xff09; 显示用户ID信息。 命令&#xff1a;id username 2.用户账号 用户和用户组的信息都存放在…

RabbitMQ(一)Windows下载安装

目录一、下载安装包二、安装erlang三、安装RabbitMQ四、配置RabbitMQ管理界面官网地址&#xff1a;https://www.rabbitmq.com/ 下载地址&#xff1a;https://www.rabbitmq.com/download.html 一、下载安装包 RabbitMQ Windows下载地址&#xff1a;https://www.rabbitmq.com/in…

001. Nginx场景,优点,组成部分和编译

目录一&#xff1a; Nginx三个重要的使用场景二&#xff1a; Nginx的优点三&#xff1a;Nginx文件的组成部分四&#xff1a;编译Nginx一&#xff1a; Nginx三个重要的使用场景 静态资源服务 疑问&#xff1a;为什么需要静态资源服务&#xff1f;答&#xff1a; 在整个的web请求…

string_string数据类型概括

目录string数据类型创建字符串的三种方式检查字符串的方法常用的字符串方法字符串遍历切割字符串为数组​截取子串判断某个字符串是否出现在当前字符串中文本匹配-match、(es6)matchAll文本替换-replace、(es6)replaceAll(es6)获取某个位置的字符串有关ASCII码的方法获取某字符…

Good Bye 2022: 2023 is NEAR C Koxia and Number Theory

Problem - C - Codeforces 题意&#xff1a; 给定一个数列a&#xff0c;问你是否存在一个数 x 使得所有数加上x之后两两互质 思路&#xff1a; 一、 我们知道一个结论&#xff0c;一些数两两之间互质&#xff0c;就说明所有数之间的质因子都不重合 如果要我们去判断一堆数…

回顾与展望Zebec举办的“Web3.0 TechHive Summit 2022 大会”

随着流支付生态 Zebec 不断的实现商业进展&#xff0c;我们看到&#xff0c;其生态从Solana陆续的迁移到以BNB Chain为代表的EVM链上&#xff0c;并推出了以ZK-Rollup为主要技术手段的公链Zebec Chain&#xff0c;以在未来服务且满足于Web2、Web3世界的发展需求。 Zebec正在成…

若依框架:前端项目结构与初始页面渲染流程

目录 main.js入口文件 前端页面初始化 package.json配置文件 vue-cli&#xff1a;.env模式和环境变量配置 vue-cli三大核心构件 CLI 服务与npm scripts vue-cli-service命令与.env模式配置文件 .env模式和环境变量配置 vue.config.js&#xff1a;项目启动-自动打开默认…

pycharm如何设置python文件模板

pycharm如何设置python文件模板设置文件模板预定义变量设置自定义模板变量设置文件模板 打开设置界面 进入编辑器->文件和代码模板 此处我只写了文件头注释模板&#xff0c;也可以加一些默认代码&#xff0c;比如&#xff1a; python if __name__ "__main__":…

NC65在单据添加按钮跳转到固定单据,且进行数据初始化

实现功能 下面两个是按钮 下面红框里面的内容是上面单据带过来的 上面就是本地代码实现的功能 具体实现代码 提前约定 为了方便称呼&#xff0c;这里把第一张图称为上游单据&#xff0c;第二张图为下游单据 所有代码AggVo都要替换成你自己的 配置文件 上游单据拓展配置文…

2023年天津美术学院专升本专业课线上考试安排准备及设备操作说明

天津美术学院2023年高职升本招生专业考试考生须知及操作说明一、考试时间安排二、考前准备 (一)纸张及其他材料(二)考试设备及网络 1.考生须提前准备好至少两部智能手机&#xff0c;为确保网络考试顺利进行&#xff0c;建议考生使用最近3年上市的主流品牌手机&#xff08;如iPh…

太强了!这么设计中间件完美解决了百万并发的问题!

V-xin&#xff1a;ruyuan0330 获得600页原创精品文章汇总PDF 目录 一、大部分人对Java并发仍停留在理论阶段二、中间件系统的内核机制&#xff1a;双缓冲机制三、百万并发的技术挑战四、内存数据写入的锁机制以及串行化问题五、片机制 分段加锁机制 六、缓冲区写满时的双缓冲…

c++ - 第19节 - c++11

1.C11简介 c11简介&#xff1a;在2003年C标准委员会曾经提交了一份技术勘误表(简称TC1)&#xff0c;使得C03这个名字已经取代了C98称为C11之前的最新C标准名称。不过由于C03(TC1)主要是对C98标准中的漏洞进行修复&#xff0c;语言的核心部分则没有改动&#xff0c;因此人们习惯…

Pycharm打开Project(工程)时停留在preparing workspace时间过长,导致打开很慢的解决方法...

Pycharm打开Project(工程)时停留在preparing workspace时间过长,导致打开很慢的解决方法… 昊虹君用Pycharm进行Python的开发&#xff0c;一直用得好好的&#xff0c;也没作过什么异常操作&#xff0c;但是从上星期开始&#xff0c;打开Project(工程)时停留在preparing worksp…

【王道操作系统】2.1.1 进程的定义、特征、组成、组织

进程的定义、特征、组成、组织 文章目录进程的定义、特征、组成、组织1.进程的定义2.进程的特征3.进程的组成4.进程的组织1.进程的定义 程序的概念&#xff1a; 进程的概念&#xff1a; 进程和程序的区别和联系&#xff1a; 区别&#xff1a; 进程是动态的&#xff0c;程序是静…

Linux系统下crond任务调度指令的常见用法

Linux系统下crond任务调度指令的常见用法 任务调度 任务调度是指系统在某个时间执行的特定的命令或程序。任务调度分类:1)系统工作:有些重要的工作必须周而复始地执行。如病毒扫描等;2)个别用户工作:个别用户可能希望执行某些程序&#xff0c;比如对mysql数据库的备份。 基本语…

【攻防世界】网鼎杯2018 Web fakebook

学web总是能让我学到新的知识&#xff0c;很开心&#xff0c;很有趣&#xff0c;很好玩 打开题目 一个登录按钮&#xff0c;一个注册按钮&#xff0c;其余没有什么发现&#xff0c;对于web来说&#xff0c;常规先测试一下robots.txt文件 果然又发现&#xff0c;有一个bak文件…

opencv-python常用函数解析及参数介绍(二)——图像填充与图像融合

1.图像填充 函数及参数介绍 在opencv中使用cv2.copyMakeBorder可以进行图像的边界填充&#xff0c;需要的参数为(img, top_size, bottom_size, left_size, right_size, borderType&#xff09;&#xff0c;即&#xff08;图片&#xff0c;上侧填充值&#xff0c;下侧填充值&a…

【数据结构与算法】KMP算法

文章目录前言一 .KMP的来历二.KMP解决的问题1.引入2.定义的引入1.字符串前缀2.字符串后缀3.最长相等前后缀3.核心思想三.next/prefix1.next的含义定义规律2.next的求取1.准备工作2.思路和图解4.应用前言 在C语言的strcmp的实现过程中&#xff0c;所涉及的算法较为简单&#xff…