在日常生活和工作中,我们经常需要将大量数据保存到Excel电子表格中以进行分析和管理。在Python中,可以使用openpyxl库来操作Excel表格。通过使用Python,我们可以利用其简洁和功能强大的特性,结合Tkinter提供的GUI开发能力,快速构建一个学生信息管理系统GUI界面。
在本节教程中,我们将介绍如何使用Tkinter库来创建一个基于GUI界面的学生信息管理系统,简单实现添加学生信息到Excel表格中(包括:学号、姓名、性别、年龄、专业、班级、电话等)。系统支持从Excel表格中查询学生信息(支持按学号、姓名等任意条件查询),同时支持增加、删除和修改Excel表格中的学生信息,并将编辑后的数据内容保存到Excel表格中。运行该程序时,默认会在D盘创建一个StudentData.xlsx文件,用于存储学生信息。注意,运行该程序前,如果StudentData.xlsx文件已被双击打开,必须先关闭该Excel文件,否则会报错并自动退出程序。
学生信息管理系统具体流程图如下:
创建一个基于图形用户界面(GUI)的学生信息管理系统是一个涉及多个步骤的过程。下面简单介绍设计过程,使用了tkinter库来构建GUI和openpyxl库来处理数据存储。
第一步、安装openpyxl库
首先,要使用Python操作Excel表格,可以使用openpyxl库。openpyxl库是一个用于处理Excel文件的第三方库。它专门处理xlsx格式的Excel文件,支持读写Excel文件、操作单元格、创建和修改工作表、设置样式、添加图表和公式等功能。
openpyxl不是Python的标准库,在使用openpyxl模块之前,首先需要将其安装到Python环境中。使用Python的包管理工具pip来安装openpyxl库是最简单的方法。打开Pyhint编辑器,点击Pyhint编辑器上面的“打开终端”按钮,在弹出的黑色cmd终端窗口中,输入以下命令安装openpyxl库:
pip install openpyxl
执行以上安装命令,如果运行结果出现“Successfully installed”,说明已经安装成功;如果运行结果出现“Requirement already satisfied”,说明该模块已经被安装过,不用再安装。不懂得安装第三方库,可以参考本教程第10章中第9节和第10节里面的内容。
第二步、使用openpyxl库操作Excel表格
首先,为了使学生信息的数据易于保存和读取,我们需要使用openpyxl库操作Excel表格来存储和读取数据。目前,Excel表格文件普遍被用于存储大量的数据,如名单、账目、库存列表等,同时我们也可以直接双击打开Excel表格手动输入数据。
这里,我们先指定Excel文件保存的路径为D盘的“StudentData.xlsx”。使用os.path.exists()方法检查文件是否存在,如果不存在则创建该Excel文件。当我们在使用openpyxl库操作Excel文件时,如果文件没有被关闭(已被人为双击打开),就会出现PermissionError: [Errno 13] Permission denied的错误。为了解决这个问题,这里我们使用try-except语句来捕获异常,并在发生异常时退出程序。代码如下:
动手练一练:
# 指定Excel文件路径
file_path = r"D:/StudentData.xlsx"
# 检查文件是否存在
if os.path.exists(file_path):
pass
else:
# 文件不存在,则创建一个新的Workbook对象
workbook = openpyxl.Workbook()
# 最后保存Workbook到指定路径
# 将在D盘创建一个StudentData.xlsx文件
workbook.save(file_path)
# 利用try语句打开Excel文件,捕获并处理可能出现的异常
try:
# 获取指定的文件
wb = openpyxl.load_workbook(file_path)
# 选择默认的工作表
ws = wb.active
# 在Excel文件的第一行写入标题信息
# 写入内容到对应单元格 A1
ws['A1'] = "学生学号"
# 写入内容到对应单元格 B1
ws['B1'] = "学生姓名"
# 写入内容到对应单元格 C1
ws['C1'] = "学生性别"
# 写入内容到对应单元格 D1
ws['D1'] = "学生年龄"
# 写入内容到对应单元格 E1
ws['E1'] = "学生专业"
# 写入内容到对应单元格 F1
ws['F1'] = "班级"
# 写入内容到对应单元格 G1
ws['G1'] = "电话号码"
# 保存文件到"D:/StudentData.xlsx"
wb.save(file_path)
except PermissionError:
# 如果Excel文件已经被打开,则捕获异常并弹出提示信息
messagebox.showinfo("提示", "StudentData.xlsx文件已经被打开,请先关闭...")
# 关闭主进程
sys.exit()
上面的代码中,在存储和读取学生信息前,我们首先在Excel文件的默认工作表“Sheet”的第一行中写入标题信息,包括:学号、姓名、性别、年龄、专业、班级、电话,以便通过结构化的方式来组织数据,提高数据的可读性。
第三步、定义写入和读取Excel文件的方法
在写入和读取Excel文件前,我们先定义一个全局变量“Info= []”,用来存储学生信息,默认为空列表。函数ReadTxt()从Excel文件里面的第二行开始读取数据(注意:第一行为固定的标题信息),再将读取后的信息以“键值对”的字典形式返回一个混合列表,最后通过try语句将返回的列表信息存入全局变量Info中。代码如下:
动手练一练:
# 定义一个全局变量,用来存储学生信息,默认为空列表
Info = []
# 定义一个方法用于写入文件:传入已经变更好的信息列表,然后遍历写入Excel文件
# 点击"写入"按钮被触发的函数
def WriteTxt_w_Mode(Student_List):
try:
# 将第一行设置为当前行
ws._current_row = 1
# 获取第一行的最大列号
max_column = ws.max_column
# 遍历第一行之后的所有行并删除单元格内容
# min_row=2表示从第二行开始迭代
for row in ws.iter_rows(min_row=2):
for cell in row[:max_column]:
cell.value = None
# 使用列表推导式提取所有字典的值
multi_list = [list(d.values()) for d in Student_List]
# 将多元列表中的数据追加到Excel工作表中
for row in multi_list:
# 使用append方法,将行数据按行追加写入
ws.append(row)
# 保存文件
wb.save(file_path)
except PermissionError:
# 如果文件已经被打开,则捕获异常并弹出提示信息
messagebox.showinfo("提示", "StudentData.xlsx文件已经被打开,请先关闭...")
# 关闭主进程
sys.exit()
# 定义一个方法用于读取Excel文件,“-> list”是注释,表示函数的返回值类型为“列表”
def ReadTxt() -> list:
try:
# 创建两个空的临时列表
Temp_List1 = []
Temp_List2 = []
# 加载Excel文件
workbook_read = openpyxl.load_workbook(filename=file_path)
# 获取默认的工作表
sheet = workbook_read.active
# 从第二行开始遍历行,注意第一行为标题信息
for row in sheet.iter_rows(min_row=2, values_only=True):
# 将每一行的内容转换为字符串格式
row_strings = [str(cell) for cell in row]
# 将每一行的内容追加到临时列表“Temp_List1”中
Temp_List1.append(list(row_strings))
# 将读取的信息以“键值对”的字典形式并入临时列表“Temp_List2”
while len(Temp_List2) < len(Temp_List1):
for j in range(0,len(Temp_List1)):
ID = Temp_List1[j][0]
Name = Temp_List1[j][1]
Sex = Temp_List1[j][2]
Age = Temp_List1[j][3]
Major = Temp_List1[j][4]
Class = Temp_List1[j][5]
Telephone = Temp_List1[j][6]
Info_dict = {"ID":ID,
"Name":Name,
"Sex":Sex,
"Age":Age,
"Major":Major,
"Class":Class,
"Telephone":Telephone
}
Temp_List2.append(Info_dict)
# 关闭工作簿
workbook_read.close()
# 将含有学生信息的临时列表返回
return Temp_List2
except PermissionError:
# 如果文件已经被打开,则捕获异常并弹出提示信息
messagebox.showinfo("提示", "StudentData.xlsx文件已经被打开,请先关闭...")
# 关闭主进程
sys.exit()
# 用于将ReadTxt()方法返回的学生信息存入全局变量Info中
try:
for i in ReadTxt():
Info.append(i)
except:
pass
第四步、设计学生信息管理系统GUI界面
学生信息管理系统主要包括以下几个GUI窗口界面:
添加学生信息窗口界面:通过输入学生的个人信息,即学号、姓名、性别、年龄、专业、班级、电话,来实现添加学生的操作。具体实现的过程是用户通过输入框输入学号、姓名、性别、年龄、专业、班级、电话,然后使用get()方法来获取各个输入框的内容,判断学号是否重复,并且限制学号为6位数,限制年龄范围在0~25内,限制电话号码为11位数字,限制班级为大于0的任何数字,如果所有的验证成功通过,则把信息写入Excel文件内。
删除学生信息窗口界面:通过输入学生的学号来实现删除学生信息的操作。具体实现的过程是用户通过输入框输入学号,然后通过get()方法来获取输入框的学号;再通过遍历全局变量Info来判断学号是否存在,如果存在则把用户输入学号的匹配数据所在行删除,最后将全局变量Info信息再写入Excel文件中。
修改学生信息窗口界面:通过输入学生的学号来实现对学生信息的修改。具体实现的过程是用户通过输入框输入学号,然后通过get()方法来获取输入框的学号;再通过遍历全局变量Info检查学号是否存在;如果存在则进入修改页面,输入信息到输入框,通过get()方法获取修改信息,最后将全局变量Info信息再写入Excel文件中。
查询学生信息窗口界面:通过输入学生的信息条件来实现对学生信息的查询。具体实现的过程是用户通过输入框输入学号、姓名、性别、年龄、专业、班级、电话其中的任何一个信息(可不全填),然后通过get()方法来获取输入框的信息;再通过遍历全局变量Info查询所有符合条件的学生信息,最后在主界面打印出符合条件的学生信息。
显示所有学生信息主界面:具体实现的过程是通过遍历全局变量Info的全部内容,通过获取result变量统计输出汇总学生人数,最后通过tkinter的Text文本控件把所有学生信息全部有序地在主界面显示出来。
退出窗口界面:点击“确认”退出程序,“确认”按钮控件绑定函数destroy()用于关闭主窗口,并释放所占用的资源。
部分窗口界面图片如下:
以下是学生信息管理系统的总代码:
动手练一练:
import os
import sys
# 使用tkinter模块实现GUI界面
import tkinter as tk
from tkinter import messagebox
# 导入库,openpyxl用于处理Excel文件
import openpyxl
from tkinter import ttk
# 定义一个全局变量,用来存储学生信息,默认为空列表
Info = []
# 指定Excel文件路径
file_path = r"D:/StudentData.xlsx"
# 检查文件是否存在
if os.path.exists(file_path):
pass
else:
# 文件不存在,则创建一个新的Workbook对象
workbook = openpyxl.Workbook()
# 最后保存Workbook到指定路径
# 将在D盘创建一个StudentData.xlsx文件
workbook.save(file_path)
# 利用try语句打开Excel文件,捕获并处理可能出现的异常
try:
# 获取指定的文件
wb = openpyxl.load_workbook(file_path)
# 选择默认的工作表
ws = wb.active
# 在Excel文件的第一行写入标题信息
# 写入内容到对应单元格 A1
ws['A1'] = "学生学号"
# 写入内容到对应单元格 B1
ws['B1'] = "学生姓名"
# 写入内容到对应单元格 C1
ws['C1'] = "学生性别"
# 写入内容到对应单元格 D1
ws['D1'] = "学生年龄"
# 写入内容到对应单元格 E1
ws['E1'] = "学生专业"
# 写入内容到对应单元格 F1
ws['F1'] = "班级"
# 写入内容到对应单元格 G1
ws['G1'] = "电话号码"
# 保存文件到"D:/StudentData.xlsx"
wb.save(file_path)
except PermissionError:
# 如果Excel文件已经被打开,则捕获异常并弹出提示信息
messagebox.showinfo("提示", "StudentData.xlsx文件已经被打开,请先关闭...")
# 关闭主进程
sys.exit()
# 定义一个方法用于写入文件:传入已经变更好的信息列表,然后遍历写入Excel文件
# 点击"写入"按钮被触发的函数
def WriteTxt_w_Mode(Student_List):
try:
# 将第一行设置为当前行
ws._current_row = 1
# 获取第一行的最大列号
max_column = ws.max_column
# 遍历第一行之后的所有行并删除单元格内容
# min_row=2表示从第二行开始迭代
for row in ws.iter_rows(min_row=2):
for cell in row[:max_column]:
cell.value = None
# 使用列表推导式提取所有字典的值
multi_list = [list(d.values()) for d in Student_List]
# 将多元列表中的数据追加到Excel工作表中
for row in multi_list:
# 使用append方法,将行数据按行追加写入
ws.append(row)
# 保存文件
wb.save(file_path)
except PermissionError:
# 如果文件已经被打开,则捕获异常并弹出提示信息
messagebox.showinfo("提示", "StudentData.xlsx文件已经被打开,请先关闭...")
# 关闭主进程
sys.exit()
# 定义一个方法用于读取Excel文件,“-> list”是注释,表示函数的返回值类型为“列表”
def ReadTxt() -> list:
try:
# 创建两个空的临时列表
Temp_List1 = []
Temp_List2 = []
# 加载Excel文件
workbook_read = openpyxl.load_workbook(filename=file_path)
# 获取默认的工作表
sheet = workbook_read.active
# 从第二行开始遍历行,注意第一行为标题信息
for row in sheet.iter_rows(min_row=2, values_only=True):
# 将每一行的内容转换为字符串格式
row_strings = [str(cell) for cell in row]
# 将每一行的内容追加到临时列表“Temp_List1”中
Temp_List1.append(list(row_strings))
# 将读取的信息以“键值对”的字典形式并入临时列表“Temp_List2”
while len(Temp_List2) < len(Temp_List1):
for j in range(0,len(Temp_List1)):
ID = Temp_List1[j][0]
Name = Temp_List1[j][1]
Sex = Temp_List1[j][2]
Age = Temp_List1[j][3]
Major = Temp_List1[j][4]
Class = Temp_List1[j][5]
Telephone = Temp_List1[j][6]
Info_dict = {"ID":ID,
"Name":Name,
"Sex":Sex,
"Age":Age,
"Major":Major,
"Class":Class,
"Telephone":Telephone
}
Temp_List2.append(Info_dict)
# 关闭工作簿
workbook_read.close()
# 将含有学生信息的临时列表返回
return Temp_List2
except PermissionError:
# 如果文件已经被打开,则捕获异常并弹出提示信息
messagebox.showinfo("提示", "StudentData.xlsx文件已经被打开,请先关闭...")
# 关闭主进程
sys.exit()
# 用于将ReadTxt()方法返回的学生信息存入全局变量Info中
try:
for i in ReadTxt():
Info.append(i)
except:
pass
# 定于一个方法,用于检查学号是否规范,返回6位数字
def is_ID(ID):
return len(ID) == 6 and ID.isdigit()
# 定于一个方法,用于检查年龄是否规范,返回0~25的数字
def is_Age(Age):
return Age.isdigit() and 0 <= int(Age) and int(Age) <= 25
# 定于一个方法,用于检查班级是否规范,返回任何大于0的数字
def is_Class(Class):
return Class.isdigit() and int(Class) > 0
# 定于一个方法,用于检查电话是否规范,返回11位数字的电话号码
def is_Telephone(Telephone):
return len(Telephone) == 11 and Telephone.isdigit()
# 定义一个方法,用于判断是否为中文字符
def is_Chinese(ch):
if ch >= '\u4e00' and ch <='\u9fa5':
return True
else:
return False
# 定义一个方法,用于计算中西文混合字符串的字符串长度
def Len_Str(string):
count = 0
for line in string:
if is_Chinese(line):
count = count + 2
else:
count = count + 1
return count
# 提示信息
def Tip_Add():
messagebox.showinfo("提示信息","添加成功")
def Tip_Search():
messagebox.showinfo("提示信息","查询成功")
def Tip_Del():
messagebox.showinfo("提示信息","删除成功")
def Tip_Mod():
messagebox.showinfo("提示信息","修改成功")
def Tip_Add_ID_Repeat():
messagebox.showinfo("提示信息","此学号已经存在,请勿重复添加!")
def Tip_Del_ID_None():
messagebox.showinfo("提示信息","此学号不存在,请重新输入!")
def Tip_Search_None():
messagebox.showinfo("提示信息","未查询到有关学生信息!")
def Tip_Add_ID():
messagebox.showinfo("提示信息","学号格式有误,请重新输入!")
def Tip_Add_Age():
messagebox.showinfo("提示信息","年龄格式有误,请重新输入!")
def Tip_Add_Class():
messagebox.showinfo("提示信息","班级格式有误,请重新输入!")
def Tip_Add_Telephone():
messagebox.showinfo("提示信息","电话格式有误,请重新输入!")
# 1.添加学生信息的主方法
def Add_Student_Info(ID, Name, Sex, Age, Major, Class, Telephone):
# 导入信息
global Info
# 检查输入信息是否规范
if not is_ID(ID):
Tip_Add_ID()
return
# 判断学号是否重复
for i in Info:
if ID == i['ID']:
Tip_Add_ID_Repeat()
return
if not is_Age(Age):
Tip_Add_Age()
return
if not is_Class(Class):
Tip_Add_Class()
return
if not is_Telephone(Telephone):
Tip_Add_Telephone()
return
# 用字典整合学生信息
Info_dict = {'ID':ID, 'Name':Name, 'Sex':Sex, 'Age':Age, 'Major':Major, 'Class':Class, 'Telephone':Telephone}
# 将字典存入总列表
Info.append(Info_dict)
# 添加成功
Tip_Add()
# 将信息写入文件
WriteTxt_w_Mode(Info)
# 2.删除学生信息的主方法
def Del_Student_Info(ID):
# 检查输入信息是否规范
if not is_ID(ID):
Tip_Add_ID()
return
# 用于指示是否删除的状态指标
Flag = True
# 导入信息
global Info
# 遍历,删除学生信息
for i in Info:
if ID == i["ID"]:
Info.remove(i)
Flag = False
break
if Flag:
Tip_Del_ID_None()
return
# 删除成功
Tip_Del()
# 将删除后的信息写入文件
WriteTxt_w_Mode(Info)
# 3.修改学生信息,ID符合条件则打开修改学生信息的窗口
def Mod_Student_Info(ID):
if not is_ID(ID):
Tip_Add_ID()
return
# 用于指示是否打开窗口的指标
Flag = True
global Info
for i in Info:
if ID == i["ID"]:
Window_Mod_Input(ID)
Flag = False
break
if Flag:
Tip_Del_ID_None()
return
# 3.修改学生信息的主方法
def Mod_Student_Info_1(ID, Name, Sex, Age, Major, Class, Telephone):
# 检查输入信息是否规范
if not is_ID(ID):
Tip_Add_ID()
return
if not is_Age(Age):
Tip_Add_Age()
return
if not is_Class(Class):
Tip_Add_Class()
return
if not is_Telephone(Telephone):
Tip_Add_Telephone()
return
# 导入信息
global Info
# 遍历,修改学生信息
for i in Info:
if i["ID"] == ID:
i["Name"] = Name
i["Sex"] = Sex
i["Age"] = Age
i["Major"] = Major
i["Class"] = Class
i["Telephone"] = Telephone
# 修改成功
Tip_Mod()
# 将修改后的信息写入文件
WriteTxt_w_Mode(Info)
# 4.查找学生信息的主方法
def Search_Student_Info(ID, Name, Major, Age, Class, Telephone):
# 检查输入是否规范,规范的和空字符串通过
if len(ID) != 0 and not is_ID(ID):
Tip_Add_ID()
return
if len(Age) != 0 and not is_Age(Age):
Tip_Add_Age()
return
if len(Class) != 0 and not is_Class(Class):
Tip_Add_Class()
return
if len(Telephone) != 0 and not is_Telephone(Telephone):
Tip_Add_Telephone()
return
# 导入信息
global Info
# 临时列表
List = []
# 用来指示是否查找到学生的信息指标
Flag = False
# 遍历,根据输入的部分信息找到符合条件的学生
for i in Info:
# and后面加“\”,代表行续符,用于告诉Python解释器,当前行代码未完,下一行也是这个代码的一部分
if (i["ID"]==ID or len(ID)==0)and \
(i["Name"]==Name or len(Name)==0)and \
(i["Major"]==Major or len(Major)==0)and \
(i["Age"]==Age or len(Age)==0)and \
(i["Class"]==Class or len(Class)==0)and \
(i["Telephone"]==Telephone or len(Telephone)==0)and \
(len(ID)!=0 or len(Name)!=0 or len(Major)!=0 or len(Age)!=0 or len(Class)!=0 or len(Telephone)!=0 ):
List.append(i)
if len(List) != 0:
Flag = True
# 在主界面打印符合条件学生信息
Print_Student_Info(List)
# 是否查找成功
if Flag:
Tip_Search()
else:
Tip_Search_None()
# 5.显示所有学生的主方法
def Print_Student_Info(Student_Info):
# 定义一个字符串用于存储想要输出显示的内容
str_out = ""
# 学生信息为空将返回
if Student_Info is None:
result.set("学生信息为空")
return
if len(Student_Info) == 0:
# 清空输出框,'1.0' 是开始位置,表示第一行的第0个字符
# tk.END是一个特殊的常量,表示Text控件中的最后一个字符的位置。
Show_result.delete(1.0, tk.END)
# 在主界面显示学生信息
result.set("无学生信息")
Show_result.insert('end', result.get())
return
# 学生信息不为空
if len(Student_Info) != 0:
# 显示信息标题
str_out += ("{:^5}".format("学生学号") +
"{:^7}".format("学生姓名") +
"{:^7}".format("学生性别") +
"{:^7}".format("学生年龄") +
"{:^7}".format("学生专业") +
"{:^5}".format("班级") +
"{:^9}".format("电话号码") +
"\n")
for i in range(0, len(Student_Info)):
# 格式化字符串
str_out += ("{:^}".format(Student_Info[i].get("ID")) + ' '*(11-Len_Str(Student_Info[i].get("ID"))) +
"{:^}".format(Student_Info[i].get("Name")) +' '*(11-Len_Str(Student_Info[i].get("Name")))+
"{:^}".format(Student_Info[i].get("Sex")) +' '*(11-Len_Str(Student_Info[i].get("Sex")))+
"{:^}".format(Student_Info[i].get("Age")) +' '*(10-Len_Str(Student_Info[i].get("Age")))+
"{:^}".format(Student_Info[i].get("Major")) +' '*(13-Len_Str(Student_Info[i].get("Major")))+
"{:^}".format(Student_Info[i].get("Class")) +' '*(5-Len_Str(Student_Info[i].get("Class")))+
"{:^}".format(Student_Info[i].get("Telephone")) +' '*(11-Len_Str(Student_Info[i].get("Telephone")))+
"\n")
# 清空输出框,'1.0' 是开始位置,表示第一行的第0个字符
# tk.END是一个特殊的常量,表示Text控件中的最后一个字符的位置。
Show_result.delete(1.0, tk.END)
# 在主界面显示学生信息
result.set(str_out)
Show_result.insert('end', result.get())
# 清空“汇总”统计输出框
total_text.delete(1.0, tk.END)
# 在主界面显示学生信息的数量
lists_str = result.get()
lists = [lst for lst in lists_str.split('\n') if lst]
total_text.insert(tk.END, f'{len(lists)-1}')
# 添加学生信息的窗口
def Window_Add():
# 实例化对象,创建app的子窗口window
window = tk.Toplevel(app)
# 窗口名字
window.title("添加学生信息")
# 设置窗口大小:宽x高(500x500),并指定窗口在屏幕上的位置为(500, 200)
window.geometry('500x500+500+200')
# 关于学号的 label 和 entry
Txt_ID = tk.StringVar()
Txt_ID.set("")
Label_Line1 = tk.Label(window, text = "学 号 (6 位):", font = ('黑体', 12), width = 15)
Label_Line1.place(x = 75, y = 50, anchor = 'nw')
Entry_Line1 = tk.Entry(window, show = None, font = ('黑体', 15), textvariable = Txt_ID, width = 20)
Entry_Line1.place(x = 200, y = 50, anchor = 'nw')
# 关于姓名的 label 和 entry
Txt_Name = tk.StringVar()
Txt_Name.set("")
Label_Line2 = tk.Label(window, text = "姓 名:", font = ('黑体', 12), width = 15)
Label_Line2.place(x = 75, y = 100, anchor = 'nw')
Entry_Line2 = tk.Entry(window, show = None, font = ('黑体', 15), textvariable = Txt_Name, width = 20)
Entry_Line2.place(x = 200, y = 100, anchor = 'nw')
# 关于性别的 label 和 entry
sex = tk.StringVar()
sex.set("男")
Label_Line7 = tk.Label(window, text = "性 别:", font = ('黑体', 12), width = 15)
Label_Line7.place(x = 75, y = 150, anchor = 'nw')
sex_input1 = tk.Radiobutton(window, text="男", variable=sex, value="男")
sex_input2 = tk.Radiobutton(window, text="女", variable=sex, value="女")
sex_input1.place(x = 200, y = 150, anchor = 'nw')
sex_input2.place(x = 260, y = 150, anchor = 'nw')
# 关于年龄的 label 和 entry
Txt_Age = tk.StringVar()
Txt_Age.set("")
Label_Line4 = tk.Label(window, text = "年 龄 (0~25):", font = ('黑体', 12), width = 15)
Label_Line4.place(x = 75, y = 200, anchor = 'nw')
Entry_Line4 = tk.Entry(window, show = None, font = ('黑体', 15), textvariable = Txt_Age, width = 20)
Entry_Line4.place(x = 200, y = 200, anchor = 'nw')
# 关于专业的 label 和 entry
Txt_Major = tk.StringVar()
Txt_Major.set("")
Label_Line3 = tk.Label(window, text = "专 业:", font = ('黑体', 12), width = 15)
Label_Line3.place(x = 75, y = 250, anchor = 'nw')
Entry_Line3 = tk.Entry(window, show = None, font = ('黑体', 15), textvariable = Txt_Major, width = 20)
Entry_Line3.place(x = 200, y = 250, anchor = 'nw')
# 关于班级的 label 和 entry
Txt_Class = tk.StringVar()
Txt_Class.set("")
Label_Line5 = tk.Label(window, text = "班 级 (数字):", font = ('黑体', 12), width = 15)
Label_Line5.place(x = 75, y = 300, anchor = 'nw')
Entry_Line5 = tk.Entry(window, show = None, font = ('黑体', 15), textvariable = Txt_Class, width = 20)
Entry_Line5.place(x = 200, y = 300, anchor = 'nw')
# 关于电话的 label 和 entry
Txt_Telephone = tk.StringVar()
Txt_Telephone.set("")
Label_Line6 = tk.Label(window, text = "电 话 (11位):", font = ('黑体', 12), width = 15)
Label_Line6.place(x = 75, y = 350, anchor = 'nw')
Entry_Line6 = tk.Entry(window, show = None, font = ('黑体', 15), textvariable = Txt_Telephone, width = 20)
Entry_Line6.place(x = 200, y = 350, anchor = 'nw')
# 关于"确认"组件,此处绑定函数Add_Student_Info用于添加学生信息
Button1_Yes = tk.Button(window, text = '确认', bg = 'silver', font = ('黑体', 12), command = lambda:Add_Student_Info(Txt_ID.get(), Txt_Name.get(), sex.get(), Txt_Age.get(), Txt_Major.get(), Txt_Class.get(), Txt_Telephone.get()), width = 10)
Button1_Yes.place(x = 75, y = 400, anchor = 'nw')
# 关于"取消"组件,此处绑定函数destroy()用于关闭窗口
Button2_No = tk.Button(window, text = '取消', bg = 'silver', font = ('黑体', 12), command = lambda:window.destroy(), width = 10)
Button2_No.place(x = 325, y = 400, anchor = 'nw')
# 窗口显示
window.mainloop()
# 删除学生信息的窗口
def Window_Del():
# 创建app的子窗口
window = tk.Toplevel(app)
window.title("删除学生信息")
# 设置窗口大小:宽x高(500x300),并指定窗口在屏幕上的位置为(500, 200)
window.geometry('500x300+500+200')
# 关于学号的 label 和 entry
Txt_ID = tk.StringVar()
Txt_ID.set("")
Label_Line1 = tk.Label(window, text = "学 号 (6 位):", font = ('黑体', 12), width = 15)
Label_Line1.place(x = 75, y = 100, anchor = 'nw')
Entry_Line1 = tk.Entry(window, show = None, font = ('黑体', 15), textvariable = Txt_ID, width = 20)
Entry_Line1.place(x = 200, y = 100, anchor = 'nw')
# 关于"确认"组件,此处绑定函数Del_Student_Info用于删除学生信息
Button1_Yes = tk.Button(window, text = '确认', bg = 'silver', font = ('黑体', 12), command = lambda:Del_Student_Info(Txt_ID.get()), width = 10)
Button1_Yes.place(x = 75, y = 200, anchor = 'nw')
# 关于"取消"组件,此处绑定函数destroy()用于关闭窗口
Button2_No = tk.Button(window, text = '取消', bg = 'silver', font = ('黑体', 12), command = lambda:window.destroy(), width = 10)
Button2_No.place(x = 325, y = 200, anchor = 'nw')
# tk.StringVar()用于接收用户输入
result = tk.StringVar()
result.set(">>>请输入待删除学生的学号<<<")
# 在界面中显示文本框,打印result的信息
Show_result = tk.Label(window, bg = "white", fg = "black", font = ("黑体", 12), bd = '0', anchor = 'center', textvariable = result)
Show_result.place(x = "50", y = "30", width = "400", height = "50")
# 显示窗口
window.mainloop()
# 修改学生信息的窗口
def Window_Mod():
# 创建app的子窗口
window = tk.Toplevel(app)
window.title("修改学生信息")
# 设置窗口大小:宽x高(500x300),并指定窗口在屏幕上的位置为(500, 200)
window.geometry('500x300+500+200')
# 关于学号的 label 和 entry
Txt_ID = tk.StringVar()
Txt_ID.set("")
Label_Line1 = tk.Label(window, text = "学 号 (6 位):", font = ('黑体', 12), width = 15)
Label_Line1.place(x = 75, y = 100, anchor = 'nw')
Entry_Line1 = tk.Entry(window, show = None, font = ('黑体', 15), textvariable = Txt_ID, width = 20)
Entry_Line1.place(x = 200, y = 100, anchor = 'nw')
# 关于"确认"组件,此处绑定函数Mod_Student_Info用于修改学生信息
Button1_Yes = tk.Button(window, text = '确认', bg = 'silver', font = ('黑体', 12), command = lambda:Mod_Student_Info(Txt_ID.get()), width = 10)
Button1_Yes.place(x = 75, y = 200, anchor = 'nw')
# 关于"取消"组件,此处绑定函数destroy()用于关闭窗口
Button2_No = tk.Button(window, text = '取消', bg = 'silver', font = ('黑体', 12), command = lambda:window.destroy(), width = 10)
Button2_No.place(x = 325, y = 200, anchor = 'nw')
# 在界面中显示文本框,打印result的信息
result = tk.StringVar()
result.set(">>>请输入待修改学生的学号<<<")
Show_result = tk.Label(window, bg = "white", fg = "black", font = ("黑体", 12), bd = '0', anchor = 'center', textvariable = result)
Show_result.place(x = "50", y = "30", width = "400", height = "50")
#显示窗口
window.mainloop()
# 输入修改学生信息的窗口
def Window_Mod_Input(ID):
# 创建app的子窗口
window = tk.Toplevel(app)
window.title("修改学生信息")
# 设置窗口大小:宽x高(500x500),并指定窗口在屏幕上的位置为(500, 200)
window.geometry('500x500+500+200')
for i in Info:
if i["ID"] == ID:
Name = i["Name"]
Sex = i["Sex"]
Age = i["Age"]
Major = i["Major"]
Class = i["Class"]
Telephone = i["Telephone"]
# 关于姓名的 label 和 entry
Txt_Name = tk.StringVar()
Txt_Name.set(Name)
Label_Line2 = tk.Label(window, text = "姓 名:", font = ('黑体', 12), width = 15)
Label_Line2.place(x = 75, y = 100, anchor = 'nw')
Entry_Line2 = tk.Entry(window, show = None, font = ('黑体', 15), textvariable = Txt_Name, width = 20)
Entry_Line2.place(x = 200, y = 100, anchor = 'nw')
# 关于性别的 label 和 entry
sex = tk.StringVar()
sex.set(Sex)
Label_Line7 = tk.Label(window, text = "性 别:", font = ('黑体', 12), width = 15)
Label_Line7.place(x = 75, y = 150, anchor = 'nw')
sex_input1 = tk.Radiobutton(window, text="男", variable=sex, value="男")
sex_input2 = tk.Radiobutton(window, text="女", variable=sex, value="女")
sex_input1.place(x = 200, y = 150, anchor = 'nw')
sex_input2.place(x = 260, y = 150, anchor = 'nw')
# 关于年龄的 label 和 entry
Txt_Age = tk.StringVar()
Txt_Age.set(Age)
Label_Line4 = tk.Label(window, text = "年 龄 (0~25):", font = ('黑体', 12), width = 15)
Label_Line4.place(x = 75, y = 200, anchor = 'nw')
Entry_Line4 = tk.Entry(window, show = None, font = ('黑体', 15), textvariable = Txt_Age, width = 20)
Entry_Line4.place(x = 200, y = 200, anchor = 'nw')
# 关于专业的 label 和 entry
Txt_Major = tk.StringVar()
Txt_Major.set(Major)
Label_Line3 = tk.Label(window, text = "专 业:", font = ('黑体', 12), width = 15)
Label_Line3.place(x = 75, y = 250, anchor = 'nw')
Entry_Line3 = tk.Entry(window, show = None, font = ('黑体', 15), textvariable = Txt_Major, width = 20)
Entry_Line3.place(x = 200, y = 250, anchor = 'nw')
# 关于班级的 label 和 entry
Txt_Class = tk.StringVar()
Txt_Class.set(Class)
Label_Line5 = tk.Label(window, text = "班 级 (数字):", font = ('黑体', 12), width = 15)
Label_Line5.place(x = 75, y = 300, anchor = 'nw')
Entry_Line5 = tk.Entry(window, show = None, font = ('黑体', 15), textvariable = Txt_Class, width = 20)
Entry_Line5.place(x = 200, y = 300, anchor = 'nw')
# 关于电话的 label 和 entry
Txt_Telephone = tk.StringVar()
Txt_Telephone.set(Telephone)
Label_Line6 = tk.Label(window, text = "电 话 (11位):", font = ('黑体', 12), width = 15)
Label_Line6.place(x = 75, y = 350, anchor = 'nw')
Entry_Line6 = tk.Entry(window, show = None, font = ('黑体', 15), textvariable = Txt_Telephone, width = 20)
Entry_Line6.place(x = 200, y = 350, anchor = 'nw')
# 关于"确认"组件,此处绑定函数Mod_Student_Info_1用于修改学生信息
Button1_Yes = tk.Button(window, text = '确认', bg = 'silver', font = ('黑体', 12), command = lambda:Mod_Student_Info_1(ID, Txt_Name.get(), sex.get(), Txt_Age.get(), Txt_Major.get(), Txt_Class.get(), Txt_Telephone.get()), width = 10)
Button1_Yes.place(x = 75, y = 400, anchor = 'nw')
# 关于"取消"组件,此处绑定函数destroy()用于关闭窗口
Button2_No = tk.Button(window, text = '取消', bg = 'silver', font = ('黑体', 12), command = lambda:window.destroy(), width = 10)
Button2_No.place(x = 325, y = 400, anchor = 'nw')
# 在界面中显示文本框,打印result的信息
result = tk.StringVar()
result.set(">>>请输入修改后的信息<<<")
Show_result = tk.Label(window, bg = "white", fg = "black", font = ("黑体", 12), bd = '0', anchor = 'center', textvariable = result)
Show_result.place(x = "50", y = "30", width = "400", height = "50")
# 显示窗口
window.mainloop()
# 查找学生信息的窗口
def Window_Ser():
# 创建app的子窗口
window = tk.Toplevel(app)
window.title("查询学生信息")
# 设置窗口大小:宽x高(500x500),并指定窗口在屏幕上的位置为(500, 200)
window.geometry('500x500+500+200')
# 关于学号的 label 和 entry
Txt_ID = tk.StringVar()
Txt_ID.set("")
Label_Line1 = tk.Label(window, text = "学 号 (6 位):", font = ('黑体', 12), width = 15)
Label_Line1.place(x = 75, y = 100, anchor = 'nw')
Entry_Line1 = tk.Entry(window, show = None, font = ('黑体', 15), textvariable = Txt_ID, width = 20)
Entry_Line1.place(x = 200, y = 100, anchor = 'nw')
# 关于姓名的 label 和 entry
Txt_Name = tk.StringVar()
Txt_Name.set("")
Label_Line2 = tk.Label(window, text = "姓 名:", font = ('黑体', 12), width = 15)
Label_Line2.place(x = 75, y = 150, anchor = 'nw')
Entry_Line2 = tk.Entry(window, show = None, font = ('黑体', 15), textvariable = Txt_Name, width = 20)
Entry_Line2.place(x = 200, y = 150, anchor = 'nw')
# 关于年龄的 label 和 entry
Txt_Age = tk.StringVar()
Txt_Age.set("")
Label_Line4 = tk.Label(window, text = "年 龄 (0~25):", font = ('黑体', 12), width = 15)
Label_Line4.place(x = 75, y = 200, anchor = 'nw')
Entry_Line4 = tk.Entry(window, show = None, font = ('黑体', 15), textvariable = Txt_Age, width = 20)
Entry_Line4.place(x = 200, y = 200, anchor = 'nw')
# 关于专业的 label 和 entry
Txt_Major = tk.StringVar()
Txt_Major.set("")
Label_Line3 = tk.Label(window, text = "专 业:", font = ('黑体', 12), width = 15)
Label_Line3.place(x = 75, y = 250, anchor = 'nw')
Entry_Line3 = tk.Entry(window, show = None, font = ('黑体', 15), textvariable = Txt_Major, width = 20)
Entry_Line3.place(x = 200, y = 250, anchor = 'nw')
# 关于班级的 label 和 entry
Txt_Class = tk.StringVar()
Txt_Class.set("")
Label_Line5 = tk.Label(window, text = "班 级 (数字):", font = ('黑体', 12), width = 15)
Label_Line5.place(x = 75, y = 300, anchor = 'nw')
Entry_Line5 = tk.Entry(window, show = None, font = ('黑体', 15), textvariable = Txt_Class, width = 20)
Entry_Line5.place(x = 200, y = 300, anchor = 'nw')
# 关于电话的 label 和 entry
Txt_Telephone = tk.StringVar()
Txt_Telephone.set("")
Label_Line6 = tk.Label(window, text = "电 话 (11位):", font = ('黑体', 12), width = 15)
Label_Line6.place(x = 75, y = 350, anchor = 'nw')
Entry_Line6 = tk.Entry(window, show = None, font = ('黑体', 15), textvariable = Txt_Telephone, width = 20)
Entry_Line6.place(x = 200, y = 350, anchor = 'nw')
# 关于"确认"组件,此处绑定函数Search_Student_Info用于修改学生信息
Button1_Yes = tk.Button(window, text = '确认', bg = 'silver', font = ('黑体', 12), command = lambda:Search_Student_Info(Txt_ID.get(), Txt_Name.get(), Txt_Major.get(),Txt_Age.get(),Txt_Class.get(),Txt_Telephone.get()), width = 10)
Button1_Yes.place(x = 75, y = 400, anchor = 'nw')
# 关于"取消"组件,此处绑定函数destroy()用于关闭窗口
Button2_No = tk.Button(window, text = '取消', bg = 'silver', font = ('黑体', 12), command = lambda:window.destroy(), width = 10)
Button2_No.place(x = 325, y = 400, anchor = 'nw')
# 在界面中显示文本框,打印result的信息
result = tk.StringVar()
result.set(">>>请输入待查找学生的部分信息(可不全填)<<<")
Show_result = tk.Label(window, bg = "white", fg = "black", font = ("黑体", 12), bd = '0', anchor = 'center', textvariable = result)
Show_result.place(x = "50", y = "30", width = "400", height = "50")
# 显示窗口
window.mainloop()
# 退出管理系统的窗口
def Window_Exit():
# 创建app的子窗口
window = tk.Toplevel()
window.title("退出管理系统")
# 设置窗口大小:宽x高(400x300),并指定窗口在屏幕上的位置为(500, 200)
window.geometry('400x300+500+200')
# 关于"确认"组件,此处绑定函数destroy()用于关闭主窗口
Button1_Yes = tk.Button(window, text = '确认', bg = 'silver', font = ('黑体', 12), command = lambda:app.destroy(), width = 10)
Button1_Yes.place(x = 50, y = 200, anchor = 'nw')
# 关于"取消"组件,此处绑定函数destroy()用于关闭窗口
Button2_No = tk.Button(window, text = '取消', bg = 'silver', font = ('黑体', 12), command = lambda:window.destroy(), width = 10)
Button2_No.place(x = 250, y = 200, anchor = 'nw')
# 在界面中显示文本框,打印result的信息
result = tk.StringVar()
result.set(">>>您确认离开系统吗?<<<")
Show_result = tk.Label(window, bg = "white", fg = "black", font = ("黑体", 15), bd = '0', anchor = 'center', textvariable = result)
Show_result.place(x = "50", y = "75", width = "300", height = "50")
# 显示窗口
window.mainloop()
# 在Text控件上方点击"查找"按钮被触发的函数
def search():
# 删除文本标签名“found”内容,但不删除文本定义
Show_result.tag_remove('found', '1.0', tk.END)
# 设置起始值为1.0,也就是第一排第0个字符
start = 1.0
# 获取输入框的值
key = entry.get()
# strip()方法用于移除字符串两端的空白字符
if len(key.strip()) == 0:
# 如果没有输入时,就当什么都没有发生
return -1
# 死循环,因为我们查找的可能不止一个值,可能文本框中有多个
while True:
# 执行查找,key是我们需要查询的,start是起始值,END是从头查到尾,函数自动返回查询到的索引位置
pos = Show_result.search(key, start, tk.END)
# 查不到就结束,break是退出死循环
if pos == '':
break
# 进行到这一步说明查到了数据
# 添加标签名“found”,pos是查询到的索引位置
Show_result.tag_add('found', pos, '%s+%dc' % (pos, len(key)))
# 这里是更新查找起始位置
start = '%s+%dc' % (pos, len(key))
# 找到字符串后,滚动到该位置
Show_result.see(pos)
# 定义找到的标签
Show_result.tag_configure('found', background='yellow')
# 如果该文件是程序运行的主文件,将会运行以下代码
if __name__ == '__main__':
# 创建主窗口
app = tk.Tk()
app.title("学生信息管理系统")
# 设置窗口大小:宽x高(900x500),并指定窗口在屏幕上的位置为(200, 200)
app.geometry('930x550+200+200')
# 关于"添加学生信息"控件,此处绑定函数Search_Student_Info用于修改学生信息
Button1_Add = tk.Button(app, text = '添 加 学 生 信 息', bg = 'silver', font = ('黑体', 15), command = Window_Add, width = 20)
Button1_Add.place(x = 40, y = 30, anchor = 'nw')
Button2_Del = tk.Button(app, text = '删 除 学 生 信 息', bg = 'silver', font = ('黑体', 15), command = Window_Del, width = 20)
Button2_Del.place(x = 40, y = 110, anchor = 'nw')
Button3_Mod = tk.Button(app, text = '修 改 学 生 信 息', bg = 'silver', font = ('黑体', 15), command = Window_Mod, width = 20)
Button3_Mod.place(x = 40, y = 190, anchor = 'nw')
Button4_Ser = tk.Button(app, text = '查 询 学 生 信 息', bg = 'silver', font = ('黑体', 15), command = Window_Ser, width = 20)
Button4_Ser.place(x = 40, y = 270, anchor = 'nw')
Button5_Show = tk.Button(app, text = '显 示 所 有 信 息', bg = 'silver', font = ('黑体', 15), command = lambda:Print_Student_Info(Info), width = 20)
Button5_Show.place(x = 40, y = 350, anchor = 'nw')
Button6_Exit = tk.Button(app, text = '退 出 管 理 系 统', bg = 'silver', font = ('黑体', 15), command = Window_Exit, width = 20)
Button6_Exit.place(x = 40, y = 430, anchor = 'nw')
# 输入查找的文本框
entry = tk.Entry()
entry.place(x = "280", y = "30", width = "200", height = "25")
# 查找按钮
btu = tk.Button(app, text='查找', command=search)
btu.place(x = "490", y = "30", width = "40", height = "25")
# 创建一个Text控件,用于输出学生信息
Show_result = tk.Text(app, spacing1=2, spacing2=2, spacing3=2, wrap='none', font=10, width=40, height=10)
Show_result.place(x = "280", y = "70", width = "600", height = "400")
result = tk.StringVar()
result.set(" >>>欢迎使用学生信息管理系统<<<\n\n 使用说明:运行该程序时,默认会在D盘创建一个StudentData.xlsx文件,用于\n 存储学生信息。注意,运行该程序前,如果StudentData.xlsx文件已被双击打开,\n 必须先关闭该Excel文件,否则会报错并自动退出程序。")
# 创建一个垂直滚动条并将其与Text控件关联
v_scrollbar = ttk.Scrollbar(app, orient='vertical', command=Show_result.yview)
v_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
Show_result.configure(yscrollcommand=v_scrollbar.set)
# 创建一个水平滚动条并将其与Text控件关联
h_scrollbar = ttk.Scrollbar(app, orient='horizontal', command=Show_result.xview)
h_scrollbar.pack(side=tk.BOTTOM, fill=tk.X)
Show_result.configure(xscrollcommand=h_scrollbar.set)
# 绑定变量到Text控件
Show_result.insert('end', result.get())
# 在主界面创建“汇总”信息,显示学生信息的数量
total_label = tk.Label(app, text='汇总:', font=10)
total_label.place(x = "740", y = "38", width = "50", height = "25")
total_text = tk.Text(app, relief=tk.FLAT, bg='#f0f0f0', font=10)
total_text.place(x = "790", y = "40", width = "50", height = "25")
# 开启主循环,让窗口处于显示状态
app.mainloop()