目录
一、引言
在现实生活中,有些教育管理仍困于纸质表格与手动录入的低效循环,因此,想做一款融合Python逻辑力量、PyQt优雅界面与Excel轻量存储的本地化系统 ------它既不依赖云端,又不索取权限,只需一个Excel文件作为数据库 ,便可让学籍数据的增、删、改、查从繁琐的手工操作,蜕变为指尖轻点的流畅体验。无需数据库服务器,无需复杂部署,只用最熟悉的Excel承载最核心的数据,用最专业的PyQt重构最直观的交互,让(教育)信息管理者(当然其他行业也可以,只需微调代码)轻松驾驭一套真正属于自己的学生信息管理工具。
二、GUI界面设计
使用PyQt5进行界面的搭建,最终的增、删、改、查界面如下:


信息管理者可在"增加"页面新增学生信息;在"删除"页面将身份证号码作为主键,删除该学生的全部信息;在"修改"页面根据身份证号码匹配已在数据库中的学生,并修改其任意字段所对应的信息;在"查找"页面以身份证号码查找学生是否存在。
当然,上述操作界面下方的表格也会根据用户的操作(增、删、改、查),实时更新excel数据库并显示最新的学生信息。
1.上传学生信息
在程序运行的初始阶段,除对用户表示欢迎之外,更为重要的是需要询问信息管理者是否具有"学生信息表";如果用户选择"Yes",则弹出"请选择文件"界面,选择相应且满足格式的文件并上传后,会弹出"上传成功"的提示;如果用户选择"No",则会自动下载Excel文件"学生信息表模板"至当前目录下。


下载的Excel文件"学生信息模板"如下,按列录入学生信息再保存上传即可。

这里需要注意: 当填写完上述的空模板文件后,无论是选择"OK",还是直接关闭该对话框,均会提示"学生信息表是否填写完毕"。此时若选择"Yes",则会跳转到上传文件的界面,反之则会一直提示"学生信息表是否填写完毕",直至上传的文件没有问题之后,才会进入到主程序界面并即刻显示excel文件中的数据。

2.增加学生信息

依次填写"年级"、"班级"、"学号"、"身份证号"、"姓名"信息后,点击"添加",新增信息默认添加至表格的末尾。需要注意的是,这五项信息缺一不可,均为必填字段 ,若有一项是空白,则会弹出提示"请填写全部信息!",直至满足条件后,才会添加成功(这里并未对每个输入框信息格式做特别要求,均按照字符串进行处理,若有需求,可自行添加正则表达式规定特定输入格式)。此外,若添加的学生在表格中已经存在(根据身份证号码进行判断),则会弹出"该生已存在,无法新增"的提示。

3.删除学生信息
删除指定学生信息,仅需要该生的身份证号码即可。点击"删除"后,自行将数据库中该生的全部信息删除,若下方还有学生,则依次向上递补,同时显示"删除成功"。若该生不存在,点击"删除"后,则无法删除。


4.修改学生信息
首先,同样通过输入身份证号码来判断学生是否存在,若不存在,提示"无法修改";若存在,则需要通过下拉框来选择被修改字段名称(修改值不能为空,否则出现"填写全部信息"的提示),最后单击"修改",显示"修改成功",并实时更新在下方表格中。但每点击一次"修改",只能修改一个字段中的一个信息。


5.查找学生信息
首先,同样通过输入身份证号码来判断学生是否存在,若不存在,提示"不存在该生";若存在,则直接在下方表格中用蓝色定位到该生的整行所有信息。

