最近在学习GUI设计,分享做的一些小项目。
这篇文我们讲一下如何基于PyQt5和MySQL用Python制作简单的学生信息管理系统。
(由于内容比较多,我们分模块讲解。)
添加博主vx(私信)获得源码和资源文件
当我们着手开发一款软件,得先有软件架构的思路。
我们先用PM的视角去了解它:
1.系统架构
我们把整个系统分为5个模块,并列出了各个模块的需求。
2.产品使用流程图
整个系统的操作流程也直观地体现出来了。
(逻辑性和条理性比较清晰。)
开发环境及工具如下:
操作系统:Win 10
Python版本:Python 3.8.6
开发工具:PyCharm专业版、MySQL、Navicat、PyQt5
我们直接使用数据库可视化工具Navicat生成需要的表和视图。
数据库的设计就完成了,后续使用只需向数据库中导入相关数据即可。
我们定义一个open()函数用于连接数据库:
# 连接数据库连接
def open():
db = pymysql.connect(host='localhost', user='root', password='111111', database="db_student",charset="utf8")
return db
( 数据库信息根据个人修改。)
我们定义一个exec()函数用于数据库数据的增、删、改:
# 数据库数据的增、删、改操作
def exec(sql,values):
db=open()
cursor = db.cursor() # 使用cursor获取操作游标
try:
cursor.execute(sql,values) # 执行增、删、改的SQL语句
db.commit()
return 1
except:
db.rollback()
return 0
finally:
cursor.close()
db.close()
我们定义query()函数用于数据库数据带参数的精确查询:
# 带参数的精确查询
def query(sql,*keys):
db=open()
cursor = db.cursor()
cursor.execute(sql,keys) # 执行查询SQL语句
result = cursor.fetchall() # 记录查询结果
cursor.close()
db.close()
return result
我们定义query2()函数用于数据库数据不带参数的模糊查询:
# 不带参数的模糊查询
def query2(sql):
db=open()
cursor = db.cursor()
cursor.execute(sql) # 执行查询SQL语句
result = cursor.fetchall() # 记录查询结果
cursor.close()
db.close()
return result
用户信息界面UI设计图如下:
此处我们要注意的一点:
Qt Designer设计窗口.ui转为.py时,默认继承的是object类,为了各个窗口可以相互调用,我们需要将继承类手动改为QMainWindow,并添加__init__方法。
class Ui_MainWindow(QMainWindow):
# 构造方法
def __init__(self):
super(Ui_MainWindow, self).__init__()
self.setWindowFlags(QtCore.Qt.MSWindowsFixedSizeDialogHint) # 只显示最小化和关闭按钮
self.setupUi(self) # 初始化窗体设置
我们定义query()函数和getItem()来实现。
# 查询用户信息,并显示在表格中
def query(self):
self.tbUser.setRowCount(0) # 清空表格中的所有行
result = service.query("select * from tb_user") # 调用服务类中的公共方法执行查询语句
row = len(result) # 取得记录个数,用于设置表格的行数
self.tbUser.setRowCount(row) # 设置表格行数
self.tbUser.setColumnCount(2) # 设置表格列数
# 设置表格的标题名称
self.tbUser.setHorizontalHeaderLabels(['用户名称', '用户密码'])
for i in range(row): # 遍历行
for j in range(self.tbUser.columnCount()): # 遍历列
data = QTableWidgetItem(str(result[i][j])) # 转换后可插入表格
self.tbUser.setItem(i, j, data) # 设置每个单元格的数据
# 获取选中的表格内容
def getItem(self, item):
if item.column() == 0: # 如果单击的是第一列
self.select = item.text() # 获取单击的单元格文本
self.editName.setText(self.select) # 显示在用户名称文本框中
我们通过自定义add()、 edit()、delete()函数来实现。
# 添加用户信息
def add(self):
userName = self.editName.text() # 记录输入的用户名
userPwd = self.editPwd.text() # 记录输入的用户密码
if userName != "" and userPwd != "": # 判断用户名和密码不为空
# 执行添加语句
result=service.exec("insert into tb_user(userName,userPwd) values (%s,%s)",(userName,userPwd))
if result>0: # 如果结果大于0,说明添加成功
self.query() # 在表格中显示最新数据
QMessageBox.information(None, '提示', '信息添加成功!', QMessageBox.Ok)
else:
QMessageBox.warning(None, '警告', '请输入数据后,再执行相关操作!', QMessageBox.Ok)
# 修改用户信息
def edit(self):
try:
if self.select!="": # 判断是否选择了要修改的数据
userPwd = self.editPwd.text() # 记录修改的用户密码
if userPwd != "": # 判断密码不为空
# 执行修改操作
result=service.exec("update tb_user set userPwd= %s where userName=%s",(userPwd,self.select))
if result>0: # 如果结果大于0,说明修改成功
self.query() # 在表格中显示最新数据
QMessageBox.information(None, '提示', '信息修改成功!', QMessageBox.Ok)
except:
QMessageBox.warning(None, '警告', '请先选择要修改的数据!', QMessageBox.Ok)
# 删除用户信息
def delete(self):
try:
if self.select!="": # 判断是否选择了要删除的数据
# 执行删除操作
result=service.exec("delete from tb_user where userName= %s",(self.select,))
if result>0: # 如果结果大于0,说明删除成功
self.query() # 在表格中显示最新数据
QMessageBox.information(None, '提示', '信息删除成功!', QMessageBox.Ok)
except:
QMessageBox.warning(None, '警告', '请先选择要删除的数据!', QMessageBox.Ok)
登录界面UI设计图如下:
将.ui转为.py文件后,我们还要进行以下功能的添加:
我们定义openMain()函数用于打开大厅界面:
# 打开主窗体
def openMain(self):
service.userName=self.editName.text()
self.userPwd=self.editPwd.text() # 记录用户密码
if service.userName != "" and self.userPwd != "": # 判断用户名、密码不为空,根据用户名和密码查询
result=service.query("select * from tb_user where userName = %s and userPwd = %s",service.userName,self.userPwd)
if len(result)>0: # 如果查询结果>0,说明存在该用户可以登录
self.m = main.Ui_MainWindow()
self.m.show()
MainWindow.hide()
else:
self.editName.setText("")
self.editPwd.setText("")
QMessageBox.warning(None, '警告', '请输入正确的用户名和密码!', QMessageBox.Ok)
else:
QMessageBox.warning(None, '警告', '请输入用户名和密码!', QMessageBox.Ok)
通过按键的clicked信号定义即可
# 输入密码后回车执行登录
self.editPwd.editingFinished.connect(self.openMain)
# 单击“登录”按钮执行登录
self.btnLogin.clicked.connect(self.openMain)
# 关闭登录界面
self.btnExit.clicked.connect(MainWindow.close)
添加’main’方法使程序可运行:
# 窗口函数
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow() # 创建窗体对象
ui = Ui_MainWindow() # 创建PyQt5设计的窗体对象
ui.setupUi(MainWindow) # 调用PyQt5窗体的方法对窗体对象进行初始化设置
MainWindow.show() # 显示窗体
sys.exit(app.exec_()) # 程序关闭时退出进程
将.ui转为.py文件后,我们还要进行以下功能的添加:
此处我们还是注意的一点:
为了各个窗口可以相互调用,我们需要将继承类手动改为QMainWindow,并添加__init__方法。
class Ui_MainWindow(QMainWindow):
# 构造方法
def __init__(self):
super(Ui_MainWindow, self).__init__()
self.setWindowFlags(QtCore.Qt.MSWindowsFixedSizeDialogHint) # 只显示最小化和关闭按钮
self.setupUi(self) # 初始化窗体设置
在大厅界面,选择相应菜单下的菜单项,可以打开对应的模块。对此我们定义一个openSet()函数来实现此功能。
# 菜单对应槽函数
def openSet(self,m):
if m.text()=="年级设置":
self.m = grade.Ui_MainWindow() # 创建年级设置窗体对象
self.m.show() # 显示窗体
elif m.text()=="班级设置":
self.m = classes.Ui_MainWindow() # 创建班级设置窗体对象
self.m.show() # 显示窗体
def openBase(self,m):
if m.text()=="学生管理":
self.m = student.Ui_MainWindow() # 创建学生管理窗体对象
self.m.show() # 显示窗体
def openQuery(self,m):
if m.text()=="学生信息查询":
self.m = studentinfo.Ui_MainWindow() # 创建学生信息查询窗体对象
self.m.show() # 显示窗体
def openSys(self,m):
if m.text()=="用户维护":
self.m = user.Ui_MainWindow() # 创建用户维护窗体对象
self.m.show() # 显示窗体
还要用trigger信号去关联自定义的openSet()函数。
# 为基础设置菜单中的QAction绑定triggered信号
self.menu.triggered[QtWidgets.QAction].connect(self.openSet)
# 为基本信息管理菜单中的QAction绑定triggered信号
self.menu_2.triggered[QtWidgets.QAction].connect(self.openBase)
# 为系统查询菜单中的QAction绑定triggered信号
self.menu_3.triggered[QtWidgets.QAction].connect(self.openQuery)
# 为系统管理菜单中的QAction绑定triggered信号
self.menu_4.triggered[QtWidgets.QAction].connect(self.openSys)
显示登录的用户信息通过调用前面编写的数据库模块里的userName即可获取,同时我们附加一个可以获取当前时间的功能。
datetime = QtCore.QDateTime.currentDateTime() # 获取当前日期时间
time = datetime.toString("yyyy-MM-dd HH:mm:ss") # 对日期时间进行格式化
# 状态栏中显示登录用户信息、登录时间
self.statusbar.showMessage("当前登录用户:" + service.userName + " | 登录时间:" + time + ", 0)
将.ui转为.py文件后,我们还要进行以下功能的添加:
此处我们任然需要将继承类手动改为QMainWindow,并添加__init__方法。
class Ui_MainWindow(QMainWindow):
# 构造方法
def __init__(self):
super(Ui_MainWindow, self).__init__()
self.setWindowFlags(QtCore.Qt.MSWindowsFixedSizeDialogHint) # 只显示最小化和关闭按钮
self.setupUi(self) # 初始化窗体设置
查询功能是通过自定义的query ()函数实现的,分为精确查询和模糊查询。
精确查询:输入学生所有信息
模糊查询:输入学生部分信息,如编号、姓名
# 查询学生信息,并显示
def query(self):
self.tbStudent.setRowCount(0)
# 获取所有学生的信息
if self.editKey.text() == "":
result = service.query(
"select stuID,stuName,CONCAT(gradeName,className),sex,age,address,phone from v_studentinfo")
else:
key = self.editKey.text() # 记录关键字
# 编号查询
if self.cboxCondition.currentText() == "学生编号":
sql="select stuID,stuName,CONCAT(gradeName,className),sex,age,address,phone from v_studentinfo where stuID like '%" + key + "%'"
result = service.query2(sql)
# 姓名查询
elif self.cboxCondition.currentText() == "学生姓名":
sql = "select stuID,stuName,CONCAT(gradeName,className),sex,age,address,phone from v_studentinfo where stuName like '%" + key + "%'"
result = service.query2(sql)
row = len(result) # 取得记录个数,用于设置表格的行数
self.tbStudent.setRowCount(row) # 设置表格行数
self.tbStudent.setColumnCount(7) # 设置表格列数
# 设置表格的标题名称
self.tbStudent.setHorizontalHeaderLabels(['学生编号', '学生姓名', '班级', '性别', '年龄', '家庭地址', '联系电话'])
for i in range(row): # 遍历行
for j in range(self.tbStudent.columnCount()): # 遍历列
data = QTableWidgetItem(str(result[i][j])) # 转换后可插入表格
self.tbStudent.setItem(i, j, data) # 设置每个单元格的数据
班级设置UI设计图如下:
信息增、删、改设置UI设计图如下:
将.ui转为.py文件后,我们还要进行以下功能的添加:
我们要显示学生相关信息,可以通过前面的模块来获取年级和班级信息。
显示年级信息:
# 查询年级信息,并显示在表格中
def query(self):
self.tbGrade.setRowCount(0) # 清空表格中的所有行
result = service.query("select * from tb_grade") # 调用服务类中的公共方法执行查询语句
row = len(result) # 取得记录个数,用于设置表格的行数
self.tbGrade.setRowCount(row) # 设置表格行数
self.tbGrade.setColumnCount(2) # 设置表格列数
# 设置表格的标题名称
self.tbGrade.setHorizontalHeaderLabels(['年级编号', '年级名称'])
for i in range(row): # 遍历行
for j in range(self.tbGrade.columnCount()): # 遍历列
data = QTableWidgetItem(str(result[i][j])) # 转换后可插入表格
self.tbGrade.setItem(i, j, data) # 设置每个单元格的数据
# 获取选中的表格内容
def getItem(self, item):
if item.column() == 0: # 如果单击的是第一列
self.select = item.text() # 获取单击的单元格文本
self.editID.setText(self.select) # 显示在文本框中
def getName(self,name):
result = service.query("select * from tb_grade where gradeName = %s", name)
return len(result)
显示班级信息:
# 获取所有年级,显示在下拉列表中
def bindGrade(self):
result=service.query("select gradeName from tb_grade") # 从年级表中查询数据
for i in result: # 遍历查询结果
self.cboxGrade.addItem(i[0]) # 在下拉列表中显示年级
# 获取选中的表格内容
def getItem(self, item):
if item.column() == 0: # 如果单击的是第一列
self.select = item.text() # 获取单击的单元格文本
self.editID.setText(self.select) # 显示在班级编号文本框中
# 判断要添加的记录是否存在
def getName(self, cid,name):
# 根据年级编号和班级名查询数据
result = service.query("select * from tb_class where gradeID =%s and className = %s", cid,name)
return len(result) # 返回查询结果的记录
年级设置的增、删、改:
# 添加年级信息
def add(self):
gradeID = self.editID.text() # 记录输入的年级编号
gradeName = self.editName.text() # 记录输入的年级名称
if gradeID != "" and gradeName != "": # 判断年级编号和年级名称不为空
if self.getName(gradeName)>0:
self.editName.setText("")
QMessageBox.information(None, '提示', '您要添加的年级已经存在,请重新输入!', QMessageBox.Ok)
else:
# 执行添加语句
result = service.exec("insert into tb_grade(gradeID,gradeName) values (%s,%s)", (gradeID, gradeName))
if result > 0: # 如果结果大于0,说明添加成功
self.query() # 在表格中显示最新数据
QMessageBox.information(None, '提示', '信息添加成功!', QMessageBox.Ok)
else:
QMessageBox.warning(None, '警告', '请输入数据后,再执行相关操作!', QMessageBox.Ok)
# 修改年级信息
def edit(self):
try:
if self.select != "": # 判断是否选择了要修改的数据
gradeName = self.editName.text() # 记录修改的年级名称
if gradeName != "": # 判断年级名称不为空
if self.getName(gradeName) > 0:
self.editName.setText("")
QMessageBox.information(None, '提示', '您要修改的年级已经存在,请重新输入!', QMessageBox.Ok)
else:
# 执行修改操作
result = service.exec("update tb_grade set gradeName= %s where gradeID=%s", (gradeName, self.select))
if result > 0: # 如果结果大于0,说明修改成功
self.query() # 在表格中显示最新数据
QMessageBox.information(None, '提示', '信息修改成功!', QMessageBox.Ok)
except:
QMessageBox.warning(None, '警告', '请先选择要修改的数据!', QMessageBox.Ok)
# 删除年级信息
def delete(self):
try:
if self.select != "": # 判断是否选择了要删除的数据
# 执行删除年级操作
result = service.exec("delete from tb_grade where gradeID= %s", (self.select,))
if result > 0: # 如果结果大于0,说明删除成功
self.query() # 在表格中显示最新数据
# 删除年级下的所有班级
result = service.exec("delete from tb_class where gradeID= %s", (self.select,))
if result > 0: # 如果结果大于0,说明删除成功
self.query() # 在表格中显示最新数据
QMessageBox.information(None, '提示', '信息删除成功!', QMessageBox.Ok)
except:
QMessageBox.warning(None, '警告', '请先选择要删除的数据!', QMessageBox.Ok)
班级设置的增、删、改:
# 添加班级信息
def add(self):
classID = self.editID.text() # 记录输入的班级编号
className = self.editName.text() # 记录输入的班级名称
if self.cboxGrade.currentText() !="": # 如果选择了年级
# 获取年级对应的ID
result=service.query("select gradeID from tb_grade where gradeName=%s",self.cboxGrade.currentText())
if len(result)>0: # 如果结果大于0
gradeID=result[0] # 记录选择的年级对应的ID
if classID != "" and className != "": # 判断班级编号和班级名称不为空
if self.getName(gradeID,className) > 0: # 判断已经存在该记录
self.editName.setText("") # 清空班级文本框
QMessageBox.information(None, '提示', '您要添加的班级已经存在,请重新输入!', QMessageBox.Ok)
else:
# 执行添加语句
result = service.exec("insert into tb_class(classID,gradeID,className) values (%s,%s,%s)", (classID, gradeID,className))
if result > 0: # 如果结果大于0,说明添加成功
self.query() # 在表格中显示最新数据
QMessageBox.information(None, '提示', '信息添加成功!', QMessageBox.Ok)
else:
QMessageBox.warning(None, '警告', '请输入数据后,再执行相关操作!', QMessageBox.Ok)
else:
QMessageBox.warning(None, '警告', '请先添加年级!', QMessageBox.Ok)
# 修改班级信息
def edit(self):
try:
if self.select != "": # 判断是否选择了要修改的数据
className = self.editName.text() # 记录修改的班级名称
if self.cboxGrade.currentText() != "": # 如果选择了年级
# 获取年级对应的ID
result = service.query("select gradeID from tb_grade where gradeName=%s", self.cboxGrade.currentText())
if len(result) > 0: # 如果结果大于0
gradeID = result[0] # 记录选择的年级对应的ID
if className != "": # 判断班级名称不为空
if self.getName(gradeID,className) > 0: # 判断已经存在该记录
self.editName.setText("") # 清空班级文本框
QMessageBox.information(None, '提示', '您要修改的班级已经存在,请重新输入!', QMessageBox.Ok)
else:
# 执行修改操作
result = service.exec("update tb_class set gradeID=%s , className= %s where classID=%s",
(gradeID,className, self.select))
if result > 0: # 如果结果大于0,说明修改成功
self.query() # 在表格中显示最新数据
QMessageBox.information(None, '提示', '信息修改成功!', QMessageBox.Ok)
except:
QMessageBox.warning(None, '警告', '请先选择要修改的数据!', QMessageBox.Ok)
# 删除班级信息
def delete(self):
try:
if self.select != "": # 判断是否选择了要删除的数据
# 执行删除操作
result = service.exec("delete from tb_class where classID= %s", (self.select,))
if result > 0: # 如果结果大于0,说明删除成功
self.query() # 在表格中显示最新数据
QMessageBox.information(None, '提示', '信息删除成功!', QMessageBox.Ok)
except:
QMessageBox.warning(None, '警告', '请先选择要删除的数据!', QMessageBox.Ok)
同理,还是自定义一个query()函数。
# 查询学生信息,并显示在表格中
def query(self):
self.tbStudent.setRowCount(0) # 清空表格中的所有行
gname = self.cboxGrade.currentText() # 记录选择的年级
cname = self.cboxClass.currentText() # 记录选择的班级
# 获取所有学生信息
if gname == "所有":
result = service.query("select stuID,stuName,CONCAT(gradeName,className),sex,age,address,phone from v_studentinfo")
# 获取指定年级学生信息
elif gname != "所有" and cname == "所有":
result = service.query(
"select stuID,stuName,CONCAT(gradeName,className),sex,age,address,phone from v_studentinfo where gradeName=%s", gname)
# 获取指定年级指定班的学生信息
elif gname != "所有" and cname != "所有":
result = service.query(
"select stuID,stuName,CONCAT(gradeName,className),sex,age,address,phone from v_studentinfo where gradeName=%s and className=%s",
gname, cname)
row = len(result) # 取得记录个数,用于设置表格的行数
self.tbStudent.setRowCount(row) # 设置表格行数
self.tbStudent.setColumnCount(7) # 设置表格列数
# 设置表格的标题名称
self.tbStudent.setHorizontalHeaderLabels(['学生编号', '学生姓名', '班级', '性别', '年龄', '家庭地址', '联系电话'])
for i in range(row): # 遍历行
for j in range(self.tbStudent.columnCount()): # 遍历列
data = QTableWidgetItem(str(result[i][j])) # 转换后可插入表格
self.tbStudent.setItem(i, j, data) # 设置每个单元格的数据
自定义add()函数。
# 添加学生信息
def add(self):
stuID = self.editID.text() # 学生编号
stuName = self.editName.text() # 学生姓名
age = self.editAge.text() # 年龄
sex = self.cboxSex.currentText() # 性别
phone = self.editPhone.text() # 电话
address = self.editAddress.text() # 地址
if self.cboxGrade.currentText() != "" and self.cboxGrade.currentText() != "所有": # 如果选择了年级
# 获取年级对应的ID
result = service.query("select gradeID from tb_grade where gradeName=%s", self.cboxGrade.currentText())
if len(result) > 0: # 如果结果大于0
gradeID = result[0] # 记录选择的年级对应的ID
if self.cboxClass.currentText() != "" and self.cboxClass.currentText() != "所有": # 如果选择了班级
# 获取班级对应的ID
result = service.query("select classID from tb_class where gradeID=%s and className=%s", gradeID,
self.cboxClass.currentText())
if len(result) > 0: # 如果结果大于0
classID = result[0] # 记录选择的班级对应的ID
if stuID != "" and stuName != "": # 判学生编号和学生姓名不为空
if self.getName(stuID) > 0: # 判断已经存在该记录
self.editID.setText("") # 清空学生编号文本框
QMessageBox.information(None, '提示', '您要添加的学生编号已经存在,请重新输入!', QMessageBox.Ok)
else:
# 执行添加语句
result = service.exec(
"insert into tb_student(stuID,stuName,classID,gradeID,age,sex,phone,address) values (%s,%s,%s,%s,%s,%s,%s,%s)",
(stuID, stuName, classID, gradeID, age, sex, phone, address))
if result > 0: # 如果结果大于0,说明添加成功
self.query() # 在表格中显示最新数据
QMessageBox.information(None, '提示', '信息添加成功!', QMessageBox.Ok)
else:
QMessageBox.warning(None, '警告', '请输入数据后,再执行相关操作!', QMessageBox.Ok)
else:
QMessageBox.warning(None, '警告', '请先添加年级!', QMessageBox.Ok)
自定义delet()函数。
# 删除学生信息
def delete(self):
try:
if self.select != "": # 判断是否选择要删除的数据
# 执行删除操作
result = service.exec("delete from tb_student where stuID= %s", (self.select,))
if result > 0: # 如果结果大于0,说明删除成功
self.query() # 在表格中显示最新数据
QMessageBox.information(None, '提示', '信息删除成功!', QMessageBox.Ok)
except:
QMessageBox.warning(None, '警告', '请先选择要删除的数据!', QMessageBox.Ok)
自定义edit()函数。
# 修改学生信息
def edit(self):
try:
if self.select != "": # 判断是否选择要修改的数据
stuID = self.select # 要修改的学生编号
age = self.editAge.text() # 年龄
sex = self.cboxSex.currentText() # 性别
phone = self.editPhone.text() # 电话
address = self.editAddress.text() # 地址
# 执行修改操作
result = service.exec("update tb_student set age=%s ,sex= %s,phone= %s,address= %s where stuID=%s",
(age, sex, phone, address, stuID))
if result > 0: # 如果结果大于0,说明修改成功
self.query() # 在表格中显示最新数据
QMessageBox.information(None, '提示', '信息修改成功!', QMessageBox.Ok)
except:
QMessageBox.warning(None, '警告', '请先选择要修改的数据!', QMessageBox.Ok)
这样一个简单的学生信息管理系统就搭建完成啦。
其中有很多都是重复增、删、改、查的功能,反复的造轮子,显得有点枯燥,其实基于这个MS,我们可以结合OpenCV的人脸识别的相关知识,将这个学生信息管理变更为公司企业的打卡考勤系统,当然这就是后话了。
有兴趣的小伙伴可以动手试一试。
此外,如果有什么不清楚的地方或者疑问欢迎加博主vx讨论。
喜欢的话记得三连哦,欢迎评论。