图形化界面的学生管理系统

注意:本次项目学习与b站,地址为基于python和mysql数据库的图形化界面学生管理系统_哔哩哔哩_bilibili

然后我下面的内容讲的是关于这个项目怎么在本地运行,与每条代码的功能实现

本地运行

概述:本项目一共有四个文件,还有一个数据库,如果我们只想成功运行这个项目的话,我们只需要对mysql_student.py文件中开头的数据库连接对象进行配置即可,其中如果你的数据库在本地,配置起来是非常简单的,如果你的数据库放在远程服务器或者在虚拟机里,以下内容则是这次配置远程数据库

添加mysql配置

概述:默认情况下,mysql可能只允许本地连接,如果是连接虚拟机或者外界的数据库时需要在mysql中的配置文件,MySQL配置文件通常放在/etc/mysql/my.cnf或者/ect/mysqlmysql.conf.d/mysqld.cnf,使用vim编辑器添加如下内容

1
2
[mysqld]
bind-address = 0.0.0.0 # 允许所有 IP 连接

创建用户并授权

概述:创建具有从远程主机连接的权限的mysql用户,可以在mysql命令行工具执行以下命令

1
2
3
CREATE USER 'your_user'@'%' IDENTIFIED BY 'your_password';
GRANT ALL PRIVILEGES ON *.* TO 'your_user'@'%' WITH GRANT OPTION;
FLUSH PRIVILEGES;

启动mysql服务器

概述:使mysql服务端运行

1
2
sudo systemctl status mysql #查看MySQL服务器状态
sudo systemctl start mysql #启动MySQL服务器

防火墙设置

概述:MySQL服务器默认端口为3306。你可以使用以下命令来关闭3306端口的防火强从而允许 MySQL 连接:

1
2
sudo ufw allow 3306 #打开3306端口
sudo ufw deny 3306 #关闭3306端口

查找服务器ip

概述:使用ifcomfig命令查看Linux的ip地址,地址在inet字段后面

源代码与代码解析

概述:理解代码有利于自己以后的服务器开发,下面记录着我对代码的理解

用LoginPage.py实现登录注册界面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
from tkinter import *
from tkinter import messagebox
import mysql_student
from MianPgae import MianPage

class LoginPage:
def __init__(self, master):
self.root = master

# 定义窗口面,设置窗口大小,设置左上角的标题
self.root.geometry('300x150')
self.root.title('登录页')
# 创建一个新的 Frame(一个容器),并将其附加到主窗口 self.root 上
# 同时把这个 Frame 的引用存储在 self.page 中,以便后续使用。这使得你可以在self.page中添加其他的组件,形成一个完整的界面部分。
self.page = Frame(self.root)
# 将窗口的控件进行简单的布局
self.page.pack()

# 通过键盘键入账号,密码的容器StringVar() 是 Python 的 Tkinter 库中的一个类,用于在 Tkinter 应用程序中管理和跟踪字符串类型的变量。
# StringVar() 可以与 Tkinter 控件绑定,使得当变量的值发生变化时,相关控件的显示内容也会自动更新,反之亦然。
self.username = StringVar()
self.password = StringVar()
# 账号框,Label是标签控件,Entry是单行输入框控件,grid() 方法用于将控件放置在一个网格布局中。
# row指定行位置,column指定列位置,pady用于设置上下标签的间距
Label(self.page, text='账号: ').grid(row=1, column=1)
Entry(self.page, textvariable=self.username).grid(row=1, column=2)
# 密码框,textvariable=self.password 是一个 Tkinter 变量(通常是 StringVar 类型)
# 用于跟踪输入框中的文本内容。self.password 应该在类的其他部分定义,用于存储用户输入的密码。
Label(self.page, text='密码: ', ).grid(row=2, column=1, pady=18)
Entry(self.page, show='*', textvariable=self.password).grid(row=2, column=2)
# 注册登录退出三个选项,Button是一个按钮控件
# command=self.register 指定当按钮被点击时要执行的命令。self.register 是一个方法,应该在类的其他部分定义,用于处理注册逻辑。
Button(self.page, text='注册', command=self.register).grid(row=3, column=1, pady=4)
Button(self.page, text='登录', command=self.login).grid(row=3, column=2)
Button(self.page, text='退出', command=self.page.quit).grid(row=3, column=3)


# 注册部分调用add_login类来实现注册新账号
def register(self):
# 释放self.page中的所有组件与框架
self.page.destroy()
add_login(self.root)

# 点击登录按键后去查找账号密码是否正确,不正确发出警告,正确则进入信息页面
def login(self):
uname = self.username.get()
pwd = self.password.get()
# Python可以返回多个值,在这里check_login()函数返回了两个值
flag, message = mysql_student.check_login(uname, pwd) # 通过文件mysql_student的函数判断账号密码是否正确
if flag:
self.page.pack_forget()
MianPage(self.root)
else: messagebox.showwarning(title = '警告', message = message)
# messagebox.showwarning 函数用于显示一个警告对话框,通常用于提示用户某些潜在的问题或警告。
# title: 这是对话框的标题,通常显示在窗口的标题栏中。在你的例子中,title='警告' 表示对话框的标题是“警告”。
# message: 这是要显示的消息内容,通常是一个字符串。在你的例子中,message=message 表示将变量 message 中的内容作为对话框的消息显示。