6.实时更新excel表
以上增、删、改、查操作,不仅仅会实时更新在界面下方的表格中,还会实时同步更新到用户所上传的excel模板文件中。
7.界面设计jiemian.py
最后附上通过pyuic5产生的GUI界面代码jiemian.py:
python
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'jiemian.ui'
#
# Created by: PyQt5 UI code generator 5.15.11
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Form(object):
def setupUi(self, Form):
Form.setObjectName("Form")
Form.setEnabled(True)
Form.resize(600, 580)
Form.setMinimumSize(QtCore.QSize(600, 580))
Form.setMaximumSize(QtCore.QSize(600, 580))
icon = QtGui.QIcon()
icon.addPixmap(QtGui.QPixmap(":/image1.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
Form.setWindowIcon(icon)
self.label_5 = QtWidgets.QLabel(Form)
self.label_5.setGeometry(QtCore.QRect(370, 40, 141, 31))
font = QtGui.QFont()
font.setFamily("Adobe Arabic")
font.setPointSize(16)
self.label_5.setFont(font)
self.label_5.setObjectName("label_5")
self.label_8 = QtWidgets.QLabel(Form)
self.label_8.setGeometry(QtCore.QRect(190, 10, 211, 31))
font = QtGui.QFont()
font.setFamily("Adobe Arabic")
font.setPointSize(18)
font.setBold(False)
font.setWeight(50)
self.label_8.setFont(font)
self.label_8.setObjectName("label_8")
self.label_10 = QtWidgets.QLabel(Form)
self.label_10.setGeometry(QtCore.QRect(450, 30, 71, 51))
self.label_10.setText("")
self.label_10.setPixmap(QtGui.QPixmap(":/image1.png"))
self.label_10.setObjectName("label_10")
self.tabWidget = QtWidgets.QTabWidget(Form)
self.tabWidget.setGeometry(QtCore.QRect(30, 80, 541, 171))
self.tabWidget.setObjectName("tabWidget")
self.tab = QtWidgets.QWidget()
self.tab.setObjectName("tab")
self.label = QtWidgets.QLabel(self.tab)
self.label.setGeometry(QtCore.QRect(30, 20, 31, 21))
self.label.setObjectName("label")
self.lineEdit = QtWidgets.QLineEdit(self.tab)
self.lineEdit.setGeometry(QtCore.QRect(60, 20, 161, 20))
self.lineEdit.setObjectName("lineEdit")
self.label_2 = QtWidgets.QLabel(self.tab)
self.label_2.setGeometry(QtCore.QRect(30, 60, 31, 21))
self.label_2.setObjectName("label_2")
self.lineEdit_2 = QtWidgets.QLineEdit(self.tab)
self.lineEdit_2.setGeometry(QtCore.QRect(60, 60, 161, 20))
self.lineEdit_2.setObjectName("lineEdit_2")
self.label_3 = QtWidgets.QLabel(self.tab)
self.label_3.setGeometry(QtCore.QRect(290, 20, 41, 21))
self.label_3.setObjectName("label_3")
self.lineEdit_3 = QtWidgets.QLineEdit(self.tab)
self.lineEdit_3.setGeometry(QtCore.QRect(350, 20, 161, 20))
self.lineEdit_3.setText("")
self.lineEdit_3.setObjectName("lineEdit_3")
self.lineEdit_4 = QtWidgets.QLineEdit(self.tab)
self.lineEdit_4.setGeometry(QtCore.QRect(350, 60, 161, 20))
self.lineEdit_4.setObjectName("lineEdit_4")
self.label_4 = QtWidgets.QLabel(self.tab)
self.label_4.setGeometry(QtCore.QRect(290, 60, 81, 21))
self.label_4.setObjectName("label_4")
self.label_6 = QtWidgets.QLabel(self.tab)
self.label_6.setGeometry(QtCore.QRect(30, 100, 31, 21))
self.label_6.setObjectName("label_6")
self.lineEdit_5 = QtWidgets.QLineEdit(self.tab)
self.lineEdit_5.setGeometry(QtCore.QRect(60, 100, 161, 20))
self.lineEdit_5.setObjectName("lineEdit_5")
self.pushButton = QtWidgets.QPushButton(self.tab)
self.pushButton.setGeometry(QtCore.QRect(350, 100, 161, 23))
self.pushButton.setObjectName("pushButton")
self.tabWidget.addTab(self.tab, "")
self.tab_2 = QtWidgets.QWidget()
self.tab_2.setObjectName("tab_2")
self.label_7 = QtWidgets.QLabel(self.tab_2)
self.label_7.setGeometry(QtCore.QRect(130, 40, 111, 21))
self.label_7.setObjectName("label_7")
self.lineEdit_6 = QtWidgets.QLineEdit(self.tab_2)
self.lineEdit_6.setGeometry(QtCore.QRect(250, 40, 161, 20))
self.lineEdit_6.setObjectName("lineEdit_6")
self.pushButton_2 = QtWidgets.QPushButton(self.tab_2)
self.pushButton_2.setGeometry(QtCore.QRect(250, 80, 161, 23))
self.pushButton_2.setObjectName("pushButton_2")
self.tabWidget.addTab(self.tab_2, "")
self.tab_3 = QtWidgets.QWidget()
self.tab_3.setObjectName("tab_3")
self.lineEdit_7 = QtWidgets.QLineEdit(self.tab_3)
self.lineEdit_7.setGeometry(QtCore.QRect(250, 20, 161, 20))
self.lineEdit_7.setObjectName("lineEdit_7")
self.label_9 = QtWidgets.QLabel(self.tab_3)
self.label_9.setGeometry(QtCore.QRect(130, 20, 111, 21))
self.label_9.setObjectName("label_9")
self.label_11 = QtWidgets.QLabel(self.tab_3)
self.label_11.setGeometry(QtCore.QRect(130, 50, 111, 21))
self.label_11.setObjectName("label_11")
self.comboBox = QtWidgets.QComboBox(self.tab_3)
self.comboBox.setGeometry(QtCore.QRect(250, 50, 161, 22))
self.comboBox.setObjectName("comboBox")
self.label_12 = QtWidgets.QLabel(self.tab_3)
self.label_12.setGeometry(QtCore.QRect(130, 80, 111, 21))
self.label_12.setObjectName("label_12")
self.lineEdit_8 = QtWidgets.QLineEdit(self.tab_3)
self.lineEdit_8.setGeometry(QtCore.QRect(250, 80, 161, 20))
self.lineEdit_8.setObjectName("lineEdit_8")
self.pushButton_3 = QtWidgets.QPushButton(self.tab_3)
self.pushButton_3.setGeometry(QtCore.QRect(250, 110, 161, 23))
self.pushButton_3.setObjectName("pushButton_3")
self.tabWidget.addTab(self.tab_3, "")
self.tab_4 = QtWidgets.QWidget()
self.tab_4.setObjectName("tab_4")
self.pushButton_4 = QtWidgets.QPushButton(self.tab_4)
self.pushButton_4.setGeometry(QtCore.QRect(250, 80, 161, 23))
self.pushButton_4.setObjectName("pushButton_4")
self.lineEdit_9 = QtWidgets.QLineEdit(self.tab_4)
self.lineEdit_9.setGeometry(QtCore.QRect(250, 40, 161, 20))
self.lineEdit_9.setObjectName("lineEdit_9")
self.label_13 = QtWidgets.QLabel(self.tab_4)
self.label_13.setGeometry(QtCore.QRect(130, 40, 111, 21))
self.label_13.setObjectName("label_13")
self.tabWidget.addTab(self.tab_4, "")
self.retranslateUi(Form)
self.tabWidget.setCurrentIndex(0)
QtCore.QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
_translate = QtCore.QCoreApplication.translate
Form.setWindowTitle(_translate("Form", "Student_Info_Management"))
self.label_5.setText(_translate("Form", "Designed By"))
self.label_8.setText(_translate("Form", "学生信息管理系统"))
self.label.setText(_translate("Form", "年级"))
self.label_2.setText(_translate("Form", "学号"))
self.label_3.setText(_translate("Form", "班 级"))
self.label_4.setText(_translate("Form", "身份证号"))
self.label_6.setText(_translate("Form", "姓名"))
self.pushButton.setText(_translate("Form", "添加"))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), _translate("Form", "增加"))
self.label_7.setText(_translate("Form", "请输入身份证号码:"))
self.pushButton_2.setText(_translate("Form", "删除"))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), _translate("Form", "删除"))
self.label_9.setText(_translate("Form", "请输入身份证号码:"))
self.label_11.setText(_translate("Form", "请选择修改字段:"))
self.label_12.setText(_translate("Form", "修 改 为:"))
self.pushButton_3.setText(_translate("Form", "修改"))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_3), _translate("Form", "修改"))
self.pushButton_4.setText(_translate("Form", "查找"))
self.label_13.setText(_translate("Form", "请输入身份证号码:"))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_4), _translate("Form", "查找"))
import ziyuan_rc
三、主要程序详解
1.导入所需模块
python
import sys,os
from jiemian import *
from PyQt5.QtWidgets import QApplication, QWidget, QTableView, QHeaderView
import pandas as pd
from openpyxl import Workbook
from PyQt5.QtGui import QStandardItemModel, QStandardItem
# 保持窗口大小和qtdesigner中的一致
from PyQt5 import QtCore
QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling)
QTableView与表格相关;QHeaderView用于控制QTableView的列宽和行高等参数;QStandardItemModel和QStandardItem用于在tableview中添加(更新)数据,并设置模型到视图。不懂为啥需要导入Qtcore的,请看****************************一键曝光:Python+PyQt实现的文件目录透视镜****************************。
2.初始化设置
python
def __init__(self):
super(QWidget, self).__init__()
self.setupUi(self)
self.pushButton.clicked.connect(self.add_stu)
self.pushButton_2.clicked.connect(self.delete_stu)
self.pushButton_3.clicked.connect(self.modify_stu)
self.pushButton_4.clicked.connect(self.find_stu)
self.comboBox.addItems(['年级', '班级', '学号', '身份证号码', '姓名']) # 提前将用户修改字段内容写入
self.shangchuan_flag = False
# 判断是否上传
while(self.shangchuan_flag==False):
shangchuan_judge = QtWidgets.QMessageBox.question(None, "欢迎使用", "是否具有学生信息表?",
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No)
if shangchuan_judge==QtWidgets.QMessageBox.Yes:
# 如果具有学生信息表,则直接上传
self.excel_data=shangchuan()
self.shangchuan_flag = True
else:
# 否则调用xiazai,下载模板后填写完毕,再重新上传
xiazai()
self.tianxie_flag = False
while(self.tianxie_flag==False):
tianxie_judge = QtWidgets.QMessageBox.question(None, "提示", "学生信息表是否填写完毕?",
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No)
if tianxie_judge==QtWidgets.QMessageBox.Yes:
self.tianxie_flag=True
self.excel_data=shangchuan()
self.shangchuan_flag = True
# 上传的数据存储在excel_data中,供后续使用
# print(self.excel_data)
self.table_view = QTableView(self)
self.table_view.setGeometry(30, 280, 540, 270) # setGeometry(x, y, width, height)
self.table_view.setEditTriggers(QTableView.NoEditTriggers) # 禁止编辑单元格
self.table_view.horizontalHeader().setSectionResizeMode(QHeaderView.Fixed) # 禁止用户手动调整列宽
self.table_view.verticalHeader().setSectionResizeMode(QHeaderView.Fixed) # 禁止用户手动调整行高
self.table_view.setAlternatingRowColors(True) # 设置交替行显示
self.model = QStandardItemModel(len(self.excel_data), 5) # 创建行列
self.model.setHorizontalHeaderLabels(['年级', '班级', '学号', '身份证号码', '姓名']) # 设置每列的标题
self.xianshi()
将四个pushbutton分别绑定于add_stu、delete_stu、modify_stu、find_stu,并提前在"请选择修改字段"combobox中写入被修改字段名称;设置标志位shangchuan_flag,通过自定义函数shangchuan以判断用户是否上传学生信息表,且上传的学生信息表是否符合格式内容要求;若用户没有学生信息表模板,则调用自定义函数xiazai,待填写完成后在弹出的对话框中选择文件并上传即可。
完成以上程序后,获取用户上传的学生信息表内容并存储在excel_data中,供后续使用;接下来需要初始化tableview的相关参数:显示位置、大小(宽度和高度)、编辑单元格权限、手动调整行高列宽权限、交替行醒目区分显示 、行数、列数以及列名;最后通过xianshi实时显示更新。
3.增
python
def add_stu(self):
# 判断新增信息是否写全
if (self.lineEdit.text()!="") and (self.lineEdit_2.text()!="") and (self.lineEdit_3.text()!="") and (self.lineEdit_4.text()!="") and (self.lineEdit_5.text()!=""):
if chazhao(self.lineEdit_4.text(), self.excel_data):
# 已存在的学生不能再次新增
QtWidgets.QMessageBox.critical(self, "新增", "该生已存在,无法新增!")
else:
# 通过len获取末尾行索引,通过loc直接按序写入整行数据
self.excel_data.loc[len(self.excel_data)]={'年级':self.lineEdit.text(), '学号':self.lineEdit_2.text(), '班级':self.lineEdit_3.text(),
'身份证号码':self.lineEdit_4.text(), '姓名':self.lineEdit_5.text()}
# 及时通过xieru函数更新excel表内容
xieru(self.excel_data)
self.xianshi()
QtWidgets.QMessageBox.information(self, "新增", "新增成功!")
else:
QtWidgets.QMessageBox.critical(self, "提示", "请填写全部信息!")
# print(self.excel_data)
根据"与"判断5个输入框的内容是否为空,再根据自定义函数chazhao判断该生是否已经存在(依据lineedit_4所输的身份证号码),满足条件后通过len获取excel_data末尾索引,并通过loc按序写入整行数据。最后,通过自定义函数xieru及时更新excel文件中的数据,通过xianshi来更新tableview,并提示"新增成功"。
4.删
python
def delete_stu(self):
if chazhao(self.lineEdit_6.text(), self.excel_data):
# 通过身份证号判断学生是否在表中,不在表中的无法删除
for i in range(len(self.excel_data)):
# 通过遍历身份证号码获取行索引
if str(self.excel_data['身份证号码'][i]) == self.lineEdit_6.text():
index = i
break
self.excel_data = self.excel_data.drop(index) # 通过行索引删除整行数据
self.excel_data = self.excel_data.reset_index(drop=True) # 被删除数据位置不定,故删除后需要重新排序(更新行索引)
xieru(self.excel_data)
self.model.removeRows(0, self.model.rowCount())
# 为及时更新数据,在删后需将tableview清空,再重新写入
# 其实也应该在增和改中进行同样操作,但增和改没报错,只有删的数据有问题,所以只在这里进行处理
self.xianshi()
QtWidgets.QMessageBox.information(self, "删除", "删除成功!")
else:
QtWidgets.QMessageBox.critical(self, "删除", "该生不存在,无法删除!")
# print(self.excel_data)
通过chazhao函数判断是否存在,只有存在的学生才能删除,否则提示"无法删除"。接下来通过遍历并匹配身份证号来获取行索引,一旦找到就break循环。退出循环后,通过刚才在循环中获取到的行索引index来删除drop整行数据。
注意:
1.被删除数据所处位置不定,所以在删除后需要重新更新所有行索引。
2.删除后也需要通过xieru来更新excel文件。
3.为及时更新数据,在删后需将tableview清空,再重新写入。其实也应该在增和改中进行同样操作,但增和改没报错,只有删的数据有问题,所以只在这里进行单独处理。
4.最后将最新数据xianshi在tableview中。
5.改
python
def modify_stu(self):
if chazhao(self.lineEdit_7.text(), self.excel_data):
# 通过身份证号判断学生是否在表中,不在表中的无法修改
col_index = self.comboBox.currentIndex() # 列索引即combobox中items的索引,两者一致
modify_content = self.lineEdit_8.text()
if modify_content != "":
for i in range(len(self.excel_data)):
if str(self.excel_data['身份证号码'][i]) == self.lineEdit_7.text():
row_index = i
break
self.excel_data.iloc[row_index, col_index] = modify_content
xieru(self.excel_data)
self.xianshi()
QtWidgets.QMessageBox.information(self, "修改", "修改成功!")
else:
QtWidgets.QMessageBox.critical(self, "提示", "请填写全部信息!")
else:
QtWidgets.QMessageBox.critical(self, "修改", "该生不存在,无法修改!")
# print(self.excel_data)
通过身份证号在chazhao函数中判断学生是否在表中,不在表中的无法修改;获取用户选择的combobox字段索引(也即excel_data中的列索引),再加上通过遍历得到的行索引将用户修改内容modify_content写入对应位置。最后xieru和xianshi。
6.查
python
def find_stu(self):
try:
row_index = [str(i) for i in self.excel_data['身份证号码']].index(self.lineEdit_9.text())
# 根据身份证直接定位该学生所有信息
self.table_view.selectRow(row_index)
QtWidgets.QMessageBox.information(self, "查找", "该生存在!")
except ValueError:
QtWidgets.QMessageBox.critical(self, "查找", "不存在该生!")
"查"比较简单,未使用chazhao,而是直接根据列表推导式获取对应的行索引row_index,但需要注意要想实现数据定位selectRow到行,须将参数类型改为str 。整体用异常处理解决是否存在问题 :若存在则row_index中有相应具体值,否则会提示ValueError,这是一个很好的思路。
7.表格实时更新显示
python
def xianshi(self):
# 给tableview中添加/更新数据
# self.model.removeRows(0, self.model.rowCount()) # 为及时更新数据,在增、删、改后需将tableview清空
for i in range(0, len(self.excel_data)):
for j in range(0, 5):
self.model.setItem(i, j, QStandardItem(str(self.excel_data.iloc[i, j]))) # 为保证数据显示完全,将所有数据类型转换为str
# 设置模型到视图
self.table_view.setModel(self.model)
self.table_view.setColumnWidth(0,72) # 在 setModel() 之后再调用 setColumnWidth(),列宽设置才能生效
self.table_view.setColumnWidth(1,72) # 单位为像素
self.table_view.setColumnWidth(2,72)
self.table_view.setColumnWidth(3,189)
self.table_view.setColumnWidth(4,76)
实时更新并写入excel_data中的数据;在将模型映射到视图后,才能设置每行的列宽参数。
8.自定义函数
python
def shangchuan():
filepath, _ = QtWidgets.QFileDialog.getOpenFileName(None, "请选择文件", "", "XLSX工作表 (*.xlsx)") # 获取文件路径
if filepath:
# 获取原始df
yuanshi_df = pd.read_excel(filepath)
if not yuanshi_df.empty:
QtWidgets.QMessageBox.information(None, "成功", "上传成功!")
return yuanshi_df
else:
QtWidgets.QMessageBox.critical(None, "提示", "请检查操作步骤或上传非空文件!")
shangchuan() # 直到上传非空文件
else:
QtWidgets.QMessageBox.critical(None, "提示", "请选择XLSX工作表类型!")
shangchuan() # 直到成功选择类型并上传
通过递归使用户一直上传,除非上传了工作表类型,且文件非空。
python
def xiazai():
wb = Workbook()
ws = wb.active
# 写入列名
ws['A1'] = '年级'
ws['B1'] = '班级'
ws['C1'] = '学号'
ws['D1'] = '身份证号码'
ws['E1'] = '姓名'
# 设置列宽(身份证和姓名稍宽)
ws.column_dimensions['D'].width = 30
ws.column_dimensions['E'].width = 20
wb.save('全校学生信息.xlsx')
QtWidgets.QMessageBox.information(None, "成功", "学生信息表模板下载成功,请查看当前目录!")
定义"全校学生信息"模板文件的列名及列宽,并保存至当前目录下。
python
def xieru(df):
df.to_excel('全校学生信息.xlsx', index=False)
将参数df中的数据写入到excel文件中,起到实时更新excel文件的作用。
python
def chazhao(xiang, zong):
# 无论是数据库的哪一项基础操作(增、删、改、查),"查"就不调用了,因为要定位,还需要返回索引,这里就简单判断,返回索引放在class中了
# 第一步均需进行查找,故将其写为函数,供随时调用
if str(xiang) in [str(i) for i in list(zong['身份证号码'])]: # 使用列表推导式将所有类型统一为str
return True
else:
return False
使用列表推导式直接判断是否存在,若存在,返回布尔类型True,否则返回False。
四、总程序代码
python
import sys,os
from jiemian import *
from PyQt5.QtWidgets import QApplication, QWidget, QTableView, QHeaderView
import pandas as pd
from openpyxl import Workbook
from PyQt5.QtGui import QStandardItemModel, QStandardItem
# 保持窗口大小和qtdesigner中的一致
from PyQt5 import QtCore
QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling)
class login_interface(QWidget, Ui_Form):
def __init__(self):
super(QWidget, self).__init__()
self.setupUi(self)
self.pushButton.clicked.connect(self.add_stu)
self.pushButton_2.clicked.connect(self.delete_stu)
self.pushButton_3.clicked.connect(self.modify_stu)
self.pushButton_4.clicked.connect(self.find_stu)
self.comboBox.addItems(['年级', '班级', '学号', '身份证号码', '姓名']) # 提前将用户修改字段内容写入
self.shangchuan_flag = False
# 判断是否上传
while(self.shangchuan_flag==False):
shangchuan_judge = QtWidgets.QMessageBox.question(None, "欢迎使用", "是否具有学生信息表?",
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No)
if shangchuan_judge==QtWidgets.QMessageBox.Yes:
# 如果具有学生信息表,则直接上传
self.excel_data=shangchuan()
self.shangchuan_flag = True
else:
# 否则调用xiazai,下载模板后填写完毕,再重新上传
xiazai()
self.tianxie_flag = False
while(self.tianxie_flag==False):
tianxie_judge = QtWidgets.QMessageBox.question(None, "提示", "学生信息表是否填写完毕?",
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No)
if tianxie_judge==QtWidgets.QMessageBox.Yes:
self.tianxie_flag=True
self.excel_data=shangchuan()
self.shangchuan_flag = True
# 上传的数据存储在excel_data中,供后续使用
# print(self.excel_data)
self.table_view = QTableView(self)
self.table_view.setGeometry(30, 280, 540, 270) # setGeometry(x, y, width, height)
self.table_view.setEditTriggers(QTableView.NoEditTriggers) # 禁止编辑单元格
self.table_view.horizontalHeader().setSectionResizeMode(QHeaderView.Fixed) # 禁止用户手动调整列宽
self.table_view.verticalHeader().setSectionResizeMode(QHeaderView.Fixed) # 禁止用户手动调整行高
self.table_view.setAlternatingRowColors(True) # 设置交替行显示
self.model = QStandardItemModel(len(self.excel_data), 5) # 创建行列
self.model.setHorizontalHeaderLabels(['年级', '班级', '学号', '身份证号码', '姓名']) # 设置每列的标题
self.xianshi()
def add_stu(self):
# 判断新增信息是否写全
if (self.lineEdit.text()!="") and (self.lineEdit_2.text()!="") and (self.lineEdit_3.text()!="") and (self.lineEdit_4.text()!="") and (self.lineEdit_5.text()!=""):
if chazhao(self.lineEdit_4.text(), self.excel_data):
# 已存在的学生不能再次新增
QtWidgets.QMessageBox.critical(self, "新增", "该生已存在,无法新增!")
else:
# 通过len获取末尾行索引,通过loc直接按序写入整行数据
self.excel_data.loc[len(self.excel_data)]={'年级':self.lineEdit.text(), '学号':self.lineEdit_2.text(), '班级':self.lineEdit_3.text(),
'身份证号码':self.lineEdit_4.text(), '姓名':self.lineEdit_5.text()}
# 及时通过xieru函数更新excel表内容
xieru(self.excel_data)
self.xianshi()
QtWidgets.QMessageBox.information(self, "新增", "新增成功!")
else:
QtWidgets.QMessageBox.critical(self, "提示", "请填写全部信息!")
# print(self.excel_data)
def delete_stu(self):
if chazhao(self.lineEdit_6.text(), self.excel_data):
# 通过身份证号判断学生是否在表中,不在表中的无法删除
for i in range(len(self.excel_data)):
# 通过遍历身份证号码获取行索引
if str(self.excel_data['身份证号码'][i]) == self.lineEdit_6.text():
index = i
break
self.excel_data = self.excel_data.drop(index) # 通过行索引删除整行数据
self.excel_data = self.excel_data.reset_index(drop=True) # 被删除数据位置不定,故删除后需要重新排序(更新行索引)
xieru(self.excel_data)
self.model.removeRows(0, self.model.rowCount())
# 为及时更新数据,在删后需将tableview清空,再重新写入
# 其实也应该在增和改中进行同样操作,但增和改没报错,只有删的数据有问题,所以只在这里进行处理
self.xianshi()
QtWidgets.QMessageBox.information(self, "删除", "删除成功!")
else:
QtWidgets.QMessageBox.critical(self, "删除", "该生不存在,无法删除!")
# print(self.excel_data)
def modify_stu(self):
if chazhao(self.lineEdit_7.text(), self.excel_data):
# 通过身份证号判断学生是否在表中,不在表中的无法修改
col_index = self.comboBox.currentIndex() # 列索引即combobox中items的索引,两者一致
modify_content = self.lineEdit_8.text()
if modify_content != "":
for i in range(len(self.excel_data)):
if str(self.excel_data['身份证号码'][i]) == self.lineEdit_7.text():
row_index = i
break
self.excel_data.iloc[row_index, col_index] = modify_content
xieru(self.excel_data)
self.xianshi()
QtWidgets.QMessageBox.information(self, "修改", "修改成功!")
else:
QtWidgets.QMessageBox.critical(self, "提示", "请填写全部信息!")
else:
QtWidgets.QMessageBox.critical(self, "修改", "该生不存在,无法修改!")
# print(self.excel_data)
def find_stu(self):
try:
row_index = [str(i) for i in self.excel_data['身份证号码']].index(self.lineEdit_9.text())
# 根据身份证直接定位该学生所有信息
self.table_view.selectRow(row_index)
QtWidgets.QMessageBox.information(self, "查找", "该生存在!")
except ValueError:
QtWidgets.QMessageBox.critical(self, "查找", "不存在该生!")
def xianshi(self):
# 给tableview中添加/更新数据
# self.model.removeRows(0, self.model.rowCount()) # 为及时更新数据,在增、删、改后需将tableview清空
for i in range(0, len(self.excel_data)):
for j in range(0, 5):
self.model.setItem(i, j, QStandardItem(str(self.excel_data.iloc[i, j]))) # 为保证数据显示完全,将所有数据类型转换为str
# 设置模型到视图
self.table_view.setModel(self.model)
self.table_view.setColumnWidth(0,72) # 在 setModel() 之后再调用 setColumnWidth(),列宽设置才能生效
self.table_view.setColumnWidth(1,72) # 单位为像素
self.table_view.setColumnWidth(2,72)
self.table_view.setColumnWidth(3,189)
self.table_view.setColumnWidth(4,76)
def shangchuan():
filepath, _ = QtWidgets.QFileDialog.getOpenFileName(None, "请选择文件", "", "XLSX工作表 (*.xlsx)") # 获取文件路径
if filepath:
# 获取原始df
yuanshi_df = pd.read_excel(filepath)
if not yuanshi_df.empty:
QtWidgets.QMessageBox.information(None, "成功", "上传成功!")
return yuanshi_df
else:
QtWidgets.QMessageBox.critical(None, "提示", "请检查操作步骤或上传非空文件!")
shangchuan() # 直到上传非空文件
else:
QtWidgets.QMessageBox.critical(None, "提示", "请选择XLSX工作表类型!")
shangchuan() # 直到成功选择类型并上传
def xiazai():
wb = Workbook()
ws = wb.active
# 写入列名
ws['A1'] = '年级'
ws['B1'] = '班级'
ws['C1'] = '学号'
ws['D1'] = '身份证号码'
ws['E1'] = '姓名'
# 设置列宽(身份证和姓名稍宽)
ws.column_dimensions['D'].width = 30
ws.column_dimensions['E'].width = 20
wb.save('全校学生信息.xlsx')
QtWidgets.QMessageBox.information(None, "成功", "学生信息表模板下载成功,请查看当前目录!")
def xieru(df):
df.to_excel('全校学生信息.xlsx', index=False)
def chazhao(xiang, zong):
# 无论是数据库的哪一项基础操作(增、删、改、查),"查"就不调用了,因为要定位,还需要返回索引,这里就简单判断,返回索引放在class中了
# 第一步均需进行查找,故将其写为函数,供随时调用
if str(xiang) in [str(i) for i in list(zong['身份证号码'])]: # 使用列表推导式将所有类型统一为str
return True
else:
return False
if __name__ == '__main__':
app = QApplication(sys.argv)
w = login_interface()
w.show()
sys.exit(app.exec_())
欢迎留言/私信沟通交流!