目录
- 一、设计目标
- 二、实现流程
-
- [1. 数据库操作](#1. 数据库操作)
- [2. 后端功能实现](#2. 后端功能实现)
- [3. 前端UI界面实现](#3. 前端UI界面实现)
- [4. 程序入口](#4. 程序入口)
- 三、项目收获
一、设计目标
1. 模拟网易云音乐,实现本地音乐盒 。
2. 功能分析:
- 登录功能
- 窗口显示
- 加载本地音乐
- 建立播放列表
- 播放音乐
- 删除播放列表音乐
3.设计思路:- 前端UI页面:播放按钮、导入音乐按钮、删除按钮、音乐播放列表
- 后端服务端:登录功能、导入音乐的功能、播放音乐的功能、删除音乐的功能
- 为了将前端页面和后端服务更好的结合,在实际实现中,根据设计程序的目标加入一些属性或方法,来帮助程序功能更好的实现。
例如:前端音乐列表的加载,需要在后端服务程序中添加一个从数据库中查找当前用户音乐列表的方法,然后由前端UI界面调用把查找到的音乐加入音乐列表中。
4.数据库组件:- 为了实现功能,需要在数据库中设计的三张表:
- 用户表:存储用户信息,用来实现登录功能
- 音乐表:存储音乐信息(名称、歌手、资源路径),存储所有导入的音乐,用来实现寻找音乐资源进行加载、播放和删除的功能。
- 用户播放列表:存储用户id及其对应的音乐id,且这两个值分别有另外两张表的外键约束,用来正确的显示用户对应的歌曲。
二、实现流程
1. 数据库操作
- 创建数据库
sql
# 创建音乐盒数据库
CREATE DATABASE music_db
DEFAULT CHARACTER SET = 'utf8mb4';
- 使用数据库
sql
USE music_db;
- 创建用户表
sql
CREATE TABLE t_user(
id int(11) PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(32),
password VARCHAR(32)
);
- 创建音乐表
sql
CREATE TABLE t_music(
id int(11) PRIMARY KEY AUTO_INCREMENT,
title VARCHAR(32),
singer VARCHAR(32),
path VARCHAR(256)
);
- 创建用户播放列表
sql
CREATE Table t_play_list(
id int(11) PRIMARY KEY AUTO_INCREMENT,
u_id int(11),
m_id int(11),
FOREIGN KEY(u_id) REFERENCES t_user(id),
FOREIGN KEY(m_id) REFERENCES t_music(id)
);
- 插入用户数据
sql
insert into t_user(username, password) values('张三','123456');
2. 后端功能实现
1.登录功能:
python
# 导包
from tools import DBUtil # 导入工具类:进行数据库上的操作
import pygame # 用来播放音乐
class MusicService:
def __init__(self) -> None:
self.user = None
def login(self,uname,pwd) -> bool:
'''
用户登录功能
:param uname: 用户名
:param pwd: 密码
'''
# 编写sql
sql = "select id,username from t_user where username=%s and password=%s"
# 创建工具类对象
db = DBUtil()
# 执行查询操作
res = db.query_one(sql,uname,pwd)
# 判断是否登录成功
if res:
# print("赋值 self.user", self.user) # 这里应该看到 self.user 被正确赋值
self.user = res
print("登录成功,欢迎使用")
# print(self.user[0])
return True
else:
print("登录失败")
return False
2.导入音乐:
python
def insert_music(self,files:tuple[str]) -> None:
'''
将导入的音乐添加到数据库
'''
# 编写sql
insert_sql = "insert into t_music(title,path) values(%s,%s)"
# 遍历音乐文件
for file in files:
# print(file)
name = file[file.rfind(" ")+1:file.rfind(".")]
# 创建工具类对象
db = DBUtil()
# 执行sql
mid = db.execute_dml_back_id(insert_sql,name,file)
# 维护用户和音乐的对应关系
# 创建工具类对象
db = DBUtil()
# 编写sql
insert_play_list_sql = "insert into t_play_list(u_id,m_id) values(%s,%s)"
# 执行sql
db.execute_dml(insert_play_list_sql,self.user[0],mid)
3.查询用户音乐列表:
python
def find_user_music(self) -> list[str]:
'''
查询用户音乐列表
:return: 音乐列表
'''
# 编写sql
sql = "SELECT m.title from t_play_list p left join t_music m on p.m_id = m.id WHERE u_id = %s;"
# 创建工具类对象
db = DBUtil()
# 执行sql
m_list = db.query_all(sql,self.user[0])
return m_list
4.播放音乐:
python
def play_music(self,music_name:str) -> None:
'''
播放音乐
:param m_name: 音乐名
'''
# 编写sql:根据传入的音乐名查找对应的音乐路径
sql = "SELECT m.path FROM t_play_list p left join t_music m on p.m_id = m.id WHERE u_id = %s and m.title = %s;"
# 执行sql:获取音乐路径
path = DBUtil().query_one(sql,self.user[0],music_name)[0]
# 初始化混合器
pygame.mixer.init()
# 加载音乐
pygame.mixer.music.load(path)
# 播放音乐
pygame.mixer.music.play()
5.删除音乐:
python
def delete_music(self,music_name:str) -> None:
'''
删除音乐
:param: music_name: 音乐名
'''
# 编写sql:根据传入的音乐名查找要删除的音乐id
get_id_sql = "SELECT m.id from t_music m LEFT JOIN t_play_list p on m.id = p.m_id WHERE m.title = %s and p.u_id = %s;"
# 执行sql:获取音乐id
music_id = DBUtil().query_one(get_id_sql,music_name,self.user[0])[0]
# 编写sql,并删除播放列表中对应的音乐
delete_play_list_sql = "DELETE FROM t_play_list WHERE u_id = %s and m_id = %s;"
DBUtil().execute_dml(delete_play_list_sql,self.user[0],music_id)
# 编写sql,并删除音乐表中对应的音乐
delete_music_sql = "DELETE FROM t_music WHERE id = %s;"
DBUtil().execute_dml(delete_music_sql,music_id)
3. 前端UI界面实现
1.初始化软件窗口:
python
# 导包
import tkinter # 引入tkinter,绘画界面
from service import MusicService # 引入服务层
from tkinter.filedialog import askopenfilenames # 引入文件选择框
class PlayWindow:
'''
软件窗口
'''
def __init__(self) -> None:
top = tkinter.Tk()
# 创建按钮
btn1 = tkinter.Button(top,text='播放')
btn2 = tkinter.Button(top,text='导入音乐')
btn3 = tkinter.Button(top,text='删除')
# 创建播放列表
self.listbox = tkinter.Listbox(top)
# 设置按钮的位置
btn1.grid(row=0,column=0,padx=5,pady=5)
btn2.grid(row=0,column=2,padx=5,pady=5)
btn3.grid(row=0,column=4,padx=5,pady=5)
# 设置播放列表的位置
self.listbox.grid(row=1,column=0,columnspan=5,padx=5,pady=5)
# 获取用户音乐播放列表
self.load_music()
# 绑定按钮事件
btn2.bind('<ButtonRelease-1>',self.import_music)
btn1.bind('<ButtonRelease-1>',self.play_music)
btn3.bind('<ButtonRelease-1>',self.delete_music)
# 进入消息循环
top.mainloop()
2.导入音乐按钮事件绑定的方法:
python
def import_music(self,event):
'''
导入音乐
'''
print('导入音乐')
# 弹出文件选择框
filenames = askopenfilenames(filetypes=[('mp3','*.mp3'),('flac','*.flac')])
print(filenames)
# 调用服务层
# ms = MusicService()
# 把导入的音乐插入到数据库中
ms.insert_music(filenames)
# 导入新音乐后刷新播放列表
self.load_music()
3.加载用户音乐播放列表的方法:
python
def load_music(self):
'''
加载用户音乐列表
'''
# 查询当前用户播放列表
music_list = ms.find_user_music()
# 清空播放列表
self.listbox.delete(0,tkinter.END)
# 遍历列表,把音乐名添加到播放列表中
for music in music_list:
self.listbox.insert(0,music[0])
4.播放音乐按钮绑定的方法:
python
def play_music(self,event):
'''
播放音乐
'''
# 获取当前选中的音乐索引
index = self.listbox.curselection()
# 获取当前选中的音乐名
music_name = self.listbox.get(index)
# print(music_name)
# 播放音乐
ms.play_music(music_name)
5.删除音乐按钮绑定的方法:
python
def delete_music(self,event):
'''
删除音乐
'''
# 获取当前选中的音乐索引
index = self.listbox.curselection()
# 根据音乐索引获取音乐名
music_name = self.listbox.get(index)
# 调用后台服务的删除功能
ms.delete_music(music_name)
# 刷新播放列表
self.load_music()
4. 程序入口
python
if __name__ == "__main__":
# 输入登录信息
uname = input('请输入用户名:')
pwd = input('请输入密码:')
# 创建服务对象
ms = MusicService()
if ms.login(uname,pwd):
# 创建播放窗口
pw = PlayWindow()
三、项目收获
- 一种软件设计思维:先明确要设计的软件------>分析软件各个功能并分成模块------>前端页面绘画------>根据前端页面设计与其页面相绑定的后端方法------>后端方法要与数据库交互
- 一个技巧 :在编写不确定的代码的时候,可以使用调试模式,使用调试控制台输入代码快速获得相应的变量输出,从而快速确定要编写的代码。
这样可以有效的避免多次运行才能获得正确代码的情况,这在大型软件的设计中很有用!!!
- 对软件设计的认知 :设计和编写前端页面、根据前端页面实现功能;其中有一个重点:后端服务层的功能重点是与数据库的交互,根据数据库返回的数据进行条件判断,提取数据库的数据进行信息呈现和功能实现。