# 主要负责注册页面的实现
class add_login:
def __init__(self, master):

self.page = master

self.username = StringVar() # 账号容器
self.password = StringVar() # 密码容器
self.password_examine = StringVar() # 二次键入密码容器
self.password_root = StringVar() # 管理员密码容器
# 管理员密码用于查验管理员身份,若一致才能注册账号并登录

self.page.title('注册账号')
self.page.geometry('300x200')

self.root = Frame(self.page)
self.root.pack()

Label(self.root, text='账 号: ').grid(row=1, column=1)
Entry(self.root, textvariable=self.username).grid(row=1, column=2)

# 密码
Label(self.root, text='密 码: ').grid(row=2, column=1, pady=18)
Entry(self.root, show = '*', textvariable=self.password).grid(row=2, column=2)

Label(self.root, text='确 认 密 码: ').grid(row=3, column=1)
Entry(self.root, show = '*', textvariable=self.password_examine).grid(row=3, column=2)

Label(self.root, text= '管理员密码: ').grid(row=4, column=1, pady=18)
Entry(self.root, show='*', textvariable=self.password_root).grid(row=4, column=2)

Button(self.root, text='返 回', command=self.login_page).grid(row=5, column=1)
Button(self.root, text='注 册', command=self.examine).grid(row=5, column=2)

# 用户取消账号注册,选择取消,进入登录界面
def login_page(self):
# 隐藏框架,不会销毁对象,这里替换用destory()函数销毁也是可行的
self.root.pack_forget()
LoginPage(self.page)

# 英文意思检查,检验,用户检验账号密码是否合规,例如长度,账号是否已经被注册,管理员密码是否正确
def examine(self):
self.uname = self.username.get()
self.pwd = self.password.get()
self.pwd_exm = self.password_examine.get()
self.pwd_root = self.password_root.get()
if len(self.uname) < 5:
messagebox.showwarning(title='警告', message='账号不符合要求,请输入最少5位字符')
elif mysql_student.check_usname(self.uname) == True:
messagebox.showwarning(title='警告', message='账号已存在,请更改你的账号')
elif len(self.pwd) < 8:
messagebox.showwarning(title='警告', message='密码不符合要求,请输入最少8位字符')
elif self.pwd != self.pwd_exm:
messagebox.showwarning(title='警告', message='两次密码不相同,请重新输入')
elif self.pwd_root != 'root':
messagebox.showwarning(title='警告', message='管理员密码错误,无法注册账号')
else: self.login()

# 同登录页面的login, 实现账号密码的新添加以及进入主页面,即信息页面
def login(self):
mysql_student.add_admin_name_pwd(self.uname, self.pwd)
self.root.pack_forget()
MianPage(self.page)

if __name__ == '__main__':
# 创建一个Frame对象
page = Tk()
LoginPage(page)
# 当用户关闭窗口时,程序就会终止
page.mainloop()

用MianPgae.py实现主页面,并完善菜单功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
from tkinter import *
from views import ChangeFrame, DeleteFrame, InsertFrame, SearchFrame, HelpFrame
import mysql_student
import keyboard


# 主页面的实现
class MianPage:
def __init__(self, master):
self.root = master
self.root.title('学生信息管理系统')
self.root.geometry('570x290')
self.create_page()

# 用于创建和配置主界面的菜单以及不同的功能框架,通过menu组件创建菜单
def create_page(self):
# 创建了多个功能框架(如插入、查询、删除、修改和帮助)
self.insert_frame = InsertFrame(self.root)
self.search_frame = SearchFrame(self.root)
self.delete_frame = DeleteFrame(self.root)
self.change_frame = ChangeFrame(self.root)
self.help_frame = HelpFrame(self.root)
# 创建一个菜单栏,tearoff=False 表示菜单不会被分离为独立窗口。
menubar = Menu(self.root, tearoff=False)

# 对于菜单按钮的一个实现
# 第一个大按钮录入
menubar.add_command(label=' 录 入 ', command=self.show_insert)

