做完期末设计传上来看看,要源码的留言,有人的话,我开源在github中
三、实验步骤(及实验数据)
1.数据库的设计
使用Navicat配置好基础的数据库和表,填入一些基本信息
在项目的一个文件模块中连接好对应数据库,写好对应的方法函数,便于主程序调用。
2.图书管理系统的设计与实现
首先确定好项目的路径以及放好资源文件,在Qt Designer里面把基础界面完成,主要为两个页面大类,一是登录和注册,二就是管理系统的主要展示和功能界面。这里因为我在需要更换主题,测试后发现直接写在designer里会把项目中写的css样式覆盖掉,所以我先把这里的样式移到代码中,这里一些组件样式显示为透明
明确好目录结构,使用到的图片资源会自动存为.qrc文件,在python中使用需要用外部工具pyrcc进行转换,否则无法在运行界面里显示图片。
接下来开始代码编写,首先导入所需库,这里基本是需要用到在导入的,避免过多导入不需要的库。做好一些全局的配置,像是这里我有做的,图标同步,直接使用ui文件,做高分辨率屏幕适配,以及引起的高清图像显示配置。
建立登录界面的类,继承自QMainWindow,以及从对应ui文件导出的login,做好界面的初始化,因为这里的信号与槽的数量较少,这里是直接写在构造方法下面的。
在这个界面类中,最值得注意的便是这里注册信号对应的函数了,从这里正式开始接触数据库与python的交互,还是显示提交式的。这里只重点提及数据库操作。这是固定流程,1.使用先前定义的模块中的函数,get_conn()来连接上数据库及表,拿到信息;2.继续获取consur一会用来操作数据库;3.制作sql语句,重点便是在这里“%s”,这里%s便是用来代替你要传入的字符;4.使用cur.execute()执行sql语句,注意这里的顺序是从左到右填入%s的位置;5.像是对于这种修改数据库信息的都需要执行conn.commit()显示提交;6.关闭资源。
简单看一下登录信号,这里的查询数据库操作比改数据库信息的要少一步,但是也多了个cur.fetchone()来接收数据查询的结果,数据存在先创建一个属性用来存放主界面的类,关闭当前界面,通过此前的属性来打开新类窗口。
主界面类,也是注意初始化,因为这里信号与槽的数量比较多,所以在构造方法中放了个接口写到后面去了,避免太过混乱。因为这里的代码比较多,不好一一细讲。其实大致步骤和前面的相同,主要就是信号与槽,按钮与方法的关系,做好用到的控件的方法的了解,就可以很好的使用。
登录界面:
注册界面:
不展示了我的名字在这个界面上,懒得改
实现效果:
值得注意的是,为了好看我把所有窗口的windows自带边框隐藏掉了,关闭和缩小操作我都自己加的图标。
主界面:
添加书籍:
编辑书籍:
展示读者信息:
暗色主题:
配置:
主要代码:
from PyQt5.QtWidgets import *
from PyQt5.uic import loadUiType
import sys
from PyQt5 import QtCore, QtGui
from dbutil import get_conn, close_conn
import ctypes
# 保证图标同步
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID("myappid")
# UI--Logic分离 不会有提示,避免进行py转换
main, _ = loadUiType('index.ui')
login, _ = loadUiType('login.ui')
# 让实际同预览效果一致,添加对高分辨率屏幕的支持
QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling)
# PyQt5高清屏幕自适应设置,以及让添加的高清图标显示清晰,不然designer导入的图标在程序加载时会特别模糊
QApplication.setAttribute(QtCore.Qt.AA_UseHighDpiPixmaps)
class LoginAPP(QMainWindow, login): # 建立Login界面类,继承对应的父类
# 定义构造方法
def __init__(self):
# 初始化
QMainWindow.__init__(self)
self.setupUi(self)
self.qbt.clicked.connect(self.handel_login)
# self.setWindowFlags(Qt.Qt.CustomizeWindowHint) # 去掉标题栏的代码
# self.setWindowFlags(Qt.Qt.FramelessWindowHint)
# self.setWindowFlags(Qt.Qt.CustomizeWindowHint)
# 登录按扭连接的事件(连接到一个方法)
self.loginb.clicked.connect(self.login_btn)
self.zc.clicked.connect(self.zc_btn)
self.zct.clicked.connect(self.zct_btn)
self.setWindowIcon(QtGui.QIcon('icons/tushuguanli.png'))
"""隐藏系统边框;隐藏系统自带窗口背景"""
self.setWindowFlag(QtCore.Qt.FramelessWindowHint)
self.setAttribute(QtCore.Qt.WA_TranslucentBackground)
def handel_login(self):
self.close()
def zc_btn(self):
self.login_reg.setCurrentIndex(2)
def zct_btn(self):
zh = self.zh.text()
mm = self.mm.text()
qmm = self.qmm.text()
if mm == qmm:
# 数据库操作流程
# 1、获取连接
conn = get_conn()
# 2、获取cursor
cur = conn.cursor()
# 3.制作sql语句
sql = "INSERT into users(user_name,user_pwd) values(%s, %s);"
# 4.执行sql语句
cur.execute(sql, (zh, mm))
# 5、insert、update、delete必须显示提交
conn.commit()
# 6、关闭资源
close_conn(conn, cur)
self.error_message.setText('注册成功')
self.login_reg.setCurrentIndex(0)
else:
self.zcm.setText('两次密码不一致')
self.zh.setText('')
self.mm.setText('')
self.qmm.setText('')
def login_btn(self):
# 连接数据库, 这里的get_conn是自己定义的
conn = get_conn()
cur = conn.cursor()
# 创建sql语句获得数据库中的具体内容
# 注意%s代指后面执行语句的括号参数
sql = "select * from users where user_name=%s and user_pwd=%s"
print(self.password.text())
print(self.username.text())
# 拿到输入内容,这里是通过self.名为username的输入框
user_name = self.username.text()
pwd = self.password.text()
# 运行sql语句进行数据验证
cur.execute(sql, (user_name, pwd))
data = cur.fetchone()
print(data)
if data:
self.mapp = MainAPP() # 创建属性赋类
self.close() # 关闭此前类窗口
self.mapp.show() # 打开新类窗口
else:
self.error_message.setText('登录失败')
class MainAPP(QMainWindow, main):
# 定义构造方法
def __init__(self):
# 初始化
QMainWindow.__init__(self)
"""隐藏系统边框;隐藏系统自带窗口背景"""
self.setWindowFlag(QtCore.Qt.FramelessWindowHint)
self.setAttribute(QtCore.Qt.WA_TranslucentBackground)
self.setupUi(self)
self.btn()
self.show_books()
self.show_readers()
self.show_bro()
self.show_peiz()
self.show_peiz2()
self.chk.setText('暗主题')
self.theme = 1
self.setWindowIcon(QtGui.QIcon('icons/tushuguanli.png'))
self.show_author_combobox()
self.show_e_combobox()
self.dark_gray_theme()
# 按钮信号与槽
def btn(self):
self.f1.clicked.connect(self.f1weg) # 主界面1
self.f2.clicked.connect(self.f2weg) # 主界面2
self.f3.clicked.connect(self.f3weg) # 主界面3
self.add_bt.clicked.connect(self.add_th) # 添加书
self.serchbt.clicked.connect(self.serch_btn) # 搜索书
self.qbt.clicked.connect(self.handel_gb) # 关闭
self.qbt_2.clicked.connect(self.handel_sx) # 缩小窗口
self.xg.clicked.connect(self.xg_btn) # 修改书
self.chk.clicked.connect(self.dark_gray_theme) # 暗主题
self.peiz.clicked.connect(self.f4weg) # 配置界面(主界面4)
self.aat.clicked.connect(self.add_a) # 添加作者
self.aac.clicked.connect(self.add_c) # 添加出版社
self.xg_2.clicked.connect(self.delete_book) # 删除书籍
def show_author_combobox(self):
conn = get_conn()
cur = conn.cursor()
sql = "select author from pei_zhi"
cur.execute(sql)
data = cur.fetchall()
if data:
self.add_aut.clear()
self.add_aut_3.clear()
for author in data:
self.add_aut.addItem(author[0])
self.add_aut_3.addItem(author[0])
def show_e_combobox(self):
conn = get_conn()
cur = conn.cursor()
sql = "select 出版社 from pei_zhi2"
cur.execute(sql)
data = cur.fetchall()
if data:
self.add_aut_2.clear()
self.add_aut_4.clear()
for author in data:
self.add_aut_2.addItem(author[0])
self.add_aut_4.addItem(author[0])
def show_peiz(self):
# 数据库操作流程
# 1、获取连接
conn = get_conn()
# 2、获取cursor
cur = conn.cursor()
# 3.制作sql语句
sql = "SELECT author FROM pei_zhi;"
# 4.执行sql语句
cur.execute(sql)
# 5.获取执行后的数据
data = cur.fetchall()
# 设计书籍表页面类的开始行,插入行
self.pei_co.setRowCount(0)
self.pei_co.insertRow(0)
print(data) # 数据形式为列表套元组,元组内为一条记录(一行)
"""取行下标,取行数据"""
for row, form in enumerate(data):
"""取列下标,取列数据"""
for column, item in enumerate(form):
"""设置表中值"""
self.pei_co.setItem(row, column, QTableWidgetItem(str(item)))
# column += 1
"""行位置设置&数据插入"""
row_postion = self.pei_co.rowCount()
self.pei_co.insertRow(row_postion)
def show_peiz2(self):
# 数据库操作流程
# 1、获取连接
conn = get_conn()
# 2、获取cursor
cur = conn.cursor()
# 3.制作sql语句
sql = "SELECT 出版社 FROM pei_zhi2;"
# 4.执行sql语句
cur.execute(sql)
# 5.获取执行后的数据
data = cur.fetchall()
# 设计书籍表页面类的开始行,插入行
self.pei_co2.setRowCount(0)
self.pei_co2.insertRow(0)
print(data) # 数据形式为列表套元组,元组内为一条记录(一行)
"""取行下标,取行数据"""
for row, form in enumerate(data):
"""取列下标,取列数据"""
for column, item in enumerate(form):
"""设置表中值"""
self.pei_co2.setItem(row, column, QTableWidgetItem(str(item)))
# column += 1
"""行位置设置&数据插入"""
row_postion = self.pei_co2.rowCount()
self.pei_co2.insertRow(row_postion)
def add_a(self):
co = self.l1.text()
if co:
# 数据库操作流程
# 1、获取连接
conn = get_conn()
# 2、获取cursor
cur = conn.cursor()
# 3.制作sql语句
sql = "INSERT into pei_zhi(author) values(%s);"
# 4.执行sql语句
cur.execute(sql, (co,))
# 5、insert、update、delete必须显示提交
conn.commit()
# 6、关闭资源
close_conn(conn, cur)
self.l1.setText('')
# 刷新显示
self.show_peiz()
self.la3.setText('添加成功')
self.show_author_combobox()
self.show_e_combobox()
else:
self.la3.setText('添加失败')
def add_c(self):
co = self.l2.text()
if co:
# 数据库操作流程
# 1、获取连接
conn = get_conn()
# 2、获取cursor
cur = conn.cursor()
# 3.制作sql语句
sql = "INSERT into pei_zhi2(出版社) values(%s);"
# 4.执行sql语句
cur.execute(sql, (co,))
# 5、insert、update、delete必须显示提交
conn.commit()
# 6、关闭资源
close_conn(conn, cur)
self.l2.setText('')
# 刷新显示
self.show_peiz2()
self.la3.setText('添加成功')
self.show_author_combobox()
self.show_e_combobox()
else:
self.la3.setText('添加失败')
def xg_btn(self):
book_name = self.xgbn.text()
book_code = self.xgbc.text()
book_author = self.add_aut_3.currentText()
book_cb = self.add_aut_4.currentText()
book_price = self.xgbp.text()
if book_cb and book_author and book_price and book_name and book_code:
print(book_cb, book_price, book_name)
# 数据库操作流程
# 1、获取连接
conn = get_conn()
# 2、获取cursor
cur = conn.cursor()
# 3.制作sql语句
sql = "update books set ISBN=%s, book_name=%s, book_autor=%s, 出版社=%s, book_price=%s where book_name=%s;"
# 4.执行sql语句
try:
cur.execute(sql, (book_code, book_name, book_author, book_cb, book_price, book_name))
# 5、insert、update、delete必须显示提交
conn.commit()
except:
pass
# 6、关闭资源
close_conn(conn, cur)
self.show_books()
else:
self.ts.setText('请确认数据后再提交')
def serch_btn(self):
book_name = self.serch_name.text()
# 数据库操作流程
# 1、获取连接
conn = get_conn()
# 2、获取cursor
cur = conn.cursor()
# 3.制作sql语句
sql = "SELECT ISBN, book_name, book_autor, 出版社, book_price FROM books where book_name=%s"
# 4.执行sql语句
try:
cur.execute(sql, (book_name,))
# 5.获取执行后的数据
data = cur.fetchall()
self.xgbn.setText(data[0][1])
self.xgbc.setText(data[0][0])
self.add_aut_3.setCurrentText(data[0][2])
self.add_aut_4.setCurrentText(data[0][3])
self.xgbp.setText(data[0][4])
self.ts.setText('搜索成功')
except:
self.ts.setText('搜索失败')
def f1weg(self):
self.label_4.setText('')
self.sfwegs.setCurrentIndex(0)
if not self.theme:
style = open("themes/ui.css", 'r')
style = style.read()
self.setStyleSheet(style)
else:
style = open("themes/qdark.css", 'r')
style = style.read()
self.setStyleSheet(style)
def f2weg(self):
self.label_4.setText('')
self.sfwegs.setCurrentIndex(1)
if not self.theme:
style = open("themes/f1.css", 'r')
style = style.read()
self.setStyleSheet(style)
else:
style = open("themes/fd1.css", 'r')
style = style.read()
self.setStyleSheet(style)
def f3weg(self):
self.label_4.setText('')
self.sfwegs.setCurrentIndex(2)
if not self.theme:
style = open("themes/f2.css", 'r')
style = style.read()
self.setStyleSheet(style)
else:
style = open("themes/fd2.css", 'r')
style = style.read()
self.setStyleSheet(style)
def f4weg(self):
self.label_4.setText('')
self.sfwegs.setCurrentIndex(3)
if not self.theme:
style = open("themes/f3.css", 'r')
style = style.read()
self.setStyleSheet(style)
else:
style = open("themes/fd3.css", 'r')
style = style.read()
self.setStyleSheet(style)
def handel_sx(self):
self.showMinimized()
def handel_gb(self):
self.close()
# 删除书籍
def delete_book(self):
# 数据库操作流程
# 1、获取连接
conn = get_conn()
# 2、获取cursor
cur = conn.cursor()
sql = "delete from books where book_name = %s"
book_name = self.xgbn.text()
warning = QMessageBox.warning(self, '删除图书', '你确定要删除',
QMessageBox.Yes | QMessageBox.No)
if warning == QMessageBox.Yes:
cur.execute(sql, (book_name,))
conn.commit()
close_conn(conn, cur)
self.show_books()
self.statusBar().showMessage('图书成功删除')
def dark_gray_theme(self): # 暗主题
if self.theme:
style = open("themes/ui.css", 'r')
style = style.read()
self.setStyleSheet(style)
self.theme = 0
else:
style = open("themes/qdark.css", 'r')
style = style.read()
self.setStyleSheet(style)
self.theme = 1
def add_th(self):
b_name = self.add_bname.text()
b_code = self.add_bcode.text()
b_au = self.add_aut.currentText()
b_cb = self.add_aut_2.currentText()
b_price = self.add_bprice.text()
print(b_name, b_code, b_au, b_cb, type(b_price))
if b_code and b_name and b_price:
# 数据库操作流程
# 1、获取连接
conn = get_conn()
# 2、获取cursor
cur = conn.cursor()
# 3.制作sql语句
sql = "INSERT into books(ISBN, book_name, book_autor, 出版社, book_price) values(%s, %s, %s, %s, %s);"
# 4.执行sql语句
cur.execute(sql, (b_code, b_name, b_au, b_cb, b_price))
# 5、insert、update、delete必须显示提交
conn.commit()
# 6、关闭资源
close_conn(conn, cur)
self.add_bname.setText('')
self.add_bcode.setText('')
self.add_aut.setCurrentIndex(0)
self.add_aut_2.setCurrentIndex(0)
self.add_bprice.setText('')
# 刷新显示
self.show_books()
self.label_4.setText('添加成功')
# self.tabWidget_3.setCurrentIndex(0)
else:
self.label_4.setText('请检查数据!')
# 获取所有图书
def show_books(self):
# 数据库操作流程
# 1、获取连接
conn = get_conn()
# 2、获取cursor
cur = conn.cursor()
# 3.制作sql语句
sql = "SELECT ISBN, book_name, book_autor, 出版社, book_price FROM books"
# 4.执行sql语句
cur.execute(sql)
# 5.获取执行后的数据
data = cur.fetchall()
# 设计书籍表页面类的开始行,插入行
self.book_table.setRowCount(0)
self.book_table.insertRow(0)
print(data) # 数据形式为列表套元组,元组内为一条记录(一行)
"""取行下标,取行数据"""
for row, form in enumerate(data):
"""取列下标,取列数据"""
for column, item in enumerate(form):
"""设置表中值"""
self.book_table.setItem(row, column, QTableWidgetItem(str(item)))
# column += 1
"""行位置设置&数据插入"""
row_postion = self.book_table.rowCount()
self.book_table.insertRow(row_postion)
def show_readers(self):
# 数据库操作流程
# 1、获取连接
conn = get_conn()
# 2、获取cursor
cur = conn.cursor()
# 3.制作sql语句
sql = "SELECT r_id, reader_name, reader_sex, 电话,reader_birthday, reader_borrowtotal from reader;"
# 4.执行sql语句
cur.execute(sql)
# 5.获取执行后的数据
data = cur.fetchall()
# 设计书籍表页面类的开始行,插入行
self.reader_table.setRowCount(0)
self.reader_table.insertRow(0)
print(data) # 数据形式为列表套元组,元组内为一条记录(一行)
"""取行下标,取行数据"""
for row, form in enumerate(data):
"""取列下标,取列数据"""
for column, item in enumerate(form):
"""设置表中值"""
self.reader_table.setItem(row, column, QTableWidgetItem(str(item)))
# column += 1
"""行位置设置&数据插入"""
row_postion = self.reader_table.rowCount()
self.reader_table.insertRow(row_postion)
def show_bro(self):
# 数据库操作流程
# 1、获取连接
conn = get_conn()
# 2、获取cursor
cur = conn.cursor()
# 3.制作sql语句
sql = "SELECT reader_id, ISBN, name, book_name, borrow_date FROM borrow_record"
# 4.执行sql语句
cur.execute(sql)
# 5.获取执行后的数据
data = cur.fetchall()
# 设计书籍表页面类的开始行,插入行
self.bro_table.setRowCount(0)
self.bro_table.insertRow(0)
print(data) # 数据形式为列表套元组,元组内为一条记录(一行)
"""取行下标,取行数据"""
for row, form in enumerate(data):
"""取列下标,取列数据"""
for column, item in enumerate(form):
"""设置表中值"""
self.bro_table.setItem(row, column, QTableWidgetItem(str(item)))
# column += 1
"""行位置设置&数据插入"""
row_postion = self.bro_table.rowCount()
self.bro_table.insertRow(row_postion)
def main():
"""启动"""
app = QApplication(sys.argv)
window = LoginAPP()
window.show()
app.exec_() # 持续事件
if __name__ == '__main__':
main()
四、实验思考与总结
本次实验项目主要是通过Python搭配PyQt5进行数据库的实际操作项目开发。在整个项目的设计流程中我也逐渐熟悉了数据库的操作,再次加深了对SQL语言使用的印象。
本次项目的完成,也让我学习到了很多新的知识和操作,使用python连接本地MYSQL数据库;PyQt5的代码设计,信号与槽,按钮与方法的连接交互;使用Qt Designer设计前端界面。因为这里的样式也用到了CSS,也加强了我前端样式的设计能力。
当然在项目的推进过程中,也遇到了不少的问题。像是图片资源不显示,这是因为没有时刻进行设计和代码的同步转换;特别还有,在Qt Designer设计时一定要从右键点击来更改对象名称,要不然就别改,直接从对象浏览器那里改有时不会生效;此外一定要搞清楚各个文件的关系,像是ui文件和Python代码的联系,各种资源文件的使用,外部工具的使用等等;对于Qt代码各种组件的使用,不用专门系统的学习,用啥搜啥,理解到了就可以很好的使用;还有对代码中各个类界面的初始化,实例化,界面联系也要理解清楚。
总的来说,本次项目的完成,让我收获颇丰。学习到了各种新的知识,新的操作,对数据库的理解和操作也进一步加深了。相信对我为了进一步的学习和工作带来不小的益处。