学生管理系统
import os
import pickle # pickle库 用于保存和加载文件中的数据
import pandas as pd
import operator # 比较两个字典是否相等
import random
import re
import webbrowser # 访问网址
'''
待扩展内容1 实现子窗口关闭时,父窗口无法使用
待扩展内容2 可增加学科的功能
# 数据结构 {stu_id: {'name': '张三', 'subjects':{} } }
# subjects: {math: 100, chinese: 100, english: 99......}
'''
import tkinter as tk # tkinter库 用于显示操作界面(GUI)
from tkinter import messagebox
from tkinter import simpledialog
from tkinter import filedialog
from tkinter import ttk
class Student():
firstName = ["张", "李", "王", "陈", "叶", "鲁", "詹", "曹", "苏", "朱", "孔", "宋", "谢", "沈", "木", "唐", "留", "曾", "秋", "夏", "春", "冬", "林", "威", "右", "左"]
lastName = ["兆鹏", "思雨", "一飞", "淼", "峰", "成功", "天佑", "轼", "子强", "凌云", "慈欣", "蝉", "宗元", "适", "伯伦", "匡胤", "布斯", "破伦", "逸飞", "善成", "保成", "殷武", "嘉佑"]
def process(self, num):
num_str = str(num)
while len(num_str) < 5:
num_str = "0" + num_str
return num_str
def randomName(self):
return random.choice(Student.firstName) + random.choice(Student.lastName)
def __init__(self):
self.stu_no = str(random.randint(2020, 2024)) + 'b' + self.process(random.randint(1, 100000))
self.name = self.randomName()
self.python = random.randint(50, 100)
self.english = random.randint(40, 90)
self.math = random.randint(30, 100)
class ValidInput:
START_ROW = 0
IS_VALID = True # 需要在外部重置
BACK_DATA = []
def __init__(self, root, label_text, pattern, box_num, callback, former_data = ""):
self.root = root
self.box_num = box_num
self.pattern = pattern
self.callback = callback
self.cur_row = ValidInput.START_ROW
self.label = ttk.Label(root, text = label_text)
self.label.grid(row = ValidInput.START_ROW, column = 0, padx = 20, pady = 20)
self.input_entry = ttk.Entry(root)
self.input_entry.grid(row = ValidInput.START_ROW, column = 1, padx = 20, pady = 20)
self.input_entry.insert('0', former_data)
self.error_label = tk.Label(root, text="", fg="red", width = 20)
self.error_label.grid(row = ValidInput.START_ROW + 1, column = 1)
ValidInput.START_ROW += 2
def validate_input(self):
input_text = self.input_entry.get()
if re.match(self.pattern, input_text):
#print("accord")
self.error_label.config(text="", fg="red")
ValidInput.BACK_DATA.append(input_text)
if self.cur_row + 2 == self.box_num * 2 and ValidInput.IS_VALID:
self.callback(ValidInput.BACK_DATA)
else:
self.error_label.config(text="请输入合法的数据", fg="red")
ValidInput.IS_VALID = False
class CreateOrUpdateStu():
# 回调函数
def process(self, item):
# 执行回调函数,将参数传入上级
if self.status == 'create':
self.callback(item)
elif self.status == 'edit':
self.callback(item, self.former_data[0])
self.root.destroy()
def confirm(self, stu_no, name, python, english, math):
stu_no.validate_input()
name.validate_input()
python.validate_input()
english.validate_input()
math.validate_input()
ValidInput.IS_VALID = True
ValidInput.BACK_DATA = []
def on_closing(self):
ValidInput.START_ROW = 0 # todo 需要做赋值的地方
def __init__(self, callback, former_data = []):
# 编辑时以前的数据
self.former_data = former_data
if former_data == []:
self.status = "create"
else:
self.status = "edit"
# 回调函数
self.callback = callback
# 创建主窗口
root = tk.Tk()
root.protocol("WM_DELETE_WINDOW", self.on_closing())
self.root = root
# 设置窗口标题
title_text = "新增" if self.status == "create" else "编辑"
root.title(title_text)
# 设置窗口大小
#root.geometry("300x200")
'''
正则匹配(注:r""语法可以创建原始字符串(raw string),原始字符串中的反斜杠不会被转义):
0-100之间的整数: ^(0|[1-9]\d?|100)$
整数: ^-?\d+$
0-100之间的数字,可以是小数或者整数: ^(0.\d{1,2}|[1-9]\d?(.\d{1,2})?|100|100.0|100.00)$
不为空的字符串: ^.+$
小数: ^-?\d+.\d+$
2-4个汉字: ^[\u4e00-\u9fa5]{2,4}$
学号20XXbXXXXX: "^20[0-9]{2}b(?!0{5})\d{5}$"
'''
# 创建标签和输入框
# 匹配学号20XXbXXXXX
validator_stu_no = ValidInput(root, "学号(20XXbXXXXX):", "^20[0-9]{2}b(?!0{5})\d{5}$", 5, self.process, self.former_data[1] if self.former_data else "")
# 匹配2-4个汉字
validator_name = ValidInput(root, "姓名(2-4个汉字):", "^[\u4e00-\u9fa5]{2,4}$", 5, self.process, self.former_data[2] if self.former_data else "")
# 匹配0-100之间的整数
validator_python = ValidInput(root, "python成绩(0-100):", "^(0|[1-9]\d?|100)$", 5, self.process, self.former_data[3] if self.former_data else "")
# 匹配0-100之间的整数
validator_english = ValidInput(root, "英语成绩(0-100):", "^(0|[1-9]\d?|100)$", 5, self.process, self.former_data[4] if self.former_data else "")
# 匹配0-100之间的整数
validator_math = ValidInput(root, "数学成绩(0-100):", "^(0|[1-9]\d?|100)$", 5, self.process, self.former_data[5] if self.former_data else "")
confirm_button = ttk.Button(root, text = "确定", command = lambda: self.confirm(validator_stu_no, validator_name, validator_python, validator_english, validator_math))
confirm_button.grid(row = 5 * 2, column = 0, padx = 20, pady = 20)
cancel_button = ttk.Button(root, text = "取消", command = root.destroy)
cancel_button.grid(row = 5 * 2, column = 1, padx = 20, pady = 20)
# 进入消息循环
root.mainloop()
class EditProperty:
def __init__(self, properties, properties_name, callback):
self.root = tk.Tk()
self.callback = callback
self.properties = properties
self.properties_name = properties_name
self.root.title("编辑字段")
self.overwrite_var = tk.IntVar(master = self.root)
self.label = ttk.Label(self.root, text="全部字段:")
self.label.grid(row = 0, column = 0, padx = 20, pady = 20)
# 表格布局
columns = ['property', 'property_name']
table = ttk.Treeview(
master = self.root, # 父容器
height = 9, # 表格显示的行数,height行
columns = columns, # 显示的列
show='headings', # 隐藏首列
)
self.table = table
# 定义表头
table.heading('property', text='字段', anchor='w')
table.heading('property_name', text='字段名称', anchor='w')
# 定义列
table.column('property', width=140, anchor='w')
table.column('property_name', width=140, anchor='w')
# 定位
table.grid(row = 1, column = 0, columnspan = 4)
for i in range(len(properties)):
table.insert("", "end", values = (properties[i], properties_name[i]))
# 编辑 向上 向下 删除
self.confirm_button = ttk.Button(self.root, text = "新增", command = self.handleCreate)
self.confirm_button.grid(row = 2, column = 0, padx = 20, pady = 20)
self.cancel_button = ttk.Button(self.root, text = "删除", command = self.handleDelete)
self.cancel_button.grid(row = 2, column = 1, padx = 20, p