# 第二部分查询按钮,实现最复杂的一部分
# 创建一个子菜单
submenu_search = Menu(menubar)
# 实现降序, 快捷键为ctrl + j
# label: 菜单项的名称,用户在菜单中看到的文本。
# command: 当用户选择该菜单项时要调用的函数(方法)。
# accelerator: 显示在菜单项旁边的快捷键提示,通常用于提高用户操作的效率。
submenu_search.add_command(label = '降 序', command=self.show_search_sort_down,accelerator="Ctrl + J")
# 分割降序和其他部分,降序是是否选,下面的是多选一,默认为选择学号
submenu_search.add_separator()
submenu_search.add_command(label='学 号', command=self.show_search_id, accelerator="Ctrl + D")
submenu_search.add_command(label='总 分', command=self.show_search_total, accelerator="Ctrl + T")
submenu_search.add_command(label='数 学', command=self.show_search_math,accelerator="Ctrl + M")
submenu_search.add_command(label='英 语', command=self.show_search_english, accelerator="Ctrl + E")
submenu_search.add_command(label='计算机', command=self.show_search_computer, accelerator="Ctrl + S")

# 实现快捷键
keyboard.add_hotkey('ctrl+j', self.show_search_sort_down) # 降序快捷键ctrl+j
keyboard.add_hotkey('ctrl+d', self.show_search_id) # 学号快捷键ctrl+d
keyboard.add_hotkey('ctrl+t', self.show_search_total) # 总分快捷键ctrl+t
keyboard.add_hotkey('ctrl+m', self.show_search_math) # 数学快捷键ctrl+m
keyboard.add_hotkey('ctrl+e', self.show_search_english) # 英语快捷键ctrl+e
keyboard.add_hotkey('ctrl+s', self.show_search_computer) # 计算机快捷键ctrl+s

#实现剩下的主菜单栏
menubar.add_cascade(label=' 查 询 ', menu = submenu_search)
menubar.add_command(label=' 删 除 ', command=self.show_delete)
menubar.add_command(label=' 修 改 ', command=self.show_change)
menubar.add_command(label=' 帮 助 ', command=self.show_help)
# 创建的菜单栏 menubar 配置到主窗口 self.root 中,使得菜单可见并可用。
self.root.config(menu = menubar)

# 下面是一个事件处理函数,当用户右键点击时会被调用。
def xShowMenu(event):
menubar.post(event.x_root, event.y_root) # 将菜单条绑定上事件,坐标为x和y的root位置

self.root.bind("<Button-3>", xShowMenu) # 设定鼠标右键触发事件,调用xShowMenu方法
self.show_insert() # 进入程序第一个进入就是插入界面


# 升或降序排列的值由mysql.student.sort_student 的值来决定,每点击一次就异或上一改变其值
def show_search_sort_down(self):
mysql_student.sort_student ^= 1
self.show_search()

# 下面都是剩下几种排序的实现
def show_search_id(self):
mysql_student.sort_data = 0
self.show_search()
def show_search_total(self):
mysql_student.sort_data = 1
self.show_search()
def show_search_math(self):
mysql_student.sort_data = 2
self.show_search()
def show_search_english(self):
mysql_student.sort_data = 3
self.show_search()
def show_search_computer(self):
mysql_student.sort_data = 4
self.show_search()

# 创建某一个页面需要把前面页面留下的东西给pack_forget()来清空一下,然后在实现功能
def show_insert(self):
self.insert_frame.pack()
self.search_frame.pack_forget()
self.delete_frame.pack_forget()
self.change_frame.pack_forget()
self.help_frame.pack_forget()

def show_search(self):
self.insert_frame.pack_forget()
self.delete_frame.pack_forget()
self.change_frame.pack_forget()
self.help_frame.pack_forget()
self.search_frame.pack()
# 显示学生信息
self.search_frame.show_search_data()

def show_delete(self):
self.delete_frame.pack()
self.insert_frame.pack_forget()
self.search_frame.pack_forget()
self.change_frame.pack_forget()
self.help_frame.pack_forget()

def show_change(self):
self.change_frame.pack()
self.insert_frame.pack_forget()
self.search_frame.pack_forget()
self.delete_frame.pack_forget()
self.help_frame.pack_forget()
def show_help(self):
self.change_frame.pack_forget()
self.insert_frame.pack_forget()
self.search_frame.pack_forget()
self.delete_frame.pack_forget()
self.help_frame.pack()

用mysql_student.py对远程数据库的连接与数据操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197

from tkinter import *
import pymysql
# 创建连接数据库student的对象conn
conn = pymysql.connect(
host='xxxx.xxxx.xxxx.xxxx', # 数据库主机名或者ip
port=xxx, # 数据库端口号,默认为3306,需要把防火墙打开,或者指定主机
user='xxx', # 数据库用户名
password='xxx', # 数据库密码
autocommit=True # 设置修改数据无需确认
)
# 获取游标对象
sort_student = int(0)
sort_data = int(0)

cursor = conn.cursor() # 创建一个游标对象的关键步骤,之后你可以使用这个游标来执行 SQL 语句

# 创建数据库,若有则不创建
cursor.execute("create database if not exists student;")
conn.select_db("student") # 建立与数据库的连接

# 创建学生信息表, 若有则不创建
cursor.execute("""CREATE TABLE IF NOT EXISTS students(
id int,
name varchar(10),
kulas varchar(10),
math int,
english int,
computer int,
total int
);""")


