基于Tkinter开发学生成绩管理系统,能够实现增删改查操作和学生成绩统计以及生成排名报表
功能:增删改查,成绩排名,生成报表
db文件(MySQL连接)
class pydb:
def datadb(self, dsql):
# 建立数据库连接
global db
try:
db = pymysql.connect(host="localhost", user="root", password="ZZXQJL@0916.com", database="mybatis",charset="utf8")
print("数据库连接成功")
except pymysql.Error as e:
print("数据库连接失败:" + str(e))
# cursor() — 数据库连接操作 python:https://www.cnblogs.com/qixu/p/6133429.html
# 使用cursor()方法获取操作游标
cursor = db.cursor()
self.sql = dsql
# 执行数据库操作
# 使用execute方法执行SQL语句
cursor.execute(self.sql)
# 使用 fetchone() 方法获取一条数据
data = cursor.fetchall()
# 提交事务
db.commit()
# 关闭数据库连接
db.close()
# 返回数据
return data
controller文件(功能实现)
import db
class controller:
global dat # 创建全局dat
dat = db.pydb() # 创建数据库对象
def cha(self, id): # 对学号进行模糊查询
self.id = id
sql = "select * from student where id like '%" + self.id + "%'"
x = list(dat.datadb(sql)) # 获取数据 将数据由元组改为列表
for i in range(len(x)): # 将每一层数据更改成列表
x[i] = list(x[i])
n = x[i][3] + x[i][4] + x[i][5] # 计算总成绩
x[i].append(n)
return x
def pai(self): # 对成绩排名
sql = "select * from student"
x = list(dat.datadb(sql)) # 获取数据 将数据由元组改为列表
for i in range(len(x)): # 将每一层数据更改成列表
x[i] = list(x[i])
n = x[i][3] + x[i][4] + x[i][5] # 计算总成绩
x[i].append(n)
for i in range(1, len(x)):
# 从第二个元素开始,每次取出一个元素,插入前面的序列使其有序
for j in range(i, 0, -1): # 插入排序
if x[j][6] > x[j - 1][6]:
x[j], x[j - 1] = x[j - 1], x[j]
return x
def biao(self):
x = self.pai() # 调用排序表
for i in range(len(x)): # 将数据全部转为str
for j in range(7):
x[i][j] = str(x[i][j])
t = [["学号", "姓名", "性别", "数学", "英语", "语文", "排名"]] + x # 给index的列表设置表名
f = open("student.csv", "w")
for i in range(len(t)): # 自动换行输出
for j in range(7):
f.write(t[i][j] + ',')
f.write('\n')
print("完成")
f.close()
def zeng(self, s):
self.s = s
# try:
sql = "insert into student values(%d,'%s','%s',%d,%d,%d)" % (
self.s[0], self.s[1], self.s[2], self.s[3], self.s[4], self.s[5])
dat.datadb(sql) # 插入数据
def geng(self, s):
self.s = s
sql = "update student set name ='" + self.s[1] + "',sex='" + self.s[2] + "',math=" + self.s[3] + ",chinese=" + \
self.s[4] + ",english=" + self.s[5] + " where id =" + self.s[0]
dat.datadb(sql) # 更新数据
def shan(self, n):
self.n = n
sql = "delete from student where id=" + self.n
dat.datadb(sql) # 删除数据
index文件(系统界面)
import tkinter.ttk as tk
import csv
from tkinter import *
from controller import controller
root = Tk() # 通过Tk的无参构造函数创建应用程序主窗口
root.title("作者:简书:每天起床都想摆") # 设置左上角的窗口标题
root.geometry('1000x550') # 使用geometry自定义设置窗口尺寸
root.resizable(width=False, height=False) # 设置窗口为不可更改 固定窗口
# tkinter默认支持的是gif
photo = PhotoImage(file="img.gif")
#图片应用设置应用
Label(root,image=photo).pack()
# Tkinter标签控件(Label)用法:https://www.runoob.com/python/python-tk-label.html
# 用法:w = Label ( master, option, ... )
# master: 框架的父容器 此处是root
# options: 该标签的可设置的属性,以逗号分隔。
# text 设置文本,可以包含换行转义字符
# bg 标签背景颜色
# 设置前景色
# font 设置字体
# w.pack() 调用pack方法,调整其显示位置和大小,pack是按添加顺序排列组件
# side 定义停靠在父组件的哪一边上 “ top ” , “ bottom ” , “left”, “right” (默认为”top”)
# fill 填充 x(y)方向上的空间,当属性 side=”top”或” bottom”时,填充 x 方向
# Python Tkinter Place布局管理器用法:https://www.cnblogs.com/jsdd/articles/11482023.html
# x:指定组件的 X 坐标。x 为 0 代表位于最左边。 y:指定组件的 Y 坐标。y 为 0 代表位于最右边。
Label(root, text='学生成绩管理系统', bg='white', fg='red', font=('宋体', 15)).place(x=430,y=10)
Label(root, text="学号查询" ).place(x=360, y=100)
con = controller() # 创建控制器对象
data = con.cha('') # 获取首次全部数据,即首次启动系统显示的数据
def search(): # 模糊查询
x = tree.get_children() # 清空所有页面数据
for i in x:
tree.delete(i)
data = con.cha(u.get()) # 获取数据
for i in range(len(data)): # 数据输出
tree.insert('', 'end', values=data[i])
def paiMing(): # 分数排名
x = tree.get_children() # 清空所有页面数据
for i in x:
tree.delete(i)
data = con.pai()
for i in range(len(data)): # 数据输出
tree.insert('', 'end', values=data[i])
def baobiao():
con.biao() # 调用生成文件报表方法
tkinter.messagebox.showinfo('已完成', '请查看student.csv文件') # 弹出消息框确认完成
# 在使用界面编程的时候,有些时候是需要跟踪变量的值的变化,以保证值的变更随时可以显示在界面上。
# 由于python无法做到这一点,所以使用了tcl的相应的对象,也就是StringVar、BooleanVar、DoubleVar、IntVar所需要起到的作用
# StringVar类似于Java中的String
# 设定一个空字符串接收输入的值
# 双向绑定
u = StringVar()
# 通过textvariable来获取框中值
# Tkinter 组件:Entry,Entry(输入框)组件通常用于获取用户的输入文本。
Entry(root, width=20, textvariable=u).place(x=420, y=100)
# Button 控件是一种标准 Tkinter 控件, 用来展现不同样式的按钮.
# Button 控件被用以和用户交互, 比如按钮被鼠标点击后, 某种操作被启动.
# 和 Label 控件类似, 按钮可以展示图片或者文字.
# 不同的是, Label 控件可以指定字体, Button 控件只能使用单一的字体. Button 上的文字可以多行显示.
# command类型:回调;
# 说明:当按钮被按下时所调用的一个函数或方法。所回调的可以是一个函数、方法或别的可调用的Python对象。
Button(root, text="查询", width=8,command=search).place(x=575, y=95)
Button(root, text="成绩排名", width=9, height=4,command=paiMing).place(x=715, y=440)
Button(root, text="生成报表", width=9 , height= 4,command=baobiao).place(x=800, y=440)
# Python tkinter之Treeview用法:https://www.cnblogs.com/rainbow-tan/p/14125768.html
# 设置列表中列的布局,root是父容器,columns定义显示的列,show='headings'用于隐藏首列
tree = tk.Treeview(root, height= 13,columns=['1', '2', '3', '4', '5', '6', '7'], show='headings')
tree.place(x=360, y=140) # 定义位置
# anchor='w' 定义对齐方式,可选n, ne, e, se, s, sw, w, nw, center
tree.column('1', width=85, anchor='center')
tree.column('2', width=85, anchor='center')
tree.column('3', width=85, anchor='center')
tree.column('4', width=85, anchor='center')
tree.column('5', width=85, anchor='center')
tree.column('6', width=85, anchor='center')
tree.column('7', width=85, anchor='center')
# 设置列表中列的标题
tree.heading('1', text='学号')
tree.heading('2', text='姓名')
tree.heading('3', text='性别')
tree.heading('4', text='数学')
tree.heading('5', text='英语')
tree.heading('6', text='语文')
tree.heading('7', text='总成绩')
for i in range(len(data)): # 数据输出 页面首次
tree.insert('', 'end', values=data[i])
def zengjia():
s = [a.get(), b.get(), c.get(), d.get(), e.get(), f.get()] # 写入数据源
for i in range(6): # 判断输入是否有空值
if (s[i] == ''):
tkinter.messagebox.showinfo('信息', '信息不完整')
break
else: # python break 无法跳出两层,需用for()....else...来解决
con.zeng(s) # 将数据源交于控制器方法传入数据库
search() # 再次查询
a = IntVar()
b = StringVar()
c = StringVar()
d = IntVar()
e = IntVar()
f = IntVar()
Label(root, text="添加数据").place(x=40, y=100)
Label(root, text="学号").place(x=40, y=130)
Entry(root, width=20, textvariable=a).place(x=75, y=130)
Label(root, text="姓名").place(x=40, y=160)
Entry(root, width=20, textvariable=b).place(x=75, y=160)
Label(root, text="性别").place(x=40, y=190)
Entry(root, width=20, textvariable=c).place(x=75, y=190)
Label(root, text="数学").place(x=40, y=220)
Entry(root, width=20, textvariable=d).place(x=75, y=220)
Label(root, text="英语").place(x=40, y=250)
Entry(root, width=20, textvariable=e).place(x=75, y=250)
Label(root, text="语文").place(x=40, y=280)
Entry(root, width=20, textvariable=f).place(x=75, y=280)
Button(root, text="添加", width=9, height=4,bg = "white",command=zengjia).place(x=250, y=170)
# 选中的数据显示至输入框
form_record = []
def show_input():
global form_record
print(form_record)
g.set(form_record[0])
h.set(form_record[1])
i.set(form_record[2])
j.set(form_record[3])
k.set(form_record[4])
l.set(form_record[5])
def selectTree(event): # 表格点击响应函数
global form_record
form_record = tree.item(tree.selection(), "values") # 获得点击的数据
# 显示至输入框
show_input()
# Tkinter 之TreeView表格与树状标签:https://www.cnblogs.com/yang-2018/p/11824250.html
# 在bind方法里的'<>'添上相应的事件代码,后面跟定义的函数
#<< TreeviewSelect>>,代表选择变化是发生
tree.bind('<<TreeviewSelect>>', selectTree) # 绑定点击事件
def gengxin(): # 更新数据
s = [g.get(), h.get(), i.get(), j.get(), k.get(), l.get()] # 写入数据源
con.geng(s) # 调用更新方法
search() # 重新加载数据
def shanchu(): # 删除数据
n = g.get()
if (n == ''):
tkinter.messagebox.showinfo('提示', '请选中学生')
else:
con.shan(n) # 调用删除方法
search() # 重新加载数据
g = StringVar()
h = StringVar()
i = StringVar()
j = StringVar()
k = StringVar()
l = StringVar()
Label(root, text="更改数据").place(x=40, y=320)
Label(root, text="学号").place(x=40, y=350)
Entry(root, width=20, textvariable=g).place(x=75, y=350)
Label(root, text="姓名").place(x=40, y=380)
Entry(root, width=20, textvariable=h).place(x=75, y=380)
Label(root, text="性别").place(x=40, y=410)
Entry(root, width=20, textvariable=i).place(x=75, y=410)
Label(root, text="数学").place(x=40, y=440)
Entry(root, width=20, textvariable=j).place(x=75, y=440)
Label(root, text="英语").place(x=40, y=470)
Entry(root, width=20, textvariable=k).place(x=75, y=470)
Label(root, text="语文").place(x=40, y=500)
Entry(root, width=20, textvariable=l).place(x=75, y=500)
Button(root, text="更新", width=9,height = 4 ,command=gengxin).place(x=250, y=380)
Button(root, text="删除信息", width=9,height=4, command=shanchu).place(x=885, y=440)
# mainloop()方法允许程序循环执行,并进入等待和处理事件。
root.mainloop()
数据库设计(Navicat)
/*
Navicat Premium Data Transfer
Source Server : local
Source Server Type : MySQL
Source Server Version : 80027
Source Host : localhost:3306
Source Schema : mybatis
Target Server Type : MySQL
Target Server Version : 80027
File Encoding : 65001
Date: 21/12/2021 01:39:01
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for student
-- ----------------------------
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`sex` char(10) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`math` int NULL DEFAULT NULL,
`english` int NULL DEFAULT NULL,
`chinese` int NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 12346 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of student
-- ----------------------------
INSERT INTO `student` VALUES (1233, '每', '男', 88, 66, 99);
INSERT INTO `student` VALUES (1234, '天', '女', 56, 45, 90);
INSERT INTO `student` VALUES (1235, '起', '男', 70, 60, 80);
INSERT INTO `student` VALUES (1256, '床', '男', 78, 90, 67);
INSERT INTO `student` VALUES (1266, '都', '男', 88, 77, 44);
INSERT INTO `student` VALUES (1789, '想', '女', 54, 54, 56);
INSERT INTO `student` VALUES (1790, '摆', '女', 78, 67, 89);
INSERT INTO `student` VALUES (1791, '作者', '男', 67, 56, 78);
INSERT INTO `student` VALUES (1792, '简书', '男', 78, 67, 78);
SET FOREIGN_KEY_CHECKS = 1;
实现(设计作者隐私,部分数据已涂抹)
系统预览图与背景图原图
附结课答辩知识点
1.def add():
判断要添加的5个字段是否为空【add_check】。
通过Stringvar从组件entry中取出用户输入的值。
判断要添加的学号是否【exists】,若存在弹出messagebox提示框。
否则,操作数据库文件,insert into5个字段。
插入数据后,将页面【show_all】
考虑非法输入时的错误捕获
2.def update():
通过Stringvar从页面组件entry中取到5个字段的值,并以元组的方式存储。
操作数据库文件,得到当前学号学生的信息,fetchone方法返回一条记录(元组)。
若有值,通过Stringvar将用户在组件entry输入的5个字段值依次set到对应column中。
3.def delete():
messagebox.askokcancel弹出询问是否删除对话框。
通过Stringvar从页面组件entry中get学生学号。
若有值,操作数据库文件,将此学号学生delete。
messagebox.showinfo弹出删除成功对话框。
删除数据后,【show_all】。
并将刚才用户选中的读入entry值都清空(w.set(''))。
4.def search():
通过stringvar从组件entry中get用户输入的学号值。
若为空值,则【show_all】
否则,操作数据库文件获取当前学号对应的item值(元组)。
若item有值,则list(item),并【clear_tree_view】。
在item中insert(0,1)。
对item中的语数外三项成绩求和,并append到item中。
通过tree.insert(模块ttk中的函数,可查看用法),将查询到的一条item插入到treeview组件中(可直接显示在GUI页面中)。
tree.selection_set(),将选中的数据显示出来。
若无值,messagebox.showinfo(),弹窗提示无此学号。
5.def clear_tree_view():
w.get_children()得到所有子节点的数据。
循环遍历删除。
6.def show_all():
先【clear_tree_view】。
操作数据库文件,获取表中所有数据到list。
循环遍历,通过enumerate(list,start=1)处理数据(需要将总成绩求和后append到list,将序号插入后直接将数据insert到treeview中)。
- def select():
通过stringvar将组件entry中的值都set到修改学生信息labelframe中的5个entry中。
8.def order():
操作数据库文件,获取表中所有学生信息,return fetchall(多个元组)。
将返回的元组循环遍历插入到列表中,并append总成绩值。
list.sort()对列表根据总成绩排序。
【clear_tree_view】
循环编列得到的新list,inser到treeview中。
9.def export():
askdirectory()询问要导出的路径。
若有path值:打开或创建文件,并通过csv.writer将数据库表中所有数据都写入到csv文件中。
messagebox.showinfo()弹出框提示导出成功。