响应式网络网站源码,深圳市住房和城乡和建设局网站,wordpress模板 知乎,中国能建旗下公司排名文章导读 本文用于巩固Pymysql操作MySQL与MySQL操作的知识点#xff0c;实现一个简易的音乐播放器#xff0c;拟实现的功能包括#xff1a;用户登录#xff0c;窗口显示#xff0c;加载本地音乐#xff0c;加入和删除播放列表#xff0c;播放音乐。
点击此处获取参考源…文章导读 本文用于巩固Pymysql操作MySQL与MySQL操作的知识点实现一个简易的音乐播放器拟实现的功能包括用户登录窗口显示加载本地音乐加入和删除播放列表播放音乐。
点击此处获取参考源码https://pan.baidu.com/s/1HNLc7tCVBjoBMpnezzm7_g?pwdvqr4 1、创建数据库和表
概述本章的操作均在navicat的查询中实现。 1.1、创建数据库
实操通过navicat创建一个名为db_music的数据库用于存储本实战中所有的表。
CREATE DATABASE db_music DEFAULT CHARACTER SETutf8;
运行结果如下 1.2、创建用户表
实操前准备通过navicat切换到我们创建的数据库db_music这里直接关闭之前打开的查询然后按如下图所示的步骤操作。 实操在db_music数据库下创建一个名为user_info的用户表包含的字段如下id用户名(uname)密码(password)并将id设置为主键。
CREATE TABLE user_info(
id int PRIMARY KEY,
uname VARCHAR(20),
password VARCHAR(30)
)
运行后的结果为(也可以刷新数据库查看db_music数据库下的表) 1.3、创建音乐列表
实操在db_music数据库下创建一个用于保存本地音乐的表取名为music_info包含以下字段id歌曲名(name)歌手(singer)和存放路径(path)并将id设置为主键自增长。
CREATE TABLE music_info(
id int PRIMARY KEY auto_increment,
name varchar(30),
singer varchar(20),
path varchar(80)
);
运行结果如下 1.4、创建播放列表
实操在db_music数据库下创建一个用于播放本地音乐的表取名为play_info包含以下字段歌单唯一标识(id)用于关联用户唯一标识id通过该字段知道这是谁的播放列表(u_id)用于关联本地音乐的唯一标识id通过该字段知道播放哪首音乐(m_id)并将id设置为主键自增长正确的设置u_id和m_id的外键。
CREATE TABLE play_info(
id int PRIMARY KEY auto_increment,
u_id int,
m_id int,
FOREIGN KEY(u_id) REFERENCES user_info(id),
FOREIGN KEY(m_id) REFERENCES music_info(id)
);
运行结果如下 此时数据库db_music下就创建了三张表 2、实现用户登录
2.1、插入数据
概述为了能实现用户登录首先得为user_info表中插入用户数据用于验证后续程序的可行性。
INSERT INTO user_info VALUES(1,muxikeqi,123321);
运行后的user_info表数据如下 2.2、引入链接数据库的工具类
概述新建一个名为tools.py的文件然后将学习Python操作MySQL时写的工具类拷贝过来并加以修改(这里主要是修改链接的数据库)用于简化后续对数据库操作。
import pymysqlclass DBUtil:工具类用于简化链接和关闭数据库的操作以及对数据的操作# 单独保存链接的参数,可以将其保存到特定文件中直接调用文件获取也可config {host: localhost,user: root,passwd: root,db: db_music,charset: utf8,port: 3306}def __init__(self) - None:构造函数用于获取链接和游标# **指字典对象self.con pymysql.connect(**DBUtil.config)self.cursor self.con.cursor()def close(self) - None:该函数用于关闭链接和游标(关闭前需要判断是否存在)# 存在游标就关闭if self.cursor:self.cursor.close()# 存在链接就关闭if self.con:self.con.close()def dml(self, sql, args):本函数用于封装Mysql的DML语句,用于实现数据的增删改# 进行DML语句操作时一定要注意报错导致无法正常关闭链接try:# 执行sql中的DML语句self.cursor.execute(sql, args)# DML操作一定要通过链接对象提交事务self.con.commit()except Exception as e:print(存在错误,错误信息如下:, e)# 回滚前先判断链接还是否存在if self.con:# 回滚到原状态self.con.rollback()# 使用try...except...finally...最重要的作用就是保证链接正常关闭finally:print(即将关闭链接)self.close()def query_one(self, sql, args):本函数用于查询单条数据try:self.cursor.execute(sql, args)re self.cursor.fetchone()return reexcept Exception as e:print(存在错误,错误信息如下:, e)finally:print(即将关闭链接)self.close()def query_many(self, sql):本函数用于查询所有数据try:self.cursor.execute(sql)# 获取结果并返回数据return self.cursor.fetchall()except Exception as e:print(存在错误,错误信息如下:, e)finally:print(即将关闭链接)self.close()if __name__ __main__:# 获取链接对象db DBUtil()# 准备sql语句及其对应的参数sql select * from user_info where uname%s and password%s;args [muxikeqi,123321]# 运行sql语句并关闭链接re db.query_one(sql,args)print(re)pass 2.3、实现用户登录
概述创建一个名为service.py的文件(与前面引入的tools.py文件放在同一目录)该文件用于写后端逻辑类代码首先在service.py文件中写一个名为login的函数用于连接数据库并判断用户是否能登录成功。
from tools import DBUtilclass Music:def login(self, uname: str, password: str) - bool::param uname:用户名:param password:密码:return: 登录成功返回True,登录失败返回False# 准备要执行的sql语句sql select * from user_info where uname%s and password%s# 准备sql语句需要的参数args (uname, password)# 创建工具对象(实际时获取数据库的链接对象和游标对象)db DBUtil()# 利用游标对象调用对应的单条查询方法re db.query_one(sql, args)# 由于query_one函数运行成功后是有返回值的可以通过返回值判断是否查询成功进而判断是否有该用户if re:print(登录成功)return Trueelse:print(登录失败)return False# 测试代码
if __name__ __main__:# 创建Music类的实例对象service Music()# 利用实例对象调用实例方法re service.login(muxikeqi, 123321)print(re)运行结果如下 3、实现窗口(了解)
3.1、实现显示窗口
概述窗口的实现将采用Tkinter实现通过菜鸟教程可以查看其用法。
实操创建一个名为ui.py的文件放在前面创建的.py文件的同级目录该文件主要写前端页面的代码文件中用一个名为Ui的类承载显示窗口的内容为了让程序进入Ui类就能显示窗口所以推荐将显示窗口的内容直接放进构造函数中。
import tkinter
class Ui:def __init__(self):# 主窗口top tkinter.Tk()# 创建按钮对象第一个参数是父容器,text表示显示的内容b1 tkinter.Button(top, text播放)b2 tkinter.Button(top, text导入音乐)b3 tkinter.Button(top, text删除音乐)# 设置按钮显示位置(row表示放在哪一行,column表示放在哪一列,pad表示距离边框的内边距,单位为像素)b1.grid(row1,column1,padx10,pady10)b2.grid(row1,column2,padx10,pady10)b3.grid(row1,column3,padx10,pady10)# 创建播放列表lisbox tkinter.Listbox(top)# 设置播放列表位置,columnspan表示垮了多少列lisbox.grid(row2,column0,columnspan4,padx10,pady10)# 进入消息循环top.mainloop()if __name____main__:ui Ui()
运行结果如下 3.2、实现登陆后显示窗口
概述前面已经实现了用户登录和窗口的显示如果要实现登陆后显示窗口只需在ui.py文件中做出修改即可。
import tkinter
from service import Musicclass Ui:def __init__(self):# 主窗口top tkinter.Tk()# 创建按钮对象第一个参数是父容器,text表示显示的内容b1 tkinter.Button(top, text播放)b2 tkinter.Button(top, text导入音乐)b3 tkinter.Button(top, text删除音乐)# 设置按钮显示位置(row表示放在哪一行,column表示放在哪一列,pad表示距离边框的内边距,单位为像素)b1.grid(row1,column1,padx10,pady10)b2.grid(row1,column2,padx10,pady10)b3.grid(row1,column3,padx10,pady10)# 创建播放列表lisbox tkinter.Listbox(top)# 设置播放列表位置,columnspan表示垮了多少列lisbox.grid(row2,column0,columnspan4,padx10,pady10)# 进入消息循环top.mainloop()if __name____main__:# 获取用户名和密码uname input(请输入用户名:)password input(请输入密码:)# 创建Music类的实例对象ser Music()# 利用实例对象调用实例方法由于会返回布尔值所以直接进行判断if ser.login(uname,password):ui Ui()else:print(用户名或密码错误!)
运行结果如下 弹出的窗口如下 4、实现播放音乐
概述播放音乐将使用pygame模块实现。
实操前准备1进入终端通过如下命令下载pygame模块。
pip install pygame
运行结果如下 实操前准备2去任意音乐网站下载任意首歌曲放在music项目目录中的任意文件夹中(这里我放在了名为music的文件夹中) 实操利用pygame模块实现任意一首音乐的播放。
import pygame
import time
# 初始化加载音乐的混合器
pygame.mixer.init()
# 加载音乐(路径可以是相对路径也可以是绝对路径)
pygame.mixer.music.load(f.\music\c.flac)
# 播放音乐
pygame.mixer.music.play()
# 程序进入等待(不要立即关闭程序)单位为秒
time. Sleep(20)
20秒后程序运行结果如下 5、为UI界面设置导入音乐的事件
概述首先需要为导入音乐的按钮添加一个点击事件点击事件用于打开文件夹让用户选择音乐文件。只需要对ui.py文件做修改即可
import tkinter
from service import Music
import tkinter.filedialogclass Ui:def __init__(self):# 主窗口top tkinter.Tk()# 创建按钮对象第一个参数是父容器,text表示显示的内容b1 tkinter.Button(top, text播放)b2 tkinter.Button(top, text导入音乐)b3 tkinter.Button(top, text删除音乐)# 设置按钮显示位置(row表示放在哪一行,column表示放在哪一列,pad表示距离边框的内边距,单位为像素)b1.grid(row1,column1,padx10,pady10)b2.grid(row1,column2,padx10,pady10)b3.grid(row1,column3,padx10,pady10)# 为导入音乐的按钮添加点击事件,bind方法的第一个参数是事件名称(写法固定,不能修改)第二个参数是点击事件(点击按钮后触发的函数)b2.bind(Button-1,self.importMusic)# 创建播放列表lisbox tkinter.Listbox(top)# 设置播放列表位置,columnspan表示垮了多少列lisbox.grid(row2,column0,columnspan4,padx10,pady10)# 进入消息循环top.mainloop()# 导入音乐的点击事件def importMusic(self,event):# 弹出选择框(返回的结果是元组)(一定要通过filetypes限制文件类型否则什么文件都能加入进来)filenames tkinter.filedialog.askopenfilenames(filetypes[(mp3,.mp3),(flac,.flac)])if __name____main__:# 获取用户名和密码uname input(请输入用户名:)password input(请输入密码:)# 创建Music类的实例对象ser Music()# 利用实例对象调用实例方法由于会返回布尔值所以直接进行判断if ser.login(uname,password):ui Ui()else:print(用户名或密码错误!)
运行结果如下 点击导入音乐按钮后的效果如下 6、为后台添加导入音乐的逻辑
概述本章节将实现前端选择音乐后端将音乐名和音乐存放在本地的路径导入到db_music数据库下的music_info表的对应字段中。
首先在service.py文件中的Music类中添加一个名为add_music的实例方法用于将前端的数据添加到db_music数据库下music_info表中 def add_music(self,files:tuple[str]) - None:该函数用于将前台返回的文件路径存储到数据库中:param file:音乐文件的路径,默认是多个,元素类型为字符串:return: None# 编写sql语句sql insert into music_info(name,path) values(%s,%s)# 遍历数据for f in files:print(f)# 根据路径获取歌曲名name f[f.rfind(/)1:f.rfind(.)]print(name)# 创建数据库链接对象db DBUtil()# 准备参数args(name,f)# 执行插入语句db.dml(sql,args)
接下来修改ui.py文件的内容用于将tkinter.filedialog.askopenfilenames返回的歌曲路径(返回类型是元组返回的是歌曲在本地的绝对路径)传入前面写的add_music函数中进行处理而add_music又属于Music类所以要先创建一个实例对象再通过实例对象调用add_music方法 def importMusic(self,event):# 弹出选择框(返回的结果是元组)(一定要通过filetypes限制文件类型否则什么文件都能加入进来)filenames tkinter.filedialog.askopenfilenames(filetypes[(mp3,.mp3),(flac,.flac),(mgg,.mgg)])# 创建服务对象ser Music()# 调用增加音乐的功能将前端的数据发送给后端处理ser.add_music(filenames)
运行后的结果如下 加入歌曲后db_music数据库下music_info表的数据如下 完整ui.py文件如下
import tkinter
from service import Music
import tkinter.filedialogclass Ui:def __init__(self):# 主窗口top tkinter.Tk()# 创建按钮对象第一个参数是父容器,text表示显示的内容b1 tkinter.Button(top, text播放)b2 tkinter.Button(top, text导入音乐)b3 tkinter.Button(top, text删除音乐)# 设置按钮显示位置(row表示放在哪一行,column表示放在哪一列,pad表示距离边框的内边距,单位为像素)b1.grid(row1,column1,padx10,pady10)b2.grid(row1,column2,padx10,pady10)b3.grid(row1,column3,padx10,pady10)# 为导入音乐的按钮添加点击事件,bind方法的第一个参数是事件名称第二个参数是点击事件(点击按钮后触发的函数)b2.bind(Button-1,self.importMusic)# 创建播放列表lisbox tkinter.Listbox(top)# 设置播放列表位置,columnspan表示垮了多少列lisbox.grid(row2,column0,columnspan4,padx10,pady10)# 进入消息循环top.mainloop()# 导入音乐的点击事件def importMusic(self,event):# 弹出选择框(返回的结果是元组)(一定要通过filetypes限制文件类型否则什么文件都能加入进来)filenames tkinter.filedialog.askopenfilenames(filetypes[(mp3,.mp3),(flac,.flac),(mgg,.mgg)])# 创建服务对象ser Music()# 调用增加音乐的功能将前端的数据发送给后端处理ser.add_music(filenames)if __name____main__:# 获取用户名和密码uname input(请输入用户名:)password input(请输入密码:)# 创建Music类的实例对象ser Music()# 利用实例对象调用实例方法由于会返回布尔值所以直接进行判断if ser.login(uname,password):Ui()else:print(用户名或密码错误!)
完整service.py文件如下
from tools import DBUtilclass Music:def login(self, uname: str, password: str) - bool::param uname:用户名:param password:密码:return: 登录成功返回True,登录失败返回False# 准备要执行的sql语句sql select * from user_info where uname%s and password%s# 准备sql语句需要的参数args (uname, password)# 创建工具对象(实际时获取数据库的链接对象和游标对象)db DBUtil()# 利用游标对象调用对应的单条查询方法re db.query_one(sql, args)# 由于query_one函数运行成功后是有返回值的可以通过返回值判断是否查询成功进而判断是否有该用户if re:print(登录成功)return Trueelse:print(登录失败)return Falsedef add_music(self,files:tuple[str]) - None:该函数用于将前台返回的文件路径存储到数据库中:param file:音乐文件的路径,默认是多个,元素类型为字符串:return: None# 编写sql语句sql insert into music_info(name,path) values(%s,%s)# 遍历数据for f in files:print(f)# 根据路径获取歌曲名name f[f.rfind(/)1:f.rfind(.)]print(name)# 创建数据库链接对象db DBUtil()# 准备参数args(name,f)# 执行插入语句db.dml(sql,args)# 测试代码
if __name__ __main__:# 创建Music类的实例对象service Music()# 利用实例对象调用实例方法re service.login(muxikeqi, 123321)print(re)7、实现音乐列表和用户绑定
概述实现音乐列表和用户的绑定主要是为play_info表添加数据即可play_info表包含了数据库id和用户id契合用户和音乐的对应关系。
7.1、获取用户id
思路为service.py文件下的Music类添加一个构造方法构造方法用于放置一个user变量用于保存用户id默认为空。结合login函数中变量re是数据库进行查询后的返回的查询结果通过判断re是否为空来判断用户是否登录。当用户不为空时(用户登录成功)我们将返回的结果re赋值给构造方法中的user变量此时user就拿到返回的用户信息然后获取用户id在user_info表中对应位置的值就拿到了用户id。 def __init__(self):# 用户默认为空self. User Nonedef login(self, uname: str, password: str) - bool::param uname:用户名:param password:密码:return: 登录成功返回True,登录失败返回False# 准备要执行的sql语句sql select * from user_info where uname%s and password%s# 准备sql语句需要的参数args (uname, password)# 创建工具对象(实际时获取数据库的链接对象和游标对象)db DBUtil()# 利用游标对象调用对应的单条查询方法re db.query_one(sql, args)# 由于query_one函数运行成功后是有返回值的可以通过返回值判断是否查询成功进而判断是否有该用户if re:print(登录成功)self.userre# 查看内容是否包含u_id对应的内容# print(self.user)return Trueelse:print(登录失败)return False 7.2、获取音乐id
思路在service.py文件中的有一个add_music函数我们可以在修改music_info表的同时为play_info表添加数据而add_music函数中通过db.dml(sql,args)对music_info表添加了数据如果我们能让dml方法告诉我们它添加的id为多少那么获取音乐id的问题就解决了因此这里可以直接将工具类中的dml函数重写一份添加一个获取添加id的方法并返回这里我为这个函数取名为dml_back_id。写完本小节还是存在问题的需要看下一小节
工具类重写的dml_back_id函数如下 # 重写上面的dml函数,增加返回id的操作def dml_back_id(self, sql, args):本函数用于封装Mysql的DML语句,用于实现数据的增删改# 进行DML语句操作时一定要注意报错导致无法正常关闭链接try:# 执行sql中的DML语句self.cursor.execute(sql, args)# 获取添加的idid self.con.insert_id()# DML操作一定要通过链接对象提交事务self.con.commit()# 返回idreturn idexcept Exception as e:print(存在错误,错误信息如下:, e)# 回滚前先判断链接还是否存在if self.con:# 回滚到原状态self.con.rollback()# 使用try...except...finally...最重要的作用就是保证链接正常关闭finally:print(即将关闭链接)self.close()
service.py文件的add_music函数修改如下 def add_music(self,files:tuple[str]) - None:该函数用于将前台返回的文件路径存储到数据库中:param file:音乐文件的路径,默认是多个,元素类型为字符串:return: None# 编写sql语句sql insert into music_info(name,path) values(%s,%s)# 遍历数据for f in files:print(f)# 根据路径获取歌曲名name f[f.rfind(/)1:f.rfind(.)]print(name)# 创建数据库链接对象db DBUtil()# 准备参数args(name,f)# 将音乐信息添加到music_info表中m_id db.dml_back_id(sql,args)# 实现音乐与用户绑定musicBindId_sql insert into play_info(u_id,m_id) values(%s,%s)args(self.user[0],m_id)db.dml(musicBindId_sql,args) 7.3、问题解决
问题1无法获取到用户id。
原因分析用户是从前端文件进行登录的在ui.py文件的测试内容中我们创建了一个实例对象用于调用service.py文件中的login函数判断用户能否登录成功但登录成功后就进入了Ui类的构造方法构造方法为导入音乐按钮添加了一个名为importMusic的点击事件在importMusic函数中又创建了一个实例对象用于调用service.py文件中的add_music方法我们是通过第二个对象去获取用户id但用户id保存到了第一个实例对象。
解决办法将第二个实例对象删除再用第一个实例对象调用add_music函数即可。
import tkinter
from service import Music
import tkinter.filedialogclass Ui:def __init__(self):# 主窗口top tkinter.Tk()# 创建按钮对象第一个参数是父容器,text表示显示的内容b1 tkinter.Button(top, text播放)b2 tkinter.Button(top, text导入音乐)b3 tkinter.Button(top, text删除音乐)# 设置按钮显示位置(row表示放在哪一行,column表示放在哪一列,pad表示距离边框的内边距,单位为像素)b1.grid(row1,column1,padx10,pady10)b2.grid(row1,column2,padx10,pady10)b3.grid(row1,column3,padx10,pady10)# 为导入音乐的按钮添加点击事件,bind方法的第一个参数是事件名称第二个参数是点击事件(点击按钮后触发的函数)b2.bind(Button-1,self.importMusic)# 创建播放列表lisbox tkinter.Listbox(top)# 设置播放列表位置,columnspan表示垮了多少列lisbox.grid(row2,column0,columnspan4,padx10,pady10)# 进入消息循环top.mainloop()# 导入音乐的点击事件def importMusic(self,event):# 弹出选择框(返回的结果是元组)(一定要通过filetypes限制文件类型否则什么文件都能加入进来)filenames tkinter.filedialog.askopenfilenames(filetypes[(mp3,.mp3),(flac,.flac),(mgg,.mgg)])# 创建服务对象(这里如果不干掉绑定用户和音乐时就拿不到u_id,因为创建了两个实例对象)# ser Music()# 调用增加音乐的功能将前端的数据发送给后端处理ser.add_music(filenames)if __name____main__:# 获取用户名和密码uname input(请输入用户名:)password input(请输入密码:)# 创建Music类的实例对象ser Music()# 利用实例对象调用实例方法由于会返回布尔值所以直接进行判断if ser.login(uname,password):Ui()else:print(用户名或密码错误!)
问题2报错说数据库已经关闭。
问题分析在获取音乐id时我们通过重构工具类中dml函数使其返回新加的id但执行完sql语句并提交事务后就返回了数据返回数据后就直接运行了finally中关闭数据库的语句。
解决办法重新链接一下数据库即可。 def add_music(self,files:tuple[str]) - None:该函数用于将前台返回的文件路径存储到数据库中:param file:音乐文件的路径,默认是多个,元素类型为字符串:return: None# 编写sql语句sql insert into music_info(name,path) values(%s,%s)# 遍历数据for f in files:print(f)# 根据路径获取歌曲名name f[f.rfind(/)1:f.rfind(.)]print(name)# 创建数据库链接对象db DBUtil()# 准备参数args(name,f)# 将音乐信息添加到music_info表中m_id db.dml_back_id(sql,args)# 上一句调用的函数中由于需要执行return语句所以会直接执行finally语句中关闭数据库的操作因此需要重新链接数据库db DBUtil()# 实现音乐与用户绑定musicBindId_sql insert into play_info(u_id,m_id) values(%s,%s)args(self.user[0],m_id)db.dml(musicBindId_sql,args)
完整service.py文件内容如下
from tools import DBUtilclass Music:def __init__(self):# 用户默认为空self.user Nonedef login(self, uname: str, password: str) - bool::param uname:用户名:param password:密码:return: 登录成功返回True,登录失败返回False# 准备要执行的sql语句sql select * from user_info where uname%s and password%s# 准备sql语句需要的参数args (uname, password)# 创建工具对象(实际时获取数据库的链接对象和游标对象)db DBUtil()# 利用游标对象调用对应的单条查询方法re db.query_one(sql, args)# 由于query_one函数运行成功后是有返回值的可以通过返回值判断是否查询成功进而判断是否有该用户if re:print(登录成功)self.userre# 查看内容是否包含u_id对应的内容print(self.user)print(self.user[0])return Trueelse:print(登录失败)return Falsedef add_music(self,files:tuple[str]) - None:该函数用于将前台返回的文件路径存储到数据库中:param file:音乐文件的路径,默认是多个,元素类型为字符串:return: None# 编写sql语句sql insert into music_info(name,path) values(%s,%s)# 遍历数据for f in files:print(f)# 根据路径获取歌曲名name f[f.rfind(/)1:f.rfind(.)]print(name)# 创建数据库链接对象db DBUtil()# 准备参数args(name,f)# 将音乐信息添加到music_info表中m_id db.dml_back_id(sql,args)# 上一句调用的函数中由于需要执行return语句所以会直接执行finally语句中关闭数据库的操作因此需要重新链接数据库db DBUtil()# 实现音乐与用户绑定musicBindId_sql insert into play_info(u_id,m_id) values(%s,%s)args(self.user[0],m_id)db.dml(musicBindId_sql,args)# 测试代码
if __name__ __main__:# 创建Music类的实例对象service Music()# 利用实例对象调用实例方法re service.login(muxikeqi, 123321)print(re)完整ui.py文件内容如下
import tkinter
from service import Music
import tkinter.filedialogclass Ui:def __init__(self):# 主窗口top tkinter.Tk()# 创建按钮对象第一个参数是父容器,text表示显示的内容b1 tkinter.Button(top, text播放)b2 tkinter.Button(top, text导入音乐)b3 tkinter.Button(top, text删除音乐)# 设置按钮显示位置(row表示放在哪一行,column表示放在哪一列,pad表示距离边框的内边距,单位为像素)b1.grid(row1,column1,padx10,pady10)b2.grid(row1,column2,padx10,pady10)b3.grid(row1,column3,padx10,pady10)# 为导入音乐的按钮添加点击事件,bind方法的第一个参数是事件名称第二个参数是点击事件(点击按钮后触发的函数)b2.bind(Button-1,self.importMusic)# 创建播放列表lisbox tkinter.Listbox(top)# 设置播放列表位置,columnspan表示垮了多少列lisbox.grid(row2,column0,columnspan4,padx10,pady10)# 进入消息循环top.mainloop()# 导入音乐的点击事件def importMusic(self,event):# 弹出选择框(返回的结果是元组)(一定要通过filetypes限制文件类型否则什么文件都能加入进来)filenames tkinter.filedialog.askopenfilenames(filetypes[(mp3,.mp3),(flac,.flac),(mgg,.mgg)])# 创建服务对象(这里如果不干掉绑定用户和音乐时就拿不到u_id,因为创建了两个实例对象)# ser Music()# 调用增加音乐的功能将前端的数据发送给后端处理ser.add_music(filenames)if __name____main__:# 获取用户名和密码uname input(请输入用户名:)password input(请输入密码:)# 创建Music类的实例对象ser Music()# 利用实例对象调用实例方法由于会返回布尔值所以直接进行判断if ser.login(uname,password):uiUi()else:print(用户名或密码错误!)
运行结果如下 运行后的play_info表数据如下 、实现加载播放列表
概述为了后续顺利的完成功能开发先通过navicat的查询窗口尝试写出一个查询语句通过play_info表的u_id找出其对应的音乐名(即music_info表中m_id对应的name)既然涉及到两个表之间不相关列的查询就可以考虑sql99标准下的左(右)外连接查询。
SELECT name FROM play_info p LEFT JOIN music_info m ON p.m_idm.id WHERE u_id1;
运行结果如下 接下来为后端的service.py文件添加一个函数用于查询用户对应的歌曲列表。 def find_user_music(self) - list[str]:本函数用于查询特定用户id对应的音乐列表# 准备sql语句(前面已经准备好了)sql SELECT name FROM play_info p LEFT JOIN music_info m ON p.m_idm.id WHERE u_id%s;# 创建工具类对象db DBUtil();# 执行sql语句(这里使用和上一章同样的方法获取用户id)music_list db.query_many(sql,self.user[0])# 将结果返回return music_list
然后参考菜鸟教程中关于Tkinter的教程了解添加列表的玩法 最后为前端的ui.py文件调用find_user_music函数只要有歌曲被添加或进入窗口都会显示该用户对应的歌曲列表。
import tkinter
from service import Music
import tkinter.filedialogclass Ui:def __init__(self):# 主窗口top tkinter.Tk()# 创建按钮对象第一个参数是父容器,text表示显示的内容b1 tkinter.Button(top, text播放)b2 tkinter.Button(top, text导入音乐)b3 tkinter.Button(top, text删除音乐)# 设置按钮显示位置(row表示放在哪一行,column表示放在哪一列,pad表示距离边框的内边距,单位为像素)b1.grid(row1,column1,padx10,pady10)b2.grid(row1,column2,padx10,pady10)b3.grid(row1,column3,padx10,pady10)# 为导入音乐的按钮添加点击事件,bind方法的第一个参数是事件名称第二个参数是点击事件(点击按钮后触发的函数)b2.bind(Button-1,self.importMusic)# 创建播放列表(为了让importMusic函数能够使用所有为变量传入了对象self)self.lisbox tkinter.Listbox(top)# 设置播放列表位置,columnspan表示垮了多少列self.lisbox.grid(row2,column0,columnspan4,padx10,pady10)self.music_list()# 进入消息循环top.mainloop()# 导入音乐的点击事件def importMusic(self,event):# 弹出选择框(返回的结果是元组)(一定要通过filetypes限制文件类型否则什么文件都能加入进来)filenames tkinter.filedialog.askopenfilenames(filetypes[(mp3,.mp3),(flac,.flac),(mgg,.mgg)])# 创建服务对象(这里如果不干掉绑定用户和音乐时就拿不到u_id,因为创建了两个实例对象)# ser Music()# 调用增加音乐的功能将前端的数据发送给后端处理ser.add_music(filenames)# 获取用户的音乐列表self.music_list()def music_list(self) - list[str]:# 调用获取列表的函数m_lis ser.find_user_music()print(m_lis)for m in m_lis:self.lisbox.insert(0, m[0])if __name____main__:# 获取用户名和密码uname input(请输入用户名:)password input(请输入密码:)# 创建Music类的实例对象ser Music()# 利用实例对象调用实例方法由于会返回布尔值所以直接进行判断if ser.login(uname,password):uiUi()else:print(用户名或密码错误!)
运行后的窗口显示如下 运行结果如下 9、实现通过列表播放音乐
概述本章就做两件事首先确定用户点击的是列表中哪一首歌曲其次用户点击播放按钮后能播放对应的歌曲。
9.1、前端获取歌名
①、首先为播放按钮添加点击事件
b1.bind(ButtonRelease-1, self.play_music)
②、书写对应的点击事件 def play_music(self,event):# 获取当前列表中选中音乐的索引index self.lisbox.curselection()# 根据索引索取音乐的名称music_name self.lisbox.get(index)print(music_name)
运行程序后点击列表中的任意歌曲再点击播放就能顺利的打印歌曲信息此时已经正确地拿到了音乐名。 9.2、后端逻辑实现
①、将获取的歌曲名传给后端service.py文件中play_music函数 def play_music(self,event):# 获取当前列表中选中音乐的索引index self.lisbox.curselection()# 根据索引索取音乐的名称music_name self.lisbox.get(index)# print(music_name)# 将音乐名称传递给服务对象ser.play_music(music_name)
②、实现play_music函数中的sql语句(这里采用左外连接关联了music_info表和play_info表关联的字段是m表的id和p表的m_id条件同时限制p表中的用户名和m表的歌曲名)
select path from music_info m left join play_info p on m.idp.m_id where m.name%s and p.u_id%s
③、补全play_music函数 def play_music(self,music_name:str) - None:播放音乐:param music_name:音乐名称:return: Noneprint(进入play_music函数)# 编写sqlsql select path from music_info m left join play_info p on m.idp.m_id where m.name%s and p.u_id%s;# 创建工具类(链接数据库)db DBUtil()# 运行sql并将返回的结果保存到变量path中args(music_name,self.user[0])path db.query_one(sql,args)[0]print(path)print(len(path))# 调用播放器播放音乐(拷贝play.py文件的代码)# 初始化加载音乐的混合器pygame.mixer.init()# 加载音乐(路径可以是相对路径也可以是绝对路径)pygame.mixer.music.load(path)# 播放音乐pygame.mixer.music.play()
运行结果如下 注意Pygame不支持播放.mgg格式的音乐文件。 9.3、修改bug
问题描述只要点击了导入音乐就会将重新获取的列表添加到旧列表的下面。 为ui.py文件中的music_list函数清空原有列表m_lis def music_list(self) - list[str]:本函数用于获取音乐列表# 调用获取列表的函数m_lis ser.find_user_music()# 清空列表self.lisbox.delete(0,tkinter.END)# 重新加载列表for m in m_lis:self.lisbox.insert(0, m[0]) 10、实现删除音乐
①、为ui.py文件中的删除按钮添加点击事件
b3.bind(ButtonRelease-1, self.delete_music)
②、实现点击事件 def delete_music(self,event):本函数用于删除用户指定的音乐# 获取当前列表中选中音乐的索引index self.lisbox.curselection()# 根据索引索取音乐的名称music_name self.lisbox.get(index)# 调用删除功能ser.delete_music(music_name);# 重新加载播放列表self.music_list()
③、实现delete_music函数 def delete_music(self,music_name:str) - None:# 编写SQLsql_query_m_id select m.id from music_info m left join play_info p on m.idp.m_id where m.name%s and p.u_id%s;sql_delete_music delete from music_info where id%ssql_delete_play delete from play_info where m_id%s and u_id%s# 创建工具类(链接数据库)db DBUtil()args (music_name, self.user[0])# 运行sqlm_id db.query_one(sql_query_m_id, args)[0]# 准备参数args2 (m_id,self.user[0])DBUtil().dml(sql_delete_play,args2)# 注意有外键约束的列要先删除有外键关联列的数据再删除其他数据DBUtil().dml(sql_delete_music,m_id)
运行结果如下 附件完整文件源码(需要自己创建数据库和表)
完整工具类tools.py文件如下
import pymysqlclass DBUtil:工具类用于简化链接和关闭数据库的操作以及对数据的操作# 单独保存链接的参数,可以将其保存到特定文件中直接调用文件获取也可config {host: localhost,user: root,passwd: root,db: db_music,charset: utf8,port: 3306}def __init__(self) - None:构造函数用于获取链接和游标# **指字典对象self.con pymysql.connect(**DBUtil.config)self.cursor self.con.cursor()def close(self) - None:该函数用于关闭链接和游标(关闭前需要判断是否存在)# 存在游标就关闭if self.cursor:self.cursor.close()# 存在链接就关闭if self.con:self.con.close()def dml(self, sql, args):本函数用于封装Mysql的DML语句,用于实现数据的增删改# 进行DML语句操作时一定要注意报错导致无法正常关闭链接try:# 执行sql中的DML语句self.cursor.execute(sql, args)# DML操作一定要通过链接对象提交事务self.con.commit()except Exception as e:print(存在错误,错误信息如下:, e)# 回滚前先判断链接还是否存在if self.con:# 回滚到原状态self.con.rollback()# 使用try...except...finally...最重要的作用就是保证链接正常关闭finally:print(即将关闭链接)self.close()# 重写上面的dml函数,增加返回id的操作def dml_back_id(self, sql, args):本函数用于封装Mysql的DML语句,用于实现数据的增删改# 进行DML语句操作时一定要注意报错导致无法正常关闭链接try:# 执行sql中的DML语句self.cursor.execute(sql, args)# 获取添加的idid self.con.insert_id()print(id)# DML操作一定要通过链接对象提交事务self.con.commit()# 返回idreturn idexcept Exception as e:print(存在错误,错误信息如下:, e)# 回滚前先判断链接还是否存在if self.con:# 回滚到原状态self.con.rollback()# 使用try...except...finally...最重要的作用就是保证链接正常关闭finally:print(即将关闭链接)self.close()def query_one(self, sql, args):本函数用于查询单条数据try:self.cursor.execute(sql, args)re self.cursor.fetchone()return reexcept Exception as e:print(存在错误,错误信息如下:, e)finally:print(即将关闭链接)self.close()def query_many(self, sql, args):本函数用于查询所有数据try:self.cursor.execute(sql,args)# 获取结果并返回数据return self.cursor.fetchall()except Exception as e:print(存在错误,错误信息如下:, e)finally:print(即将关闭链接)self.close()if __name__ __main__:# # 获取链接对象# db DBUtil()# # 准备sql语句及其对应的参数# sql select * from user_info where uname%s and password%s;# args [muxikeqi,123321]# # 运行sql语句并关闭链接# re db.query_one(sql,args)# print(re)pass
完整play.py文件如下
import pygame
import time
# 初始化加载音乐的混合器
pygame.mixer.init()
# 加载音乐(路径可以是相对路径也可以是绝对路径)
pygame.mixer.music.load(f.\music\c.flac)
# 播放音乐
pygame.mixer.music.play()
# 程序进入等待(不要立即关闭程序),单位为秒
time. Sleep(20)
完整ui.py文件内容如下
# import tkinter
from tkinter import *
from service import Music
import tkinter.filedialogclass Ui:def __init__(self):# 主窗口top tkinter.Tk()# top.withdraw()# 创建按钮对象第一个参数是父容器,text表示显示的内容b1 tkinter.Button(top, text播放)b2 tkinter.Button(top, text导入音乐)b3 tkinter.Button(top, text删除音乐)# 设置按钮显示位置(row表示放在哪一行,column表示放在哪一列,pad表示距离边框的内边距,单位为像素)b1.grid(row0,column1,padx10,pady10)b2.grid(row0,column2,padx10,pady10)b3.grid(row0,column3,padx10,pady10)# 为导入音乐的按钮添加点击事件,bind方法的第一个参数是事件名称第二个参数是点击事件(点击按钮后触发的函数)b2.bind(ButtonRelease-1,self.importMusic)# ButtonRelease表示点击并释放了(第一个参数写法固定不要更改)b1.bind(ButtonRelease-1, self.play_music)b3.bind(ButtonRelease-1, self.delete_music)# 创建播放列表(为了让importMusic函数能够使用所有为变量传入了对象self)self.lisbox tkinter.Listbox(top)# 设置播放列表位置,columnspan表示垮了多少列self.lisbox.grid(row1,column0,columnspan4,padx5,pady5)self.music_list()# 进入消息循环top.mainloop()# 导入音乐的点击事件def importMusic(self,event):# 弹出选择框(返回的结果是元组)(一定要通过filetypes限制文件类型否则什么文件都能加入进来)filenames tkinter.filedialog.askopenfilenames(filetypes[(mp3,.mp3),(flac,.flac)])# 创建服务对象(这里如果不干掉绑定用户和音乐时就拿不到u_id,因为创建了两个实例对象)# ser Music()# 调用增加音乐的功能将前端的数据发送给后端处理ser.add_music(filenames)# 获取用户的音乐列表self.music_list()def music_list(self) - list[str]:本函数用于获取音乐列表# 调用获取列表的函数m_lis ser.find_user_music()# 清空列表self.lisbox.delete(0,tkinter.END)# 重新加载列表for m in m_lis:self.lisbox.insert(0, m[0])def play_music(self,event):# 获取当前列表中选中音乐的索引index self.lisbox.curselection()# 根据索引索取音乐的名称music_name self.lisbox.get(index)# 将音乐名称传递给服务对象ser.play_music(music_name)def delete_music(self,event):本函数用于删除用户指定的音乐# 获取当前列表中选中音乐的索引index self.lisbox.curselection()# 根据索引索取音乐的名称music_name self.lisbox.get(index)# 调用删除功能ser.delete_music(music_name);# 重新加载播放列表self.music_list()if __name____main__:# 获取用户名和密码uname input(请输入用户名:)password input(请输入密码:)# 创建Music类的实例对象ser Music()# 利用实例对象调用实例方法由于会返回布尔值所以直接进行判断if ser.login(uname,password):uiUi()else:print(用户名或密码错误!)
完整service.py文件如下
from tools import DBUtil
import pygameclass Music:def __init__(self):# 用户默认为空self.user Nonedef login(self, uname: str, password: str) - bool::param uname:用户名:param password:密码:return: 登录成功返回True,登录失败返回False# 准备要执行的sql语句sql select * from user_info where uname%s and password%s# 准备sql语句需要的参数args (uname, password)# 创建工具对象(实际时获取数据库的链接对象和游标对象)db DBUtil()# 利用游标对象调用对应的单条查询方法re db.query_one(sql, args)# 由于query_one函数运行成功后是有返回值的可以通过返回值判断是否查询成功进而判断是否有该用户if re:print(登录成功)self.userre# 查看内容是否包含u_id对应的内容# print(self.user)# print(self.user[0])return Trueelse:print(登录失败)return Falsedef add_music(self,files:tuple[str]) - None:该函数用于将前台返回的文件路径存储到数据库中:param file:音乐文件的路径,默认是多个,元素类型为字符串:return: None# 编写sql语句sql insert into music_info(name,path) values(%s,%s)# 遍历数据for f in files:# 根据路径获取歌曲名name f[f.rfind(/)1:f.rfind(.)]# 创建数据库链接对象db DBUtil()# 准备参数args(name,f)# 将音乐信息添加到music_info表中m_id db.dml_back_id(sql,args)# 上一句调用的函数中由于需要执行return语句所以会直接执行finally语句中关闭数据库的操作因此需要重新链接数据库db DBUtil()# 实现音乐与用户绑定musicBindId_sql insert into play_info(u_id,m_id) values(%s,%s)args(self.user[0],m_id)db.dml(musicBindId_sql,args)def find_user_music(self) - list[str]:本函数用于查询特定用户id对应的音乐列表# 准备sql语句(前面已经准备好了)sql SELECT name FROM play_info p LEFT JOIN music_info m ON p.m_idm.id WHERE u_id%s;# 创建工具类对象db DBUtil();# 执行sql语句(这里使用和上一章同样的方法获取用户id)music_list db.query_many(sql,self.user[0])# 将结果返回return music_listdef play_music(self,music_name:str) - None:播放音乐:param music_name:音乐名称:return: None# 编写sqlsql select path from music_info m left join play_info p on m.idp.m_id where m.name%s and p.u_id%s;# 创建工具类(链接数据库)db DBUtil()args(music_name,self.user[0])# 运行sql并将返回的结果保存到变量path中path db.query_one(sql,args)[0]# 调用播放器播放音乐(拷贝play.py文件的代码)# 初始化加载音乐的混合器pygame.mixer.init()# 加载音乐(路径可以是相对路径也可以是绝对路径)pygame.mixer.music.load(path)# 播放音乐pygame.mixer.music.play()def delete_music(self,music_name:str) - None:# 编写SQLsql_query_m_id select m.id from music_info m left join play_info p on m.idp.m_id where m.name%s and p.u_id%s;sql_delete_music delete from music_info where id%ssql_delete_play delete from play_info where m_id%s and u_id%s# 创建工具类(链接数据库)db DBUtil()args (music_name, self.user[0])# 运行sqlm_id db.query_one(sql_query_m_id, args)[0]# 准备参数args2 (m_id,self.user[0])DBUtil().dml(sql_delete_play,args2)# 注意有外键约束的列要先删除有外键关联列的数据再删除其他数据DBUtil().dml(sql_delete_music,m_id)# 测试代码
if __name__ __main__:# 创建Music类的实例对象service Music()# 利用实例对象调用实例方法re service.login(muxikeqi, 123321)print(re)