# 创建账号密码表,若有则不创建
cursor.execute("""CREATE TABLE IF NOT EXISTS admin_name_pwd(
name varchar(10),
pwd varchar(10)
);""")


# 判断登录的账号密码是否都正确
def check_login(uname, pwd):
cursor.execute("select * from admin_name_pwd")
results = cursor.fetchall() # 是游标对象的方法之一,用于获取查询结果的所有行。它返回一个列表,其中每一项都是一个表示一行数据的元组。如果没有更多的行可供提取,它将返回一个空列表。
# print(results) # 用于测试
for na, pd in results:
if na == uname and pd == pwd:
return True, '登录成功'
return False, '登录失败,账号或密码错误'

# 添加正确注册的账号以及密码
def add_admin_name_pwd(uname, pwd):
cursor.execute("insert into admin_name_pwd values('{0}', '{1}');".format(uname, pwd))

# 检验注册的账号名称是否已经存在
def check_usname(uname):
cursor.execute("select count(*) from admin_name_pwd anp where name = '{0}';".format(uname))
res = cursor.fetchall()
if res[0][0]:
return True
return False


# 获取数据库中学生所有信息,按给定的信息给出
# 通过全局变量sort_data以及sort_student
# sort_student 为0代表升序,为一代表降序
def all():
if sort_student == 1:
if sort_data == 0:
cursor.execute("select * from students order by id;")
elif sort_data == 1:
cursor.execute("select * from students order by total;")
elif sort_data == 2:
cursor.execute("select * from students order by math;")
elif sort_data == 3:
cursor.execute("select * from students order by english;")
elif sort_data == 4:
cursor.execute("select * from students order by computer;")
else:
if sort_data == 0:
cursor.execute("select * from students order by id desc;")
elif sort_data == 1:
cursor.execute("select * from students order by total desc;")
elif sort_data == 2:
cursor.execute("select * from students order by math desc;")
elif sort_data == 3:
cursor.execute("select * from students order by english desc;")
elif sort_data == 4:
cursor.execute("select * from students order by computer desc;")
data = cursor.fetchall()
key = ('id', 'name', 'kulas', 'math', 'english', 'computer', 'total')
jsonList = []
# 通过数据得到的数据是元组类型,需要压缩成字典类型便于输出
for i in data:
jsonList.append(dict(zip(key, i)))
return jsonList

# 查询录入的学号是否存在

def check_student_id(id):
cursor.execute("select count(*) from students where id = '{0}';".format(id))
res = cursor.fetchall()
if res[0][0]:
return False, "该学号已存在请重新输入"
return True, '录入成功'


# 单独查询某个班级的成绩
def search_kulas(kulas_value):
cursor.execute("select * from students where kulas = '{0}';".format(kulas_value))
data = cursor.fetchall()
key = ('id', 'name', 'kulas', 'math', 'english', 'computer', 'total')
jsonList = []
# 通过数据得到的数据是元组类型,需要压缩成字典类型便于输出
for i in data:
jsonList.append(dict(zip(key, i)))
return jsonList
# 插入一条学生信息
def insert(stu):
cursor.execute("insert into students values('{0}', '{1}', '{2}','{3}', '{4}', '{5}', '{6}');".
format(stu[0], stu[1], stu[2], stu[3], stu[4], stu[5], stu[6]))

# 通过id来删除学生信息
def delete_id(user_id):
cursor.execute("select count(*) from students where id = '{0}';".format(user_id))
res = cursor.fetchall()
if res[0][0]:
cursor.execute("delete from students where id = '{0}';".format(user_id))
return True, '删除成功'
else: return False, '学号为' + str(user_id) + '的学生不存在'

# 通过名字来删除学生信息
def delete_name(user_name):
cursor.execute("select count(*) from students where name = '{0}';".format(user_name))
res = cursor.fetchall()
# print(res)
if res[0][0]:
cursor.execute("delete from students where name = '{0}';".format(user_name))
return True, '删除成功'
else: return False, '姓名为' + str(user_name) + '的学生不存在'


# 通过id来查询学生的信息
def search_id(user_id):
cursor.execute("select count(*) from students where id = '{0}';".format(user_id))
res = cursor.fetchall()
if res[0][0]:
cursor.execute("select * from students where id = '{0}';".format(user_id))
stu = cursor.fetchall()
return True, stu
else:
return False, '学号为' + str(user_id) + '的学生不存在'

# 通过学生姓名来查询剩余的信息
def search_name(user_name):
cursor.execute("select count(*) from students where name = '{0}';".format(user_name))
res = cursor.fetchall()
if res[0][0]:
cursor.execute("select * from students where name = '{0}';".format(user_name))
stu = cursor.fetchall()
return True, stu
else:
return False, '名字为' + str(user_name) + '的学生不存在'

