Python设计图书馆管理系统技术文档(三)
5.设计和代码
5.1 用户登录与注册(MainWindow.py)
- 主入口程序
import sys from PyQt5.QtWidgets import * from PyQt5.QtGui import QIcon import qdarkstyle from SignIn import SignInWidget from SignUp import SignUpWidget import sip from AdminHome import AdminHome from StudentHome import StudentHome from changePasswordDialog import changePasswordDialog # 主窗口设计 class Main(QMainWindow): def __init__(self, parent=None): """1.初始化主页信息 """ super(Main, self).__init__(parent) # 水平布局 self.layout = QHBoxLayout() # 定义登录窗体控件 self.widget = SignInWidget() # 定义尺寸 self.resize(900, 600) # 定义标题 self.setWindowTitle("欢迎登陆图书馆管理系统") # 居中显示 self.setCentralWidget(self.widget) # 定义菜单栏 bar = self.menuBar() self.Menu = bar.addMenu("菜单栏") # 定义菜单栏时的各动作部件,并添加到菜单栏 self.signUpAction = QAction("注册", self) self.changePasswordAction = QAction("修改密码", self) self.signInAction = QAction("登录", self) self.quitSignInAction = QAction("退出登录", self) self.quitAction = QAction("退出", self) self.Menu.addAction(self.signUpAction) self.Menu.addAction(self.changePasswordAction) self.Menu.addAction(self.signInAction) self.Menu.addAction(self.quitSignInAction) self.Menu.addAction(self.quitAction) # 设置菜单栏上各部件触发的有效性 self.signUpAction.setEnabled(True) self.changePasswordAction.setEnabled(True) self.signInAction.setEnabled(False) self.quitSignInAction.setEnabled(False) # 连续三个信号槽,分别连接定位到AdminHome.py(管理员主主页)、StudentHome.py(用户主页)、 # menuTriggered方法(菜单弹出动作) self.widget.is_admin_signal.connect(self.adminSignIn) self.widget.is_student_signal[str].connect(self.studentSignIn) self.Menu.triggered[QAction].connect(self.menuTriggered) # 管理员登录页面的弹出和展示效果 def adminSignIn(self): sip.delete(self.widget) self.widget = AdminHome() self.setCentralWidget(self.widget) self.changePasswordAction.setEnabled(False) self.signUpAction.setEnabled(True) self.signInAction.setEnabled(False) self.quitSignInAction.setEnabled(True) # 用户(学生)登录页面的弹出和展示效果 def studentSignIn(self, studentId): sip.delete(self.widget) self.widget = StudentHome(studentId) self.setCentralWidget(self.widget) self.changePasswordAction.setEnabled(False) self.signUpAction.setEnabled(True) self.signInAction.setEnabled(False) self.quitSignInAction.setEnabled(True) # 主窗口菜单栏选项的弹出和展示效果 def menuTriggered(self, q): if q.text() == "修改密码": changePsdDialog = changePasswordDialog(self) changePsdDialog.show() changePsdDialog.exec_() if q.text() == "注册": sip.delete(self.widget) self.widget = SignUpWidget() self.setCentralWidget(self.widget) self.widget.student_signup_signal[str].connect(self.studentSignIn) self.signUpAction.setEnabled(False) self.changePasswordAction.setEnabled(True) self.signInAction.setEnabled(True) self.quitSignInAction.setEnabled(False) if q.text() == "退出登录" or q.text() == "登录": sip.delete(self.widget) self.widget = SignInWidget() self.setCentralWidget(self.widget) self.widget.is_admin_signal.connect(self.adminSignIn) self.widget.is_student_signal[str].connect(self.studentSignIn) self.signUpAction.setEnabled(True) self.changePasswordAction.setEnabled(True) self.signInAction.setEnabled(False) self.quitSignInAction.setEnabled(False) if q.text() == "退出": q_app = QApplication.instance() q_app.quit() return # 主main入口 if __name__ == "__main__": app = QApplication(sys.argv) app.setWindowIcon(QIcon("./images/MainWindow_1.png")) app.setStyleSheet(qdarkstyle.load_stylesheet_pyqt5()) mainWindow = Main() mainWindow.show() sys.exit(app.exec_())
- 代码分析
- 主窗口是一个程序开始运行,首先要面对用户的窗口。这个代码已经作了整体的注释,下面再逐个分析。
- (1)初始化窗口。布局、摆放各种控件。窗体及控件自底层往上层设计,代码自上而下书写。分别是水平布局、总窗体、总窗体尺寸、总窗体标题并居中。菜单栏,菜单栏自上而下的注册、修改密码、退出登录、登录、退出等按键。其中只有注册、修改密码、登录三个是窗体,而退出登录和登录是一个窗体,退出是主程序全部关闭退出的命令。
- 初始化模块最后有三个信号槽,分别重定向到登录管理页面、用户页面、菜单栏三个子程序,代码如下:
# 连续三个信号槽,分别连接定位到AdminHome.py(管理员主主页)、StudentHome.py(用户主页)、 # menuTriggered方法(菜单弹出动作) self.widget.is_admin_signal.connect(self.adminSignIn) self.widget.is_student_signal[str].connect(self.studentSignIn) self.Menu.triggered[QAction].connect(self.menuTriggered)
- 初始化模块最后有三个信号槽,分别重定向到登录管理页面、用户页面、菜单栏三个子程序,代码如下:
- (2)接下来是管理登录( adminSignIn)。在打开管理页面之后,先删除主窗口界面,接着页面居中,密码修改和登录按钮为不可用。
- (3)用户登录页面(studentSignIn)。也是先删除主窗口,接着页面居中,密码修改和登录按钮为不可用。运用效果如图。
- (4)主窗口菜单栏选项(menuTriggered)。
- 这里面设定四个if语句,其中“退出登录”和“登录”其实是相同代码,我给原代码作了合并处理。
- 这里第一个if语句是“修改密码”,调用了changePasswordDialog.py里面的changePasswordDialog从而轮到修改密码的对话框控件。
- 第二个if语句要“注册”,定义了一个注册控件SignUpWidget的实例,然后用了信号槽方法,使得注册完成后,直接跳转到用户界面。而在别的地方注册完成后,还停留在注册对话框。是不一样的。
self.widget.student_signup_signal[str].connect(self.studentSignIn)
- 第三个if语句是“退出登录”和“登录”。用了or来区分用户分别点击两个对应按钮的反应,其实质都是跳转到登录页面SignIn.py。此处用了两个信号槽方法,用来分别从管理页面AdminHome.py或者用户页面StudentHome.py跳转到用户登录界面。
- 第四个if语句是“退出”。
- (5)主入口main程序。加了两句代码,一个是主窗口左上角的图标,另一个是调用系统内置函数,来风格化窗口。代码如下:
if __name__ == "__main__": app = QApplication(sys.argv) app.setWindowIcon(QIcon("./images/MainWindow_1.png")) app.setStyleSheet(qdarkstyle.load_stylesheet_pyqt5()) mainWindow = Main() mainWindow.show() sys.exit(app.exec_())
5.1.1 注册文件(SignUp.py)
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import qdarkstyle
from PyQt5.QtSql import *
import hashlib
from PyQt5.QtWidgets import QMessageBox
class SignUpWidget(QWidget):
student_signup_signal = pyqtSignal(str)
def __init__(self):
super().__init__()
self.resize(900, 600)
self.setWindowTitle("欢迎登陆图书馆管理系统")
self.setUpUI()
"""
总组件setUpUI---由以下三大块内容完成
"""
#
def setUpUI(self):
"""
一、垂直布局管理器,放置一个小组件signUpLabel(注册)属类QLabel
"""
# 1、普通标签signUpLabel(注册)
self.signUpLabel = QLabel("注 册")
self.signUpLabel.setAlignment(Qt.AlignCenter)
self.signUpLabel.setFixedHeight(100)
# 定义一个字体风格
font = QFont()
font.setPixelSize(36)
self.signUpLabel.setFont(font)
# 2.垂直布局管理器layout,一行一行来
# 类QVBowLayout(垂直布局管理器)
# 然后把第一个组件QLable”注册“放这里面
self.VBlayout = QVBoxLayout()
self.VBlayout.addWidget(self.signUpLabel, Qt.AlignHCenter)
self.setLayout(self.VBlayout)
"""
二、定义表单布局管理器formlayout,类QFormLayout(表单式布局器)
接下来,连续在这个布局控件里面放置4个QLable和4个QLineEdit和1个PushButton
"""
# 在这里定义一个字体属性,后面继续用
lineEditFont = QFont()
lineEditFont.setPixelSize(16)
# 设置表单布局,包括学号,姓名,密码,确认密码
self.formlayout = QFormLayout()
font.setPixelSize(18)
# Row1————学号
self.studentIdLabel = QLabel("学 号: ")
self.studentIdLabel.setFont(font)
self.studentIdLineEdit = QLineEdit()
self.studentIdLineEdit.setFixedWidth(160)
self.studentIdLineEdit.setFixedHeight(32)
self.studentIdLineEdit.setFont(lineEditFont)
self.studentIdLineEdit.setMaxLength(10)
# 一行摆放两列————学号和学号输入框
self.formlayout.addRow(self.studentIdLabel, self.studentIdLineEdit)
# Row2————姓名
self.studentNameLabel = QLabel("姓 名: ")
self.studentNameLabel.setFont(font)
self.studentNameLineEdit = QLineEdit()
self.studentNameLineEdit.setFixedHeight(32)
self.studentNameLineEdit.setFixedWidth(160)
self.studentNameLineEdit.setFont(lineEditFont)
self.studentNameLineEdit.setMaxLength(10)
# 一行摆放两列————姓名和姓名输入框
self.formlayout.addRow(self.studentNameLabel, self.studentNameLineEdit)
lineEditFont.setPixelSize(10)
# Row3————密码
self.passwordLabel = QLabel("密 码: ")
self.passwordLabel.setFont(font)
self.passwordLineEdit = QLineEdit()
self.passwordLineEdit.setFixedWidth(160)
self.passwordLineEdit.setFixedHeight(32)
self.passwordLineEdit.setFont(lineEditFont)
self.passwordLineEdit.setEchoMode(QLineEdit.Password)
self.passwordLineEdit.setMaxLength(16)
# 一行摆放两列————密码和密码输入框
self.formlayout.addRow(self.passwordLabel, self.passwordLineEdit)
# Row4————确认密码
self.passwordConfirmLabel = QLabel("确认密码: ")
self.passwordConfirmLabel.setFont(font)
self.passwordConfirmLineEdit = QLineEdit()
self.passwordConfirmLineEdit.setFixedWidth(160)
self.passwordConfirmLineEdit.setFixedHeight(32)
self.passwordConfirmLineEdit.setFont(lineEditFont)
self.passwordConfirmLineEdit.setEchoMode(QLineEdit.Password)
self.passwordConfirmLineEdit.setMaxLength(16)
# # 一行摆放两列————确认和确认输入框
self.formlayout.addRow(self.passwordConfirmLabel, self.passwordConfirmLineEdit)
# Row5————注册
self.signUpbutton = QPushButton("注 册")
self.signUpbutton.setFixedWidth(120)
self.signUpbutton.setFixedHeight(30)
self.signUpbutton.setFont(font)
self.formlayout.addRow("", self.signUpbutton)
"""
三、嵌套各种窗体控件和布局
1.定义一个放表单的窗体控件widget——widget_form,上面放置布局formlayout(包括里面5个小组件)
2.接下来对再把widget_form放入一个新定义的水平布局器HLayout里面,居中显示
3.最后再定义一个窗体控件widget——widget_layout,放到最初的垂直布局器VBlayout里。
这段代码非常绕,用工具Designer也无法实现层次关系,但是代码正常没有提示报错,逻辑关系如下:
从底层到顶层为:
VBlayout(垂直布局)——>widget_Hlayout(控件)——>HLayout(水平布局)——>widget_form(控件)
——>formlayout(表单布局)——>四对label和lineEdit,以及一个“空+QPushButton”
知识链接:
有些控件或者布局有addLayout和addWidget的函数,但是有些就没有,比如QWidget这个控件只有一个setLayout函数
但是不关控件或者布局,setlayout只能set一次
addLayout和addWidget是用来管理布局的,而setLayout是将已设置好的布局应用到控件中去
"""
widget_form = QWidget()
widget_form.setFixedWidth(300)
widget_form.setFixedHeight(250)
widget_form.setLayout(self.formlayout)
self.HLayout = QHBoxLayout()
self.HLayout.addWidget(widget_form, Qt.AlignCenter)
widget_Hlayout = QWidget()
widget_Hlayout.setLayout(self.HLayout)
self.VBlayout.addWidget(widget_Hlayout, Qt.AlignHCenter)
# 设置验证
reg1 = QRegExp("PB[0~9]{8}")
pValidator = QRegExpValidator(self)
pValidator.setRegExp(reg1)
self.studentIdLineEdit.setValidator(pValidator)
reg2 = QRegExp("[a-zA-z0-9]+$")
pValidator.setRegExp(reg2)
self.passwordLineEdit.setValidator(pValidator)
self.passwordConfirmLineEdit.setValidator(pValidator)
# 连续5个信号槽,分别是注册、学号、姓名、密码、密码确认
self.signUpbutton.clicked.connect(self.SignUp)
self.studentIdLineEdit.returnPressed.connect(self.SignUp)
self.studentNameLineEdit.returnPressed.connect(self.SignUp)
self.passwordLineEdit.returnPressed.connect(self.SignUp)
self.passwordConfirmLineEdit.returnPressed.connect(self.SignUp)
"""
自定义注册函数(槽函数)
"""
def SignUp(self):
studentId = self.studentIdLineEdit.text()
studentName = self.studentNameLineEdit.text()
password = self.passwordLineEdit.text()
confirmPassword = self.passwordConfirmLineEdit.text()
if studentId == "" or studentName == "" or password == "" or confirmPassword == "":
print(QMessageBox.warning(self, "警告", "输入的内容不能为空,请重新输入", QMessageBox.Yes, QMessageBox.Yes))
return
else:
# 需要处理逻辑,1.账号已存在;2.密码不匹配;3.插入user表
db = QSqlDatabase.addDatabase("QSQLITE")
db.setDatabaseName('./db/LibraryManagement.db')
db.open()
query = QSqlQuery()
if confirmPassword != password:
# 此处和下面一样,参数可以简化不写,但必须两个字符串对应
print(QMessageBox.warning(self, "嘛意思", "你做着梦录入的吗,两次不一样"))
return
elif confirmPassword == password:
# md5编码
hl = hashlib.md5()
hl.update(password.encode(encoding='utf-8'))
md5password = hl.hexdigest()
sql = "SELECT * FROM user WHERE StudentId='%s'" % (studentId)
query.exec_(sql)
if query.next():
print(
QMessageBox.warning(self, "傻白甜", "这名字有了,请重新输入", QMessageBox.Yes, QMessageBox.Yes))
return
else:
sql = "INSERT INTO user VALUES ('%s','%s','%s',0,0,0)" % (
studentId, studentName, md5password)
db.exec_(sql)
db.commit()
print(QMessageBox.information(self, "不错", "您已成功注册账号!", QMessageBox.Yes, QMessageBox.Yes))
self.student_signup_signal.emit(studentId)
db.close()
return
if __name__ == "__main__":
app = QApplication(sys.argv)
app.setWindowIcon(QIcon("./images/MainWindow_1.png"))
app.setStyleSheet(qdarkstyle.load_stylesheet_pyqt5())
mainWindow = SignUpWidget()
mainWindow.show()
sys.exit(app.exec_())
-
代码分析
- 重要的有布局、信号槽、数据库sql语句三部分。
- 这个页面的代码特别是布局,有点绕。图形展示界面setUpUI里面的布局是这样的思路:
- (1)先设定好四对标签QLabel和单行文本框QLineEdit(包括学号、姓名、密码、确认密码),还有一个按钮QPushButton(注册)。
- (2)把上面一堆小控件放到表单布局formlayout之后,一系列麻烦的来了。源代码控件变量命名极不规范,为便于理解代码,我都规范处理了,最后布局和图层我还是说不明白,作图如下:
-
上面是层层布局的样式,和上面逻辑图对应,由下到上对应由外到内,弄清垂直布局和水平布局,widget窗体控件是为了约束这两个布局的,慢慢理解。
- 信号槽这里,一共定义了5个,一共槽函数是SignUp(),信号两种内置函数clicked(点击)和returnPressed(返回信号:回车或者离开焦点触发),对应代码如下:
# 连续5个信号槽,分别是注册、学号、姓名、密码、密码确认 self.signUpbutton.clicked.connect(self.SignUp) self.studentIdLineEdit.returnPressed.connect(self.SignUp) self.studentNameLineEdit.returnPressed.connect(self.SignUp) self.passwordLineEdit.returnPressed.connect(self.SignUp) self.passwordConfirmLineEdit.returnPressed.connect(self.SignUp)
-
最后一个难点:数据库接口判断。
-
这部分在SignIn()槽函数中,用到了if控制语句。这里面的判断有三种:输入为空、密码不一致、用户重复。也来图吧,说不明白。
-
分析上面的if语句中的逻辑关系,首先里面用到了QMessageBox.warning方法,会弹出消息框。“信息录入”节点之后,而且密码录入两遍一致,则需要调用数据库sql语法的slecet查询语句,如果查询到的记录中,和当前录入的信息一致,则提示当前录入的用户已经注册,需要重新换ID注册。如果查询到数据库中没有用户重复,则调用数据库sql语法的insert插入语句,在表中插入一条用户信息,最后提示注册成功,并且运行(exec)、提交(commit),最最后再用emit传递一下信号与槽函数。
-
5.1.2 登录文件(SignIn.py)
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import qdarkstyle
import hashlib
from PyQt5.QtSql import *
from PyQt5.QtCore import Qt
class SignInWidget(QWidget):
"""
为了给登录者进行区分,定义了两个信号
管理跳转到后台管理页面
学生跳转到图书馆界面
(信号槽函数)
"""
is_admin_signal = pyqtSignal()
is_student_signal = pyqtSignal(str)
def __init__(self):
super(SignInWidget, self).__init__()
self.resize(900, 600)
self.setWindowTitle("欢迎使用图书馆管理系统")
self.setUpUI()
def setUpUI(self):
"""
1.总的布局最底层是垂直布局Vlayout
2.再设定两个水平布局,安排登录窗体大标题和登录小控件的布局Hlayout_title、Hlayout_singIn
3.再定义表单布局formlayout_signIn,放置上上面小控件的布局
4.后面连续设定大标题的各级父控件和注册表单布局的父控件
"""
self.Vlayout = QVBoxLayout(self)
self.Hlayout_title = QHBoxLayout()
self.Hlayout_singIn = QHBoxLayout()
self.formlayout_signIn = QFormLayout()
# 学号的标签和输入框
self.label_stuID = QLabel("学号: ")
labelFont = QFont()
labelFont.setPixelSize(18)
lineEditFont = QFont()
lineEditFont.setPixelSize(16)
self.label_stuID.setFont(labelFont)
self.lineEdit_stuID = QLineEdit()
self.lineEdit_stuID.setFixedWidth(180)
self.lineEdit_stuID.setFixedHeight(32)
self.lineEdit_stuID.setFont(lineEditFont)
self.lineEdit_stuID.setMaxLength(10)
# 标签和输入框通过表单类添加到一行
self.formlayout_signIn.addRow(self.label_stuID, self.lineEdit_stuID)
# 密码和密码输入框
self.label_pwd = QLabel("密码: ")
self.label_pwd.setFont(labelFont)
self.lineEdit_pwd = QLineEdit()
self.lineEdit_pwd.setFixedWidth(180)
self.lineEdit_pwd.setFixedHeight(32)
self.lineEdit_pwd.setMaxLength(16)
# 设置验证
# QRegExp 验证用户输入的数据是否有效,
# 或者是查找并修改文本,或者是提供指定的数据
reg1 = QRegExp("PB[0~9]{8}")
pValidator = QRegExpValidator(self)
pValidator.setRegExp(reg1)
self.lineEdit_stuID.setValidator(pValidator)
reg2 = QRegExp("[a-zA-Z0-9]+$")
pValidator.setRegExp(reg2)
self.lineEdit_pwd.setValidator(pValidator)
# 设置密码不可见
passwordFont = QFont()
passwordFont.setPixelSize(10)
self.lineEdit_pwd.setFont(passwordFont)
self.lineEdit_pwd.setEchoMode(QLineEdit.Password)
self.formlayout_signIn.addRow(self.label_pwd, self.lineEdit_pwd)
self.signIn = QPushButton("登 录")
self.signIn.setFixedWidth(80)
self.signIn.setFixedHeight(30)
self.signIn.setFont(labelFont)
self.formlayout_signIn.addRow("", self.signIn)
# 添加标题界面
self.label_title = QLabel("欢迎使用享学图书管理系统")
fontlabel = QFont()
fontlabel.setPixelSize(30)
self.label_title.setFixedWidth(390)
self.lineEdit_pwd.setFont(fontlabel)
self.Hlayout_title.addWidget(self.label_title, Qt.AlignCenter)
# 整体的布局
# QWidget 窗口控制一个程序拥有一个窗口或者多个窗口
# 每个窗口都有多个控件
# 所有的窗口和控件都是直接或者间接继承自QWidget
self.widget_title = QWidget()
self.widget_title.setLayout(self.Hlayout_title)
self.widget_signIn = QWidget()
self.widget_signIn.setFixedWidth(300)
self.widget_signIn.setFixedHeight(150)
self.widget_signIn.setLayout(self.formlayout_signIn)
self.Hlayout_singIn.addWidget(self.widget_signIn, Qt.AlignCenter)
self.widgetBottom = QWidget()
self.widgetBottom.setLayout(self.Hlayout_singIn)
self.Vlayout.addWidget(self.widget_title)
self.Vlayout.addWidget(self.widgetBottom, Qt.AlignTop)
# 信号与槽,设定链接到用户和管理的对应页面
self.signIn.clicked.connect(self.signInCheck)
self.lineEdit_pwd.returnPressed.connect(self.signInCheck)
self.lineEdit_stuID.returnPressed.connect(self.signInCheck)
def signInCheck(self):
studentId = self.lineEdit_stuID.text()
password = self.lineEdit_pwd.text()
if studentId == "" or password == "":
print(QMessageBox.warning(self, "警告", "学号和密码不可为空!", QMessageBox.Yes, QMessageBox.Yes))
return
# 打开数据库连接
db = QSqlDatabase.addDatabase("QSQLITE")
db.setDatabaseName('./db/LibraryManagement.db')
db.open()
query = QSqlQuery()
sql = "SELECT * FROM user WHERE StudentId='%s'" % (studentId)
query.exec_(sql)
db.close()
hl = hashlib.md5()
hl.update(password.encode(encoding='utf-8'))
if not query.next():
print(QMessageBox.information(self, "提示", "该账号不存在!", QMessageBox.Yes, QMessageBox.Yes))
else:
if studentId == query.value(0) and hl.hexdigest() == query.value(2):
# 如果是管理员
if query.value(3) == 1:
self.is_admin_signal.emit()
else:
self.is_student_signal.emit(studentId)
else:
print(QMessageBox.information(self, "提示", "密码错误!", QMessageBox.Yes, QMessageBox.Yes))
return
if __name__ == "__main__":
app = QApplication(sys.argv)
app.setWindowIcon(QIcon("./images/MainWindow_1.png"))
app.setStyleSheet(qdarkstyle.load_stylesheet_pyqt5())
mainMindow = SignInWidget()
mainMindow.show()
sys.exit(app.exec_())
- 代码分析
-
重要的还是布局,布局。
-
这个页面的代码布局命名更加不规范,统一改成看词识意的名字了。图形展示界面setUpUI里面的布局是这样的:
- (1)总的布局最底层是垂直布局Vlayout。
- (2)再设定两个水平布局,安排登录窗体大标题和登录小控件的布局Hlayout_title、Hlayout_singIn
- (3)再定义表单布局formlayout_signIn,放置上设定好的两对标签QLabel和单行文本框QLineEdit(包括学号、密码),再设置上按钮(登录)。
- (4)后面连续设定大标题的各级父控件和注册表单布局的父控件,说不清楚,作图如下
-
其他如学号、密码的定义与设置,大标题的位置和规范,就显得好理解多了。
-
这个页面的代码,又定义了一个槽函数signInCheck(),前面代码最后,有信号连接到这个函数。代码如下:
# 信号与槽,设定链接到用户和管理的对应页面 self.signIn.clicked.connect(self.signInCheck) self.lineEdit_pwd.returnPressed.connect(self.signInCheck) self.lineEdit_stuID.returnPressed.connect(self.signInCheck)
-
signInCheck()里面的功能有:用户及密码的验证判断,以及跳转管理页面和用户页面的判断。
- (1)首先查看判断表单控件内是否有信息录入,不可为空。
- (2)然后打开数据库连接,查询验证录入的用户ID是否在数据库中存在;若存在再判断用户名和密码是否一致,不一致则密码错误重新录入。这些成立了,最后判断是不是管理员登录。最最后不忘emit(),再把信号槽提交运行。说不清楚,作图。
- 逻辑图一
- 逻辑图二
-
5.1.3 密码修改(changePasswordDialog.py)
import hashlib
import sys
import qdarkstyle
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtSql import *
from PyQt5.QtWidgets import *
class changePasswordDialog(QDialog):
def __init__(self, parent=None):
super(changePasswordDialog, self).__init__(parent)
self.setWindowModality(Qt.WindowModal)
self.setWindowTitle("修改密码")
self.setUpUI()
def setUpUI(self):
self.resize(300, 280)
self.layout = QFormLayout()
self.setLayout(self.layout)
# 自上而下,5个标签
self.titlelabel = QLabel(" 修改密码")
self.studentIdLabel = QLabel("学 号:")
# self.studentNameLabel=QLabel("姓 名:")
self.oldPasswordLabel = QLabel("旧 密 码:")
self.passwordLabel = QLabel("新 密 码:")
self.confirmPasswordLabel = QLabel("确认密码:")
# 自上而下,4个文本框
self.studentIdEdit = QLineEdit()
# self.studentNameEdit=QLineEdit()
self.oldPasswordEdit = QLineEdit()
self.passwordEdit = QLineEdit()
self.confirmPasswordEdit = QLineEdit()
# “确认修改”按钮
self.changePasswordButton = QPushButton("确认修改")
self.changePasswordButton.setFixedWidth(140)
self.changePasswordButton.setFixedHeight(32)
# 在表单布局放置以上小控件
self.layout.addRow("", self.titlelabel)
self.layout.addRow(self.studentIdLabel, self.studentIdEdit)
# self.layout.addRow(self.studentNameLabel,self.studentNameEdit)
self.layout.addRow(self.oldPasswordLabel, self.oldPasswordEdit)
self.layout.addRow(self.passwordLabel, self.passwordEdit)
self.layout.addRow(self.confirmPasswordLabel, self.confirmPasswordEdit)
self.layout.addRow("", self.changePasswordButton)
font = QFont()
font.setPixelSize(20)
self.titlelabel.setFont(font)
font.setPixelSize(16)
self.studentIdLabel.setFont(font)
# self.studentNameLabel.setFont(font)
self.oldPasswordLabel.setFont(font)
self.passwordLabel.setFont(font)
self.confirmPasswordLabel.setFont(font)
font.setPixelSize(16)
self.studentIdEdit.setFont(font)
self.changePasswordButton.setFont(font)
# self.studentNameEdit.setFont(font)
font.setPixelSize(10)
self.oldPasswordEdit.setFont(font)
self.passwordEdit.setFont(font)
self.confirmPasswordEdit.setFont(font)
self.titlelabel.setMargin(8)
self.layout.setVerticalSpacing(10)
# 设置长度
self.studentIdEdit.setMaxLength(10)
self.oldPasswordEdit.setMaxLength(16)
self.passwordEdit.setMaxLength(16)
self.confirmPasswordEdit.setMaxLength(16)
# 设置密码掩膜
self.oldPasswordEdit.setEchoMode(QLineEdit.Password)
self.passwordEdit.setEchoMode(QLineEdit.Password)
self.confirmPasswordEdit.setEchoMode(QLineEdit.Password)
# 设置校验
reg = QRegExp("PB[0~9]{8}")
pValidator = QRegExpValidator(self)
pValidator.setRegExp(reg)
self.studentIdEdit.setValidator(pValidator)
reg = QRegExp("[a-zA-z0-9]+$")
pValidator.setRegExp(reg)
self.oldPasswordEdit.setValidator(pValidator)
self.passwordEdit.setValidator(pValidator)
self.confirmPasswordEdit.setValidator(pValidator)
# 设置信号与槽
self.changePasswordButton.clicked.connect(self.changePasswordButtonClicked)
def changePasswordButtonClicked(self):
studentId = self.studentIdEdit.text()
oldPassword = self.oldPasswordEdit.text()
password = self.passwordEdit.text()
confirmPassword = self.confirmPasswordEdit.text()
if studentId == "" or oldPassword == "" or password == "" or confirmPassword == "":
print(QMessageBox.warning(self, "警告", "输入不可为空,请重新输入", QMessageBox.Yes, QMessageBox.Yes))
return
db = QSqlDatabase.addDatabase("QSQLITE")
db.setDatabaseName('./db/LibraryManagement.db')
db.open()
query = QSqlQuery()
sql = "SELECT * FROM User WHERE StudentId='%s'" % studentId
query.exec_(sql)
# 如果用户不存在
if not query.next():
print(QMessageBox.warning(self, "警告", "该用户不存在,请重新输入", QMessageBox.Yes, QMessageBox.Yes))
self.studentIdEdit.clear()
return
# 如果密码错误
hl = hashlib.md5()
hl.update(oldPassword.encode(encoding='utf-8'))
md5password = hl.hexdigest()
sql = "SELECT * FROM User WHERE Password='%s' AND StudentId='%s'" % (md5password, studentId)
query.exec_(sql)
if not query.next():
print(QMessageBox.warning(self, "警告", "原密码输入错误,请重新输入", QMessageBox.Yes, QMessageBox.Yes))
self.oldPasswordEdit.clear()
return
# 密码与确认密码不同
if password != confirmPassword:
print(QMessageBox.warning(self, "警告", "两次输入密码不同,请确认输入", QMessageBox.Yes, QMessageBox.Yes))
self.passwordEdit.clear()
self.confirmPasswordEdit.clear()
return
# 修改密码
hl = hashlib.md5()
hl.update(password.encode(encoding='utf-8'))
md5password = hl.hexdigest()
sql = "UPDATE User SET Password='%s' WHERE StudentId='%s'" % (md5password, studentId)
query.exec_(sql)
db.commit()
QMessageBox.information(self, "提醒", "修改密码成功,请登录系统!", QMessageBox.Yes, QMessageBox.Yes)
self.close()
return
if __name__ == "__main__":
app = QApplication(sys.argv)
app.setWindowIcon(QIcon("./images/MainWindow_1.png"))
app.setStyleSheet(qdarkstyle.load_stylesheet_pyqt5())
mainWindow = changePasswordDialog()
mainWindow.show()
sys.exit(app.exec_())
- 代码分析
-
这个对话框不算大,相对来说,代码却有点长,其实比前面三个要简单一些。
-
初始化就不说了,还是setUpUI()函数的布局,最底层只用了一个表单布局,然后自上而下布局为:
- (1)“” + 修改密码Label
- (2)学号Label + 学号LineEdit
- (3)旧密码Label + 旧密码LineEdit
- (4)新密码Label + 新密码LineEdit
- (5)确认密码Label + 确认密码LineEdit
- (6)“” + 修改密码PushButton,如图说明。
- 至于各标签和文本框的风格定义,代码比较多,不过不麻烦。
-
然后是密码校验,代码看上面程序。之后还是绑定信号与槽。
self.changePasswordButton.clicked.connect(self.changePasswordButtonClicked)
-
最后就是自定义槽函数changePasswordButtonClicked()的说明。
-
首先,也是输入的信息不能为空。
-
逻辑图一
-
其次,连接数据库,再用select语句查询要修改密码的用户ID是否存在,然后展开if语句进行条件判断。
-
逻辑图二
-
逻辑图二
-
逻辑图三
-