# 下面内容是初始化数据库,不过需要手动解开注释
tuple = (
(20, '徐寒研', '软件开发4班', 68, 59, 86, 213),
(19, '荣浩博', '软件开发4班', 56, 83, 20, 159),
(18, '刘德泽', '软件开发4班', 78, 83, 89, 250),
(17, '陈涵梁', '软件开发4班', 68, 99, 67, 234),
(16, '宋明玉', '软件开发4班', 79, 72, 90, 241),
(15, '邓海洋', '软件开发4班', 68, 47, 89, 204),
(14, '快乐男孩', '软件开发4班', 79, 78, 48, 205),
(13, '周解青', '软件开发4班', 69, 78, 82, 229),
(12, '帅哥', '软件开发4班', 72, 47, 88, 207),
(11, '金十一', '物联网3班', 84, 68, 92, 244),
(10, '郑十', '物联网2班', 81, 75, 88, 244),
(9, '吴九', '大数据1班', 92, 87, 61, 240),
(8, '周八', '软件土木3班', 87, 71, 92, 250),
(7, '孙七', '计算机1班', 64, 76, 83, 223),
(6, '赵六', '软件开发4班', 48, 86, 75, 209),
(5, '王五', '软件金融2班', 78, 92, 62, 232),
(4, '李四', '软件会计2班', 80, 83, 45, 208),
(3, '张三', '软件土木5班', 61, 72, 77, 210),
(2, '陈二', '计算机5班', 81, 67, 72, 220),
(1, '刘一', '软件开发4班', 60, 85, 67, 212))

# 手动解除即可将这些信息添加进数据库中,使用之后需重新注释
"""
# 往student中加入信息,若有则不加入
for stu in tuple:
if check_student_id(stu[0])[0] == True:
insert(stu)
"""
# 加入初始账号,若有则不加入
if check_usname("root") == False:
add_admin_name_pwd('root', 'root')

用views.py实现各项功能的框架

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
from tkinter import *
from tkinter import ttk
import mysql_student
from tkinter import messagebox


# 实现录入页面的类
class InsertFrame(Frame):
def __init__(self, root):
super().__init__(root)
self.id = StringVar()
self.name = StringVar()
self.kulas = StringVar()
self.math = StringVar()
self.english = StringVar()
self.computer = StringVar()

# 打印录入是否成功信息
self.status_insert = StringVar()

self.insert_page()


# 打印修输入的项目以及输入框
def insert_page(self):
Label(self, text='学 号 : ').grid(row=1, column=1, pady=5)
self.entry_id = Entry(self, textvariable=self.id)
self.entry_id.grid(row=1, column=2, pady=5)

Label(self, text = '姓 名 : ').grid(row=2, column = 1, pady=5)
self.entry_name = Entry(self, textvariable=self.name)
self.entry_name.grid(row = 2, column = 2, pady = 5)

Label(self, text='班 级 : ').grid(row=3, column=1, pady=5)
self.entry_kulas = Entry(self, textvariable=self.kulas)
self.entry_kulas.grid(row=3, column=2, pady=5)

Label(self, text='数 学 : ').grid(row=4, column=1, pady=5)
self.entry_math = Entry(self, textvariable=self.math)
self.entry_math.grid(row=4, column=2, pady=5)

Label(self, text='英 语 : ').grid(row=5, column=1, pady=5)
self.entry_english = Entry(self, textvariable=self.english)
self.entry_english.grid(row=5, column=2, pady=5)

Label(self, text='计算机 : ').grid(row=6, column=1, pady=5)
self.entry_computer = Entry(self, textvariable=self.computer)
self.entry_computer.grid(row=6, column=2, pady=5)

Button(self, text = '清空', command = self.insert_deleteValue).grid(row=7, column=1, pady=10)
Button(self, text = '录入', command = self.insert_data).grid(row = 7, column = 3, pady = 10)

Label(self, textvariable=self.status_insert).grid(row=8, column=2, padx=10)
# 输出所有学生信息
def insert_data(self):
# 检查信息是否为空,为空赋初始值
if not self.id.get():
self.insert_id = int(0)
else: self.insert_id = int(self.id.get())

if not self.name.get():
self.insert_name = 'NULL'
else: self.insert_name = self.name.get()

if not self.kulas.get():
self.insert_kulas = 'NULL'
else: self.insert_kulas = self.kulas.get()

if not self.math.get():
self.insert_math = int(0)
else: self.insert_math = int(self.math.get())

if not self.english.get():
self.insert_english = int(0)
else: self.insert_english = int(self.english.get())

if not self.computer.get():
self.insert_computer = int(0)
else: self.insert_computer = int(self.computer.get())

flag, s = mysql_student.check_student_id(self.insert_id) # 查询id
self.status_insert.set(s)
if flag == False: # 若已经存在的情况,不能录入
return
self.insert_total = self.insert_math + self.insert_computer + self.insert_english
stu = (self.insert_id, self.insert_name, self.insert_kulas, self.insert_math,
self.insert_english, self.insert_computer, self.insert_total)
mysql_student.insert(stu) # 这一部分为存在并导入信息


# 删除输入框中的内容
def insert_deleteValue(self):
self.entry_id.delete(0, END)
self.entry_name.delete(0, END)
self.entry_kulas.delete(0, END)
self.entry_math.delete(0, END)
self.entry_english.delete(0, END)
self.entry_computer.delete(0, END)


# 实现查找功能的类
class SearchFrame(Frame):
def __init__(self, root):
super().__init__(root)

self.table_search_view = Frame()

self.show_table_search()

# 实现显示查询页面的整个大框架分布
def show_table_search(self):
columns = ("id", "name", "kulas", "math", "english", "computer", "total")
columns_values = ("学号", "姓名", "班级", "数学", "英语", "计算机", "总分")
# 创建一个表格,使数据都在这个表格显示
# show = 'headings'是显示表头,columns = columns定义表中的列
self.tree_view = ttk.Treeview(self, show = 'headings', columns = columns)

for col in columns: # 设置每列的宽度与对其方式
self.tree_view.column(col, width = 80, anchor = 'center')

for col, colvalue in zip(columns, columns_values): # zip 函数将 columns 和 columns_values 配对
self.tree_view.heading(col, text = colvalue) # 设置每一列的表头名称。

self.tree_view.pack(fill = BOTH, expand = True) # 将 Treeview 控件添加到窗口中,并设置其填充和扩展方式,使其在窗口大小变化时能够适应。
self.show_search_data()

self.kulas_kulas = StringVar()
Entry(self, textvariable=self.kulas_kulas).pack(side = LEFT)
Button(self, text='按班查询', command=self.search_kulas).pack(side=LEFT) # , command = self.treeviewClick
Button(self, text = '删 除', command=self.treeviewClick).pack(side = RIGHT)

# 下面循环加函数是实现点击标题实现排序
# 处理数字列的排序
def treeview_sort_column1(tv, col, reverse): # Treeview、列名、排列方式
l = [(tv.set(k, col), k) for k in tv.get_children('')]
l.sort(key=lambda t: int(t[0]), reverse=reverse) # 排序方式
for index, (val, k) in enumerate(l): # 根据排序后索引移动
tv.move(k, '', index)
tv.heading(col, command=lambda: treeview_sort_column1(tv, col, not reverse)) # 重写标题,使之成为再点倒序的标题
self.tree_color() # 启动程序,根据奇偶行设为不同的背景颜色
# 处理文本列的排序
def treeview_sort_column2(tv, col, reverse): # Treeview、列名、排列方式
l = [(tv.set(k, col), k) for k in tv.get_children('')]
l.sort(reverse=reverse) # 排序方式
for index, (val, k) in enumerate(l): # 根据排序后索引移动
tv.move(k, '', index)
tv.heading(col, command=lambda: treeview_sort_column2(tv, col, not reverse)) # 重写标题,使之成为再点倒序的标题
self.tree_color() # 启动程序,根据奇偶行设为不同的背景颜色

for i in range(7): # 给所有标题加(循环上边的“手工”)
if i >= 1 and i <=2:
self.tree_view.heading(columns[i], text=columns_values[i], command=lambda _col=columns[i]: treeview_sort_column2(self.tree_view, _col, False))
else: self.tree_view.heading(columns[i], text = columns_values[i], command=lambda _col = columns[i]: treeview_sort_column1(self.tree_view, _col, False))
# 定义背景色风格
self.tree_view.tag_configure('even', background='lightblue') # even标签设定为浅蓝色背景颜色

def search_kulas(self): # 根据输入的班级查询学生信息并更新表格。
for _ in map(self.tree_view.delete, self.tree_view.get_children('')):
pass
if not self.kulas_kulas.get():
self.show_search_data()
return
else:
self.kulas_value = self.kulas_kulas.get()
students = mysql_student.search_kulas(self.kulas_value)

index = -1
for stu in students: # index + 1 用于将新数据插入到最后一行
self.tree_view.insert('', index + 1, values = (
stu['id'], stu['name'], stu['kulas'], stu['math'],
stu['english'], stu['computer'], stu['total']
))
self.tree_color() # 启动程序,根据奇偶行设为不同的背景颜色

def treeviewClick(self): # 单击
for item in self.tree_view.selection():
item_text = self.tree_view.item(item, "values")
mysql_student.delete_id(item_text[0]) # 删除所选行的第一列的值
self.show_search_data()

# 显示数据库中学生信息表上的信息
def show_search_data(self):

for _ in map(self.tree_view.delete, self.tree_view.get_children('')): # 删除原本显示的数据
pass
students = mysql_student.all() # 获取数据库中的信息并以字典形式返回
index = -1
for stu in students:
self.tree_view.insert('', index + 1, values=(
stu['id'], stu['name'], stu['kulas'], stu['math'],
stu['english'], stu['computer'], stu['total']
))
self.tree_color() # 启动程序,根据奇偶行设为不同的背景颜色

def tree_color(self): # 表格栏隔行显示不同颜色函数
items = self.tree_view.get_children() # 得到根目录所有行的iid
i = 0 # 初值
for hiid in items:
if i / 2 != int(i / 2): # 判断奇偶
tag1 = '' # 奇数行
else:
tag1 = 'even' # 偶数行
self.tree_view.item(hiid, tag=tag1) # 偶数行设为浅蓝色的tag='even'
i += 1 # 累加1

# 实现删除页面的类
class DeleteFrame(Frame):
def __init__(self, root):
super().__init__(root, width = 570, height = 290)

self.delete_student = StringVar()
self.status_student = StringVar()

Label(self, text='请输入需要删除学生的').place(x = 40, y = 60)
Label(self, text='姓名或者学号').place(x=64, y=80)
Entry(self, textvariable=self.delete_student).place(x = 30, y = 100)
Button(self, text='按学号查询', command=self.id_delete).place(x = 30, y = 130)
Button(self, text='按姓名查询', command=self.name_delete).place(x=110, y=130)
Label(self, textvariable=self.status_student).place(x = 45, y = 160)

self.id = StringVar()
self.name = StringVar()
self.kulas = StringVar()
self.math = StringVar()
self.english = StringVar()
self.computer = StringVar()

Label(self, text = '学 号 :').place(x=300, y=20)
Label(self, textvariable=self.id).place(x=360, y=20)
Label(self, text='姓 名 :').place(x=300, y=50)
Label(self, text = '姓 名 :', textvariable=self.name).place(x=360, y=50)
Label(self, text='班 级 :').place(x=300, y=80)
Label(self, text = '班 级 :', textvariable=self.kulas).place(x=360, y=80)
Label(self, text='数 学 :').place(x=300, y=110)
Label(self, textvariable=self.math).place(x=360, y=110)
Label(self, text='英 语 :').place(x=300, y=140)
Label(self, textvariable=self.english).place(x=360, y=140)
Label(self, text='计算机 :').place(x=300, y=170)
Label(self, textvariable=self.computer).place(x=360, y=170)

self.status_delete = StringVar()
Button(self, text='删 除', command=self.delete_stu).place(x=340, y=210)
Label(self, textvariable=self.status_delete).place(x=300, y=250)

# 通过学号来删除对应学生的信息
def id_delete(self):
if self.delete_student.get():
self.search_user_id = self.delete_student.get()
flag, stu = mysql_student.search_id(self.search_user_id)
if flag:
self.id.set(stu[0][0]),self.name.set(stu[0][1])
self.kulas.set(stu[0][2]),self.math.set(stu[0][3])
self.english.set(stu[0][4]),self.computer.set(stu[0][5])
self.status_student.set('数据查询成功')
else:
self.status_student.set(stu)
else:
self.status_student.set('请输入需要查询的信息')
def name_delete(self):
if self.delete_student.get():
self.search_user_name = self.delete_student.get()
flag, stu = mysql_student.search_name(self.search_user_name)
if flag:
self.id.set(stu[0][0])
self.name.set(stu[0][1])
self.kulas.set(stu[0][2])
self.math.set(stu[0][3])
self.english.set(stu[0][4])
self.computer.set(stu[0][5])
self.status_student.set('数据查询成功')
else:
self.status_student.set(stu)
else:
self.status_student.set('请输入需要查询的信息')

def delete_stu(self):
flag, str = mysql_student.delete_id(self.id.get())
if not self.id.get():
str = '需要删除信息不能NULL'
self.status_delete.set(str)


# 实现修改页面的类
class ChangeFrame(Frame):
def __init__(self, root):
super().__init__(root, width = 570, height = 290)
# Label(self, text = '修改页面').pack()
# 查询的修改信息,以及查询、修改成功的提示
self.change_student = StringVar()
self.status_student = StringVar()
self.status_name = StringVar()


# 存储学生信息已经更改的变量
self.id = StringVar()
self.name = StringVar()
self.kulas = StringVar()
self.math = StringVar()
self.english = StringVar()
self.computer = StringVar()

# 用于存储修改之前的信息
self.id_change_before = StringVar()
self.name_change_before = StringVar()
self.kulas_change_before = StringVar()
self.math_change_before = StringVar()
self.english_change_before = StringVar()
self.computer_change_before = StringVar()

self.insert_page()



# 打印修输入的项目以及输入框
def insert_page(self):

Label(self, text='请输入需要查询学生的').place(x=40, y=60)
Label(self, text='姓名或者学号').place(x=64, y=80)
Entry(self, textvariable=self.change_student).place(x=30, y=100)
Button(self, text='按学号查询', command=self.id_change).place(x=30, y=130)
Button(self, text='按姓名查询', command=self.name_change).place(x=110, y=130)
Label(self, textvariable=self.status_student).place(x=45, y=160)

Label(self, text='学 号 : ').place(x=240, y=20)
Label(self, textvariable=self.id_change_before).place(x=320, y=20)
self.entry_id = Entry(self, textvariable=self.id)
self.entry_id.place(x=380, y=20)

Label(self, text='姓 名 : ').place(x=240, y=50)
Label(self, textvariable=self.name_change_before).place(x=315, y=50)
self.entry_name = Entry(self, textvariable=self.name)
self.entry_name.place(x=380, y=50)

Label(self, text='班 级 : ').place(x=240, y=80)
Label(self, textvariable=self.kulas_change_before).place(x=300, y=80)
self.entry_kulas = Entry(self, textvariable=self.kulas)
self.entry_kulas.place(x=380, y=80)

Label(self, text='数 学 : ').place(x=240, y=110)
Label(self, textvariable=self.math_change_before).place(x=318, y=110)
self.entry_math = Entry(self, textvariable=self.math)
self.entry_math.place(x=380, y=110)

Label(self, text='英 语 : ').place(x=240, y=140)
Label(self, textvariable=self.english_change_before).place(x=318, y=140)
self.entry_english = Entry(self, textvariable=self.english)
self.entry_english.place(x=380, y=140)

Label(self, text='计算机 : ').place(x=240, y=170)
Label(self, textvariable=self.computer_change_before).place(x=318, y=170)
self.entry_computer = Entry(self, textvariable=self.computer)
self.entry_computer.place(x=380, y=170)

Button(self, text='修 改', command = self.create_user).place(x=320, y=220)

Label(self, textvariable=self.status_name).place(x=305, y=250)

# 通过学号或者姓名来查询学生信息,优先通过学号,若未输入学号,则通过姓名来查询
def id_change(self):
if self.change_student.get():
self.search_user_id = self.change_student.get()
flag, stu = mysql_student.search_id(self.search_user_id)
if flag:
self.change_Information(stu)
self.status_student.set('数据查询成功')
else:
self.status_student.set(stu)
else:
self.status_student.set('请输入需要查询的信息')

def name_change(self):
if self.change_student.get():
self.search_user_name = self.change_student.get()
flag, stu = mysql_student.search_name(self.search_user_name)
if flag:
self.change_Information(stu)
self.status_student.set('数据查询成功')
else:
self.status_student.set(stu)
else:
self.status_student.set('请输入需要查询的信息')
def change_Information(self, stu):
self.id.set(stu[0][0])
self.name.set(stu[0][1])
self.kulas.set(stu[0][2])
self.math.set(stu[0][3])
self.english.set(stu[0][4])
self.computer.set(stu[0][5])
self.id_change_before.set(stu[0][0])
self.name_change_before.set(stu[0][1])
self.kulas_change_before.set(stu[0][2])
self.math_change_before.set(stu[0][3])
self.english_change_before.set(stu[0][4])
self.computer_change_before.set(stu[0][5])
# 通过获取输入框的信息来修改数据库中学生的信息
def create_user(self):
if not self.id.get():
self.insert_id = int(0)
self.status_name.set('请输入修改的学号')
return
else:
self.insert_id = int(self.id.get())

if not self.name.get():
self.insert_name = 'NULL'
else:
self.insert_name = self.name.get()

if not self.kulas.get():
self.insert_kulas = 'NULL'
else:
self.insert_kulas = self.kulas.get()

if not self.math.get():
self.insert_math = int(0)
else:
self.insert_math = int(self.math.get())

if not self.english.get():
self.insert_english = int(0)
else:
self.insert_english = int(self.english.get())

if not self.computer.get():
self.insert_computer = int(0)
else:
self.insert_computer = int(self.computer.get())
mysql_student.delete_id(self.id_change_before.get())
self.insert_total = self.insert_math + self.insert_computer + self.insert_english
stu = (self.insert_id, self.insert_name, self.insert_kulas, self.insert_math,
self.insert_english, self.insert_computer, self.insert_total)
mysql_student.insert(stu)

self.status_name.set('数据修改成功')

# 实现帮助页面的类,主要打印一些程序运行的帮助以及规则
class HelpFrame(Frame):
def __init__(self, root):
super().__init__(root)

Label(self, text = '关于录入界面').pack()
Label(self, text = '可以录入所有信息为空的信息,但不建议,且学号具有唯一性').pack()
Label(self, text = ' ').pack()
Label(self, text = '关于查询界面').pack()
Label(self, text = '默认为升序排列,可以根据学生的各类信息进行排列,并能通过快捷键以及鼠标右键实现一定的功能').pack()
Label(self, text = '可以查看班级信息以及可以选择信息进行删除').pack()
Label(self, text = ' ').pack()
Label(self, text = '关于删除界面').pack()
Label(self, text = '可以根据学号或者姓名对学生信息进行删除,学号是唯一的').pack()
Label(self, text = ' ').pack()
Label(self, text = '关于修改界面').pack()
Label(self, text = '可以通过学号或者姓名来查询学学生信息,但查询名字只会出现第一位学生,按下修改键出现提示即成功').pack()