(Python设计图书馆管理系统技术文档(四))

5.设计和代码

  • (接上篇)

5.2 用户系统使用

5.2.1 用户界面(StudentHome.py)

import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import QIcon, QFont
import qdarkstyle
import sip

from BookStorageViewer import BookStorageViewer
from borrowBookDialog import borrowBookDialog
from returnBookDialog import returnBookDialog
from BorrowStatusViewer import BorrowStatusViewer


class StudentHome(QWidget):
    def __init__(self, studentId):
        super().__init__()
        self.StudentId = studentId
        self.resize(900, 600)
        self.setWindowTitle("欢迎使用图书馆管理系统")
        self.setUpUI()

    def setUpUI(self):
        # 总布局
        self.layout = QHBoxLayout(self)
        # 按钮布局
        self.buttonLayout = QVBoxLayout()

        # 按钮控件(借书、还书、借阅状态、所有书籍),放置到垂直布局并设定字体等风格样式
        self.borrowBookButton = QPushButton("借书")
        self.returnBookButton = QPushButton("还书")
        self.myBookStatus = QPushButton("借阅状态")
        self.allBookButton = QPushButton("所有书籍")
        self.buttonLayout.addWidget(self.borrowBookButton)
        self.buttonLayout.addWidget(self.returnBookButton)
        self.buttonLayout.addWidget(self.myBookStatus)
        self.buttonLayout.addWidget(self.allBookButton)
        self.borrowBookButton.setFixedWidth(100)
        self.borrowBookButton.setFixedHeight(42)
        self.returnBookButton.setFixedWidth(100)
        self.returnBookButton.setFixedHeight(42)
        self.myBookStatus.setFixedWidth(100)
        self.myBookStatus.setFixedHeight(42)
        self.allBookButton.setFixedWidth(100)
        self.allBookButton.setFixedHeight(42)
        font = QFont()
        font.setPixelSize(16)
        self.borrowBookButton.setFont(font)
        self.returnBookButton.setFont(font)
        self.myBookStatus.setFont(font)
        self.allBookButton.setFont(font)

        # 实例化一个书籍库存预览的对象并调用
        # 实例化一个借阅状态的对象并调用
        # 把所有书籍按钮设为不可用,不然会出现分屏的情况
        self.storageView = BookStorageViewer()
        self.borrowStatusView = BorrowStatusViewer(self.StudentId)
        self.allBookButton.setEnabled(False)

        # 把上面的按钮布局和书籍库存预览放到水平布局中
        self.layout.addLayout(self.buttonLayout)
        self.layout.addWidget(self.storageView)

        # 信号与槽的绑定:四个按钮与相应事件的对应
        self.borrowBookButton.clicked.connect(self.borrowBookButtonClicked)
        self.returnBookButton.clicked.connect(self.returnBookButtonClicked)
        self.myBookStatus.clicked.connect(self.myBookStatusClicked)
        self.allBookButton.clicked.connect(self.allBookButtonClicked)

    # 信号槽函数:借书的dialog
    def borrowBookButtonClicked(self):
        borrowDialog = borrowBookDialog(self.StudentId, self)
        borrowDialog.borrow_book_success_signal.connect(self.borrowStatusView.borrowedQuery)
        borrowDialog.borrow_book_success_signal.connect(self.storageView.searchButtonClicked)
        borrowDialog.show()
        borrowDialog.exec_()
        return

    # 信号槽函数:还书的dialog
    def returnBookButtonClicked(self):
        returnDialog = returnBookDialog(self.StudentId, self)
        returnDialog.return_book_success_signal.connect(self.borrowStatusView.returnedQuery)
        returnDialog.return_book_success_signal.connect(self.borrowStatusView.borrowedQuery)
        returnDialog.return_book_success_signal.connect(self.storageView.searchButtonClicked)
        returnDialog.show()
        returnDialog.exec_()

    # 槽函数:借阅状态的切换和查询
    def myBookStatusClicked(self):
        self.layout.removeWidget(self.storageView)
        sip.delete(self.storageView)
        self.storageView = BookStorageViewer()
        self.borrowStatusView = BorrowStatusViewer(self.StudentId)
        self.layout.addWidget(self.borrowStatusView)
        self.allBookButton.setEnabled(True)
        self.myBookStatus.setEnabled(False)
        return

    # 槽函数:关掉借阅状态切换至所有书籍并展示分页信息
    def allBookButtonClicked(self):
        self.layout.removeWidget(self.borrowStatusView)
        sip.delete(self.borrowStatusView)
        self.borrowStatusView = BorrowStatusViewer(self.StudentId)
        self.storageView = BookStorageViewer()
        self.layout.addWidget(self.storageView)
        self.allBookButton.setEnabled(False)
        self.myBookStatus.setEnabled(True)
        return


if __name__ == "__main__":
    app = QApplication(sys.argv)
    app.setWindowIcon(QIcon("./images/MainWindow_1.png"))
    app.setStyleSheet(qdarkstyle.load_stylesheet_pyqt5())
    mainWindow = StudentHome("PB15000139")
    mainWindow.show()
    sys.exit(app.exec_())

  • 代码分析
    • 这个窗体的布局相对前面的简单多了。一个水平布局,左侧放置一个垂直布局,垂直布局里面放置4个按钮;右侧放置所有书籍的预览界面,点击借阅状态后,这个地方还可以展示上下垂直布局的借阅状态。效果如图。 在这里插入图片描述
    • 再4个信号与槽函数的使用。前两个同时还是信号函数,连接到外部的槽函数,弹出新的对话框;后两个是槽函数,调用外部类方法,展示新的页面。注意,每个槽函数同时也是信号函数,调用外部类的方法。
      • 借书
    graph TD
    A[发送者: borrowBookButton] -- 信号: clicked--> B((连接: connect))--接收 -->
    C(接受者:borrowBookButtonClicked)--调用: get-->D(槽函数: borrowBookDialog)
    
    • 还书
    graph TD
    A[发送者: returnBookButton] -- 信号: clicked--> B((连接: connect))--接收 -->
    C(接受者:returnBookButtonClicked)--调用: get-->D(槽函数: returnBookDialog)
    
    • 借阅状态
    graph TD
    A[发送者: myBookStatus] -- 信号: clicked--> B((连接: connect))--接收 -->
    C(接受者:myBookStatusClicked)--调用: get-->D(槽函数: BorrowStatusViewer)
    
    • 所有书籍
    graph TD
    A[发送者: allBookButton] -- 信号: clicked--> B((连接: connect))--接收 -->
    C(接受者:allBookButtonClicked)--调用: get-->D(槽函数: borrowBookDialog)
    

5.2.2 查询书籍(BookStorageViewer.py)

# -*- coding: utf-8 -*-
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import Qt
import qdarkstyle
from PyQt5.QtSql import *


class BookStorageViewer(QWidget):
    def __init__(self):
        super(BookStorageViewer, self).__init__()
        self.resize(1300, 500)
        self.setWindowTitle("欢迎使用享学图书馆管理系统")
        # 查询模型
        self.queryModel = None
        # 数据表
        self.tableView = None
        # 当前页
        self.currentPage = 0
        # 总页数
        self.totalPage = 0
        # 总记录数
        self.totalRecord = 0
        # 每页数据数
        self.pageRecord = 10
        self.setUpUI()

    def setUpUI(self):
        self.Vlayout = QVBoxLayout()
        self.Hlayout_top = QHBoxLayout()
        self.Hlayout_bottom = QHBoxLayout()

        # Hlayout_top控件的初始化
        self.searchEdit = QLineEdit()
        self.searchEdit.setFixedHeight(32)
        font = QFont()
        font.setPixelSize(15)
        self.searchEdit.setFont(font)

        self.searchButton = QPushButton("查询")
        self.searchButton.setFixedHeight(32)
        self.searchButton.setFont(font)
        self.searchButton.setIcon(QIcon(QPixmap("./images/search.png")))

        self.condisionComboBox = QComboBox()
        searchCondision = ['按书名查询', '按书号查询', '按作者查询', '按分类查询', '按出版社查询']
        self.condisionComboBox.setFixedHeight(32)
        self.condisionComboBox.setFont(font)
        self.condisionComboBox.addItems(searchCondision)

        self.Hlayout_top.addWidget(self.searchEdit)
        self.Hlayout_top.addWidget(self.searchButton)
        self.Hlayout_top.addWidget(self.condisionComboBox)

        # Hlayout_bottom初始化
        self.jumpToLabel = QLabel("跳转到第")
        self.pageEdit = QLineEdit()
        self.pageEdit.setFixedWidth(30)
        s = "/" + str(self.totalPage) + "页"
        self.pageLabel = QLabel(s)
        self.jumpToButton = QPushButton("跳转")
        self.prevButton = QPushButton("前一页")
        self.prevButton.setFixedWidth(60)
        self.backButton = QPushButton("后一页")
        self.backButton.setFixedWidth(60)

        Hlayout = QHBoxLayout()
        Hlayout.addWidget(self.jumpToLabel)
        Hlayout.addWidget(self.pageEdit)
        Hlayout.addWidget(self.pageLabel)
        Hlayout.addWidget(self.jumpToButton)
        Hlayout.addWidget(self.prevButton)
        Hlayout.addWidget(self.backButton)
        widget = QWidget()
        widget.setLayout(Hlayout)
        widget.setFixedWidth(500)
        self.Hlayout_bottom.addWidget(widget)

        # tableView
        # 序号,书名,书号,作者,分类,出版社,出版时间,库存,剩余可借
        self.db = QSqlDatabase.addDatabase("QSQLITE")
        self.db.setDatabaseName('./db/LibraryManagement.db')
        self.db.open()
        self.tableView = QTableView()
        self.tableView.horizontalHeader().setStretchLastSection(True)
        self.tableView.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
        self.tableView.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.queryModel = QSqlQueryModel()
        self.searchButtonClicked()
        self.tableView.setModel(self.queryModel)

        self.queryModel.setHeaderData(0, Qt.Horizontal, "书名")
        self.queryModel.setHeaderData(1, Qt.Horizontal, "书号")
        self.queryModel.setHeaderData(2, Qt.Horizontal, "作者")
        self.queryModel.setHeaderData(3, Qt.Horizontal, "分类")
        self.queryModel.setHeaderData(4, Qt.Horizontal, "出版社")
        self.queryModel.setHeaderData(5, Qt.Horizontal, "出版时间")
        self.queryModel.setHeaderData(6, Qt.Horizontal, "库存")
        self.queryModel.setHeaderData(7, Qt.Horizontal, "剩余可借")
        self.queryModel.setHeaderData(8, Qt.Horizontal, "总借阅次数")

        self.Vlayout.addLayout(self.Hlayout_top)
        self.Vlayout.addWidget(self.tableView)
        self.Vlayout.addLayout(self.Hlayout_bottom)
        self.setLayout(self.Vlayout)

        # 信号槽绑定
        self.searchButton.clicked.connect(self.searchButtonClicked)
        self.prevButton.clicked.connect(self.prevButtonClicked)
        self.backButton.clicked.connect(self.backButtonClicked)
        self.jumpToButton.clicked.connect(self.jumpToButtonClicked)
        self.searchEdit.returnPressed.connect(self.searchButtonClicked)

    # 设置窗体底部两个按钮“前一页”、“下一页”的状态
    def setButtonStatus(self):
        if self.currentPage == self.totalPage:
            self.prevButton.setEnabled(True)
            self.backButton.setEnabled(False)
        if self.currentPage == 1:
            self.backButton.setEnabled(True)
            self.prevButton.setEnabled(False)
        if self.totalPage > self.currentPage > 1:
            self.prevButton.setEnabled(True)
            self.backButton.setEnabled(True)

    # 得到数据总的行数(记录数)
    def getTotalRecordCount(self):
        self.queryModel.setQuery("SELECT * FROM Book")
        self.totalRecord = self.queryModel.rowCount()
        return

    # 得到总页数
    def getPageCount(self):
        self.getTotalRecordCount()
        # 上取整,如num=(40+10-1)/10就是4页,num=(41+10-1)/10就是5页
        # totalRecord查询得到的记录数(根据条件得到部分和总数),pageRecord是一个类常量
        self.totalPage = int((self.totalRecord + self.pageRecord - 1) / self.pageRecord)
        return

    # 分页记录查询,根据条件得到记录数量,以便分布展示
    def recordQuery(self, index):
        # 1.选定查询书籍的类型(类型字段)
        conditionOrCategory = self.condisionComboBox.currentText()
        condition = {
            'BookName': "按书名查询",
            'BookId': "按书号查询",
            'Auth': "按作者查询",
            'Category': '按分类查询',
            'Publisher': '按出版社查询'
        }
        for k, v in condition.items():
            if conditionOrCategory == v:
                conditionOrCategory = k

        # 2.根据模糊查询条件,得到记录的信息,最后获取查询记录的行数
        temp = self.searchEdit.text()
        s = '%'
        for i in range(0, len(temp)):
            s = s + temp[i] + "%"
        # 下面语法为查询字段为conditionOrCategory的记录,并按字段conditionOrCategory排序,
        # 该字段里面匹配 "%str1%str2%……“这样的文本信息
        queryCondition = ("SELECT * FROM Book WHERE %s LIKE '%s' ORDER BY %s " % (
            conditionOrCategory, s, conditionOrCategory))
        self.queryModel.setQuery(queryCondition)
        # 得到查询到的记录数量
        self.totalRecord = self.queryModel.rowCount()

        # 3.当查询无记录时提醒,再次刷新下面状态栏的记录数
        if self.totalRecord == 0:
            print(QMessageBox.information(self, "提醒", "查询无记录"))
            queryCondition = "select * from Book"
            self.queryModel.setQuery(queryCondition)
            self.totalRecord = self.queryModel.rowCount()
            self.totalPage = int((self.totalRecord + self.pageRecord - 1) / self.pageRecord)
            label = "/" + str(int(self.totalPage)) + "页"
            self.pageLabel.setText(label)

            # 下面语句加了字段排序,然后限定从第几条开始查,查10条
            queryCondition = (
                    "select * from Book ORDER BY %s  limit %d,%d "
                    % (conditionOrCategory, index, self.pageRecord))
            self.queryModel.setQuery(queryCondition)
            self.setButtonStatus()
            return
        self.totalPage = int((self.totalRecord + self.pageRecord - 1) / self.pageRecord)
        label = "/" + str(int(self.totalPage)) + "页"
        self.pageLabel.setText(label)
        queryCondition = ("SELECT * FROM Book WHERE %s LIKE '%s' ORDER BY %s LIMIT %d,%d " % (
            conditionOrCategory, s, conditionOrCategory, index, self.pageRecord))
        self.queryModel.setQuery(queryCondition)
        self.setButtonStatus()
        return

    # 点击查询,查询按钮的槽函数
    def searchButtonClicked(self):
        self.currentPage = 1
        self.pageEdit.setText(str(self.currentPage))
        self.getPageCount()
        s = "/" + str(int(self.totalPage)) + "页"
        self.pageLabel.setText(s)
        index = (self.currentPage - 1) * self.pageRecord
        self.recordQuery(index)
        return

    # 向前翻页,前一页按钮的槽函数
    def prevButtonClicked(self):
        self.currentPage -= 1
        if self.currentPage <= 1:
            self.currentPage = 1
        self.pageEdit.setText(str(self.currentPage))
        index = (self.currentPage - 1) * self.pageRecord
        self.recordQuery(index)
        return

    # 向后翻页,后一页按钮的槽函数
    def backButtonClicked(self):
        self.currentPage += 1
        if self.currentPage >= int(self.totalPage):
            self.currentPage = int(self.totalPage)
        self.pageEdit.setText(str(self.currentPage))
        index = (self.currentPage - 1) * self.pageRecord
        self.recordQuery(index)
        return

    # 点击跳转,跳转按钮的槽函数
    def jumpToButtonClicked(self):
        if self.pageEdit.text().isdigit():
            self.currentPage = int(self.pageEdit.text())
            if self.currentPage > self.totalPage:
                self.currentPage = self.totalPage
            if self.currentPage <= 1:
                self.currentPage = 1
        else:
            self.currentPage = 1
        index = (self.currentPage - 1) * self.pageRecord
        self.pageEdit.setText(str(self.currentPage))
        self.recordQuery(index)
        return


if __name__ == "__main__":
    app = QApplication(sys.argv)
    app.setWindowIcon(QIcon("./images/MainWindow_1.png"))
    app.setStyleSheet(qdarkstyle.load_stylesheet_pyqt5())
    mainWindow = BookStorageViewer()
    mainWindow.show()
    sys.exit(app.exec_())


  • 代码分析
    • 第一说明的,还是布局。文字太麻烦,下面两幅图说明。 在这里插入图片描述 在这里插入图片描述
    • 第二是信号与槽的学习。一共有5对信号槽绑定,分别对应“查询”、“前一页”、“后一页”、“跳转”按钮这4个clicked信号的,以及一个“查询文本框”searchEdit里的焦点信号returnPressed的,最后这个功能自然是录入查询信息回车后发送查询信号。这部分代码如下:
      # 信号槽绑定
      self.searchButton.clicked.connect(self.searchButtonClicked)
      self.prevButton.clicked.connect(self.prevButtonClicked)
      self.backButton.clicked.connect(self.backButtonClicked)
      self.jumpToButton.clicked.connect(self.jumpToButtonClicked)
      self.searchEdit.returnPressed.connect(self.searchButtonClicked)
      
    • 第三是各小控件功能与代码实现,其中也对应上面信号槽绑定下的各个槽函数,现在我们自左至右,自上而下开始逐个理解。
      • (1)查询文本框(searchEdit)
        • 这里看代码有个小技巧,那就是双击关键词,再按Ctrl+F,就给整片代码中的这个词打上小方框,然后还可以点上一个、下一个。
        • 这个上面searchEdit的大小 、位置、样式等容易理解,后面接着是功能实现的代码,用来给searchEdit文本实现模糊查询。
          # 得到模糊查询条件
          temp = self.searchEdit.text()
          s = '%'
          for i in range(0, len(temp)):
              s = s + temp[i] + "%"
          queryCondition = ("SELECT * FROM Book WHERE %s LIKE '%s' ORDER BY %s " % (
              conditionChoice, s, conditionChoice))
          
        • 这段代码抽出来,代入实例如下图。
          • 数据库sql语句实现 在这里插入图片描述
          • 系统实现在这里插入图片描述
      • (2)查询按钮(searchButton)
        • 大小 、位置、样式、小图标(放大镜)……,这个按钮用信号槽完成搜索动作,槽函数是searchButtonClicked,同时这个槽函数还用于上面的searchEdit,不点这个按钮,输入文本回车也能实现查询。
        • 槽函数searchButtonClicked里面的实现机制是,
      • (3)根据书籍分数查询(condisionComboBox)
        • 这里用了一个下拉列表框,原来里面用if方法实现,改成字典的了:

           # 1.选定查询书籍的类型(类型字段)
                  conditionOrCategory = self.condisionComboBox.currentText()
                  condition = {
                      'BookName': "按书名查询",
                      'BookId': "按书号查询",
                      'Auth': "按作者查询",
                      'Category': '按分类查询',
                      'Publisher': '按出版社查询'
                  }
                  for k, v in condition.items():
                      if conditionOrCategory == v:
                          conditionOrCategory = k
          
          • 这段代码放在分页记录查询recordQuery函数里面。这个函数的作用就是动态刷新窗体底部小控件的数值,除了查询用这个函数,下面的几个按钮都要调用,注释在代码里面。
      • (4)跳转数字框(pageEdit)
        • 这是底部的当前页码框,从currentPage(当前页)中获取,而currentPage又是来自getPageCount函数的变量totalPage(总页数)。
      • (5)总页数(totalPage)
        • 看代码里面的注释
      • (6)跳转按钮(jumpToButton)
        • 根据第(4)条的pageEdit,点击此按钮跳转到相应的页面。
      • (7)前一页(prevButton)
        • 根据pageEdit里面的currentPage(当前页)和totalPage(总页数),判断翻页定位。
      • (8)后一页(backButton)
        • 根据pageEdit里面的currentPage(当前页)和totalPage(总页数),判断翻页定位。

5.2.3 借阅书籍(borrowBookDialog.py)

import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import qdarkstyle
import time
from PyQt5.QtSql import *


class borrowBookDialog(QDialog):
    borrow_book_success_signal = pyqtSignal()

    def __init__(self, StudentId, parent=None):
        super(borrowBookDialog, self).__init__(parent)
        self.studentId = StudentId
        self.setUpUI()
        self.setWindowModality(Qt.WindowModal)
        self.setWindowTitle("借阅书籍")

    def setUpUI(self):
        # 书名,书号,作者,分类,添加数量.出版社,出版日期
        # 书籍分类:哲学类、社会科学类、政治类、法律类、军事类、经济类、文化类、教育类、体育类、语言文字类、艺术类、历史类、地理类、天文学类、生物学类、医学卫生类、农业类
        BookCategory = ["哲学", "社会科学", "政治", "法律", "军事", "经济", "文化", "教育", "体育", "语言文字", "艺术", "历史"
            , "地理", "天文学", "生物学", "医学卫生", "农业"]
        self.resize(300, 400)
        self.layout = QFormLayout()
        self.setLayout(self.layout)

        # Label控件
        self.borrowStudentLabel = QLabel("借 阅 人:")
        self.borrowStudentIdLabel = QLabel(self.studentId)
        self.titlelabel = QLabel("  借阅书籍")
        self.bookNameLabel = QLabel("书    名:")
        self.bookIdLabel = QLabel("书    号:")
        self.authNameLabel = QLabel("作    者:")
        self.categoryLabel = QLabel("分    类:")
        self.publisherLabel = QLabel("出 版 社:")
        self.publishDateLabel = QLabel("出版日期:")

        # button控件
        self.borrowBookButton = QPushButton("确认借阅")

        # lineEdit控件
        self.bookNameEdit = QLineEdit()
        self.bookIdEdit = QLineEdit()
        self.authNameEdit = QLineEdit()
        self.categoryComboBox = QComboBox()
        self.categoryComboBox.addItems(BookCategory)
        self.publisherEdit = QLineEdit()
        self.publishTime = QLineEdit()

        self.bookNameEdit.setMaxLength(10)
        self.bookIdEdit.setMaxLength(6)
        self.authNameEdit.setMaxLength(10)
        self.publisherEdit.setMaxLength(10)

        # 添加进formlayout
        self.layout.addRow("", self.titlelabel)
        self.layout.addRow(self.borrowStudentLabel, self.borrowStudentIdLabel)
        self.layout.addRow(self.bookNameLabel, self.bookNameEdit)
        self.layout.addRow(self.bookIdLabel, self.bookIdEdit)
        self.layout.addRow(self.authNameLabel, self.authNameEdit)
        self.layout.addRow(self.categoryLabel, self.categoryComboBox)
        self.layout.addRow(self.publisherLabel, self.publisherEdit)
        self.layout.addRow(self.publishDateLabel, self.publishTime)
        self.layout.addRow("", self.borrowBookButton)

        # 设置字体
        font = QFont()
        font.setPixelSize(20)
        self.titlelabel.setFont(font)
        font.setPixelSize(16)
        self.borrowStudentIdLabel.setFont(font)
        font.setPixelSize(14)
        self.borrowStudentLabel.setFont(font)
        self.bookNameLabel.setFont(font)
        self.bookIdLabel.setFont(font)
        self.authNameLabel.setFont(font)
        self.categoryLabel.setFont(font)
        self.publisherLabel.setFont(font)
        self.publishDateLabel.setFont(font)

        self.bookNameEdit.setFont(font)
        self.bookNameEdit.setReadOnly(True)
        self.bookNameEdit.setStyleSheet("background-color:#363636")
        self.bookIdEdit.setFont(font)
        self.authNameEdit.setFont(font)
        self.authNameEdit.setReadOnly(True)
        self.authNameEdit.setStyleSheet("background-color:#363636")
        self.publisherEdit.setFont(font)
        self.publisherEdit.setReadOnly(True)
        self.publisherEdit.setStyleSheet("background-color:#363636")
        self.publishTime.setFont(font)
        self.publishTime.setStyleSheet("background-color:#363636")
        self.categoryComboBox.setFont(font)
        self.categoryComboBox.setStyleSheet("background-color:#363636")

        # button设置
        font.setPixelSize(16)
        self.borrowBookButton.setFont(font)
        self.borrowBookButton.setFixedHeight(32)
        self.borrowBookButton.setFixedWidth(140)

        # 设置间距
        self.titlelabel.setMargin(8)
        self.layout.setVerticalSpacing(10)
        self.borrowBookButton.clicked.connect(self.borrowButtonClicked)
        self.bookIdEdit.textChanged.connect(self.bookIdEditChanged)
        self.bookIdEdit.returnPressed.connect(self.borrowButtonClicked)

    def borrowButtonClicked(self):
        # 获取书号,书号为空或不存在库中,则弹出错误
        # 向Book_User表插入记录,更新User表以及Book表
        BookId = self.bookIdEdit.text()
        # BookId为空的处理
        if BookId == "":
            print(QMessageBox.warning(self, "警告", "你所要借的书不存在,请查看输入", QMessageBox.Yes, QMessageBox.Yes))
            return
        # 打开数据库
        db = QSqlDatabase.addDatabase("QSQLITE")
        db.setDatabaseName('./db/LibraryManagement.db')
        db.open()
        query = QSqlQuery()
        # 如果BookId不存在
        sql = "SELECT * FROM Book WHERE BookId='%s'" % BookId
        query.exec_(sql)
        if not query.next():
            print(QMessageBox.warning(self, "警告", "你所要借的书不存在,请查看输入", QMessageBox.Yes, QMessageBox.Yes))
            return

        # 借书上限5本
        sql = "SELECT COUNT(StudentId) FROM User_Book WHERE StudentId='%s' AND BorrowState=1" % (
            self.studentId)
        query.exec_(sql)
        if (query.next()):
            borrowNum = query.value(0)
            if (borrowNum == 5):
                QMessageBox.warning(self, "警告", "您借阅的书达到上限(5本),借书失败!", QMessageBox.Yes, QMessageBox.Yes)
                return
        # 不允许重复借书
        sql = "SELECT COUNT(StudentId) FROM User_Book WHERE  StudentId='%s' AND BookId='%s' AND BorrowState=1" % (
        self.studentId, BookId)
        query.exec_(sql)
        if (query.next() and query.value(0)):
            QMessageBox.warning(self, "警告", "您已经借阅了本书并尚未归还,借阅失败!", QMessageBox.Yes, QMessageBox.Yes)
            return
        # 更新User表
        sql = "UPDATE User SET TimesBorrowed=TimesBorrowed+1,NumBorrowed=NumBorrowed+1 WHERE StudentId='%s'" % self.studentId
        query.exec_(sql)
        db.commit()
        # 更新Book表
        sql = "UPDATE Book SET NumCanBorrow=NumCanBorrow-1,NumBorrowed=NumBorrowed+1 WHERE BookId='%s'" % BookId
        query.exec_(sql)
        db.commit()
        # 插入User_Book表
        timenow = time.strftime('%Y-%m-%d', time.localtime(time.time()))
        sql = "INSERT INTO User_Book VALUES ('%s','%s','%s',NULL,1)" % (self.studentId, BookId, timenow)
        print(sql)
        query.exec_(sql)
        db.commit()
        print(QMessageBox.information(self, "提示", "借阅成功!", QMessageBox.Yes, QMessageBox.Yes))
        self.borrow_book_success_signal.emit()
        self.close()
        return

    def bookIdEditChanged(self):
        bookId = self.bookIdEdit.text()
        if bookId == "":
            self.bookNameEdit.clear()
            self.publisherEdit.clear()
            self.authNameEdit.clear()
            self.publishTime.clear()
        db = QSqlDatabase.addDatabase("QSQLITE")
        db.setDatabaseName('./db/LibraryManagement.db')
        db.open()
        query = QSqlQuery()
        sql = "SELECT * FROM Book WHERE BookId='%s'" % (bookId)
        query.exec_(sql)
        # 查询对应书号,如果存在就更新form
        if (query.next()):
            self.bookNameEdit.setText(query.value(0))
            self.authNameEdit.setText(query.value(2))
            self.categoryComboBox.setCurrentText(query.value(3))
            self.publisherEdit.setText(query.value(4))
            self.publishTime.setText(query.value(5))
        return


if __name__ == "__main__":
    app = QApplication(sys.argv)
    app.setWindowIcon(QIcon("./images/MainWindow_1.png"))
    app.setStyleSheet(qdarkstyle.load_stylesheet_pyqt5())
    mainWindow = borrowBookDialog("PB15000135")
    mainWindow.show()
    sys.exit(app.exec_())


  • 代码分析
    • 首先,这个对话框布局很简单。除了8个Label和5个LineEdit就是1个下拉列表框加最底下1个按钮。这些小控件自上而下,分两列放置到表单布局器QFormLayout。字体、样式、位置、大小、间距都设定好后,放入布局。最后再绑定3个信号槽函数,一个按钮绑定按钮单击方法、书籍ID文本框绑定两个槽,信号是文字改变bookIdEditChanged和回车及焦点离开都如同触发按钮borrowButtonClicked。太啰嗦,画图吧。 在这里插入图片描述
    • 然后,再解释两个槽函数。borrowButtonClicked定义书籍ID不为空的时候,加上if条件语句,再应用数据库sql查询语句,一旦借阅的书籍存在,点击“确认借阅”,再应用数据库update更新用户表User和书籍表Book的信息(还设定了用户最多借阅5本的限制条件)。bookIdEditChanged的作用就是通过书籍ID的文本框是否有信息、信息是否正确,同时触发其它控件里的信息,然后用回车、点击按钮等方法作出上面borrowButtonClicked函数相同的效果。

5.2.4 借阅状态(BorrowStatusViewer.py)

import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import qdarkstyle
import time
from PyQt5.QtSql import *


class returnBookDialog(QDialog):
    return_book_success_signal=pyqtSignal()
    def __init__(self, StudentId, parent=None):
        super(returnBookDialog, self).__init__(parent)
        self.studentId = StudentId
        self.setUpUI()
        self.setWindowModality(Qt.WindowModal)
        self.setWindowTitle("归还书籍")

    def setUpUI(self):
        # 书名,书号,作者,分类,添加数量.出版社,出版日期
        # 书籍分类:哲学类、社会科学类、政治类、法律类、军事类、经济类、文化类、教育类、体育类、语言文字类、艺术类、历史类、地理类、天文学类、生物学类、医学卫生类、农业类
        BookCategory = ["哲学", "社会科学", "政治", "法律", "军事", "经济", "文化", "教育", "体育", "语言文字", "艺术", "历史"
            , "地理", "天文学", "生物学", "医学卫生", "农业"]
        self.resize(300, 400)
        self.layout = QFormLayout()
        self.setLayout(self.layout)

        # Label控件
        self.returnStudentLabel = QLabel("还 书 人:")
        self.returnStudentIdLabel = QLabel(self.studentId)
        self.titlelabel = QLabel("  归还书籍")
        self.bookNameLabel = QLabel("书    名:")
        self.bookIdLabel = QLabel("书    号:")
        self.authNameLabel = QLabel("作    者:")
        self.categoryLabel = QLabel("分    类:")
        self.publisherLabel = QLabel("出 版 社:")
        self.publishDateLabel = QLabel("出版日期:")

        # button控件
        self.returnBookButton = QPushButton("确认归还")

        # lineEdit控件
        self.bookNameEdit = QLineEdit()
        self.bookIdEdit = QLineEdit()
        self.authNameEdit = QLineEdit()
        self.categoryComboBox = QComboBox()
        self.categoryComboBox.addItems(BookCategory)
        self.publisherEdit = QLineEdit()
        self.publishTime = QLineEdit()

        self.bookNameEdit.setMaxLength(10)
        self.bookIdEdit.setMaxLength(6)
        self.authNameEdit.setMaxLength(10)
        self.publisherEdit.setMaxLength(10)

        # 添加进formlayout
        self.layout.addRow("", self.titlelabel)
        self.layout.addRow(self.returnStudentLabel, self.returnStudentIdLabel)
        self.layout.addRow(self.bookNameLabel, self.bookNameEdit)
        self.layout.addRow(self.bookIdLabel, self.bookIdEdit)
        self.layout.addRow(self.authNameLabel, self.authNameEdit)
        self.layout.addRow(self.categoryLabel, self.categoryComboBox)
        self.layout.addRow(self.publisherLabel, self.publisherEdit)
        self.layout.addRow(self.publishDateLabel, self.publishTime)
        self.layout.addRow("", self.returnBookButton)

        # 设置字体
        font = QFont()
        font.setPixelSize(20)
        self.titlelabel.setFont(font)
        font.setPixelSize(16)
        self.returnStudentIdLabel.setFont(font)
        font.setPixelSize(14)
        self.returnStudentLabel.setFont(font)
        self.bookNameLabel.setFont(font)
        self.bookIdLabel.setFont(font)
        self.authNameLabel.setFont(font)
        self.categoryLabel.setFont(font)
        self.publisherLabel.setFont(font)
        self.publishDateLabel.setFont(font)

        self.bookNameEdit.setFont(font)
        self.bookNameEdit.setReadOnly(True)
        self.bookNameEdit.setStyleSheet("background-color:#363636")
        self.bookIdEdit.setFont(font)
        self.authNameEdit.setFont(font)
        self.authNameEdit.setReadOnly(True)
        self.authNameEdit.setStyleSheet("background-color:#363636")
        self.publisherEdit.setFont(font)
        self.publisherEdit.setReadOnly(True)
        self.publisherEdit.setStyleSheet("background-color:#363636")
        self.publishTime.setFont(font)
        self.publishTime.setStyleSheet("background-color:#363636")
        self.categoryComboBox.setFont(font)
        self.categoryComboBox.setStyleSheet("background-color:#363636")

        # button设置
        font.setPixelSize(16)
        self.returnBookButton.setFont(font)
        self.returnBookButton.setFixedHeight(32)
        self.returnBookButton.setFixedWidth(140)

        # 设置间距
        self.titlelabel.setMargin(8)
        self.layout.setVerticalSpacing(10)
        self.returnBookButton.clicked.connect(self.returnButtonClicked)
        self.bookIdEdit.textChanged.connect(self.bookIdEditChanged)

    def returnButtonClicked(self):
        # 获取书号,书号为空或并未借阅,则弹出错误
        # 更新Book_User表User表以及Book表
        BookId = self.bookIdEdit.text()
        # BookId为空的处理
        if (BookId == ""):
            print(QMessageBox.warning(self, "警告", "你所要还的书不存在,请查看输入", QMessageBox.Yes, QMessageBox.Yes))
            return
        # 打开数据库
        db = db = QSqlDatabase.addDatabase("QSQLITE")
        db.setDatabaseName('./db/LibraryManagement.db')
        db.open()
        query = QSqlQuery()
        # 如果未借阅
        sql = "SELECT * FROM User_Book WHERE StudentId='%s' AND BookId='%s' AND BorrowState=1" %(self.studentId,BookId)
        query.exec_(sql)
        if (not query.next()):
            print(QMessageBox.information(self, "提示", "您并未借阅此书,故无需归还", QMessageBox.Yes, QMessageBox.Yes))
            return
        # 更新User表
        sql = "UPDATE User SET NumBorrowed=NumBorrowed-1 WHERE StudentId='%s'" % self.studentId
        query.exec_(sql)
        db.commit()
        # 更新Book表
        sql = "UPDATE Book SET NumCanBorrow=NumCanBorrow+1 WHERE BookId='%s'" % BookId
        query.exec_(sql)
        db.commit()
        # 更新User_Book表
        timenow = time.strftime('%Y-%m-%d', time.localtime(time.time()))
        sql = "UPDATE User_Book SET ReturnTime='%s',BorrowState=0 WHERE StudentId='%s' AND BookId='%s' AND BorrowState=1" % (timenow,self.studentId,BookId)
        query.exec_(sql)
        db.commit()
        print(QMessageBox.information(self, "提示", "归还成功!", QMessageBox.Yes, QMessageBox.Yes))
        self.return_book_success_signal.emit()
        self.close()
        return

    def bookIdEditChanged(self):
        bookId = self.bookIdEdit.text()
        if (bookId == ""):
            self.bookNameEdit.clear()
            self.publisherEdit.clear()
            self.authNameEdit.clear()
            self.publishTime.clear()
        db = QSqlDatabase.addDatabase("QSQLITE")
        db.setDatabaseName('./db/LibraryManagement.db')
        db.open()
        query = QSqlQuery()
        # 在User_Book表中找借阅记录,如果存在借阅,则更新form内容
        sql = "SELECT * FROM User_Book WHERE StudentId='%s' AND BookId='%s' AND BorrowState=1" % (
            self.studentId, bookId)
        query.exec_(sql)
        if (query.next()):
            # 更新form内容
            sql = "SELECT * FROM Book WHERE BookId='%s'" % (bookId)
            query.exec_(sql)
            # 查询对应书号,如果存在就更新form
            if (query.next()):
                self.bookNameEdit.setText(query.value(0))
                self.authNameEdit.setText(query.value(2))
                self.categoryComboBox.setCurrentText(query.value(3))
                self.publisherEdit.setText(query.value(4))
                self.publishTime.setText(query.value(5))
        return


if __name__ == "__main__":
    app = QApplication(sys.argv)
    app.setWindowIcon(QIcon("./images/MainWindow_1.png"))
    app.setStyleSheet(qdarkstyle.load_stylesheet_pyqt5())
    mainMindow = returnBookDialog("PB15000135")
    mainMindow.show()
    sys.exit(app.exec_())

  • 代码分析
  • 这个对话框和借阅书籍的对话框总体一样。包括布局和槽函数,大同小异。只是归还的槽函数中,更改了归还的书籍是否存在,是否借阅过,也是在if语句的条件判断中。

5.2.5 归还书籍(returnBookDialog.py)

import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import Qt
import qdarkstyle
from PyQt5.QtSql import *


class BorrowStatusViewer(QWidget):
    def __init__(self, studentId):
        super(BorrowStatusViewer, self).__init__()
        self.resize(700, 500)
        self.studentId = studentId
        self.setWindowTitle("欢迎使用图书馆管理系统")
        self.setUpUI()

    def setUpUI(self):
        self.db = QSqlDatabase.addDatabase("QSQLITE")
        self.db.setDatabaseName('./db/LibraryManagement.db')
        self.db.open()
        # 分为两块,上方是已借未归还书,下方是已归还书
        self.layout = QVBoxLayout(self)
        # Label设置
        self.borrowedLabel = QLabel("未归还:")
        self.returnedLabel = QLabel("已归还:")
        self.borrowedLabel.setFixedHeight(32)
        self.borrowedLabel.setFixedWidth(60)
        self.returnedLabel.setFixedHeight(32)
        self.returnedLabel.setFixedWidth(60)
        font = QFont()
        font.setPixelSize(18)
        self.borrowedLabel.setFont(font)
        self.returnedLabel.setFont(font)

        # Table和Model
        self.borrowedTableView = QTableView()
        self.borrowedTableView.horizontalHeader().setStretchLastSection(True)
        self.borrowedTableView.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
        self.borrowedTableView.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.returnedTableView = QTableView()
        self.returnedTableView.horizontalHeader().setStretchLastSection(True)
        self.returnedTableView.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
        self.returnedTableView.setEditTriggers(QAbstractItemView.NoEditTriggers)

        self.borrowedQueryModel = QSqlQueryModel()
        self.returnedQueryModel = QSqlQueryModel()
        self.borrowedTableView.setModel(self.borrowedQueryModel)
        self.returnedTableView.setModel(self.returnedQueryModel)
        self.borrowedQuery()
        self.borrowedQueryModel.setHeaderData(0, Qt.Horizontal, "书名")
        self.borrowedQueryModel.setHeaderData(1, Qt.Horizontal, "书号")
        self.borrowedQueryModel.setHeaderData(2, Qt.Horizontal, "作者")
        self.borrowedQueryModel.setHeaderData(3, Qt.Horizontal, "分类")
        self.borrowedQueryModel.setHeaderData(4, Qt.Horizontal, "出版社")
        self.borrowedQueryModel.setHeaderData(5, Qt.Horizontal, "出版时间")
        self.borrowedQueryModel.setHeaderData(6, Qt.Horizontal, "借出时间")

        self.returnedQuery()
        self.returnedQueryModel.setHeaderData(0, Qt.Horizontal, "书名")
        self.returnedQueryModel.setHeaderData(1, Qt.Horizontal, "书号")
        self.returnedQueryModel.setHeaderData(2, Qt.Horizontal, "作者")
        self.returnedQueryModel.setHeaderData(3, Qt.Horizontal, "分类")
        self.returnedQueryModel.setHeaderData(4, Qt.Horizontal, "出版社")
        self.returnedQueryModel.setHeaderData(5, Qt.Horizontal, "出版时间")
        self.returnedQueryModel.setHeaderData(6, Qt.Horizontal, "借阅时间")
        self.returnedQueryModel.setHeaderData(7, Qt.Horizontal, "归还时间")

        self.layout.addWidget(self.borrowedLabel)
        self.layout.addWidget(self.borrowedTableView)
        self.layout.addWidget(self.returnedLabel)
        self.layout.addWidget(self.returnedTableView)
        return

    def borrowedQuery(self):
        sql = "SELECT Book.BookName,Book.BookId,Auth,Category,Publisher,PublishTime,BorrowTime  FROM Book,User_Book " \
              "WHERE Book.BookId=User_Book.BookId AND User_Book.BorrowState=1 AND StudentId='%s'" % self.studentId
        self.borrowedQueryModel.setQuery(sql)
        return

    def returnedQuery(self):
        sql = "SELECT Book.BookName,Book.BookId,Auth,Category,Publisher,PublishTime,BorrowTime,ReturnTime  FROM Book," \
              "User_Book WHERE Book.BookId=User_Book.BookId AND User_Book.BorrowState=0 AND StudentId='%s'" % \
              self.studentId
        self.returnedQueryModel.setQuery(sql)
        return


if __name__ == "__main__":
    app = QApplication(sys.argv)
    app.setWindowIcon(QIcon("./images/MainWindow_1.png"))
    app.setStyleSheet(qdarkstyle.load_stylesheet_pyqt5())
    mainMindow = BorrowStatusViewer("PB15000135")
    mainMindow.show()
    sys.exit(app.exec_())


  • 代码分析
  • 这个页面展示了当前用户借阅图书的情况。分为两块,上方是已借未归还书,下方是已归还书。用了一个垂直布局,然后上下分别放置了两个QTableView视图表。
  • 数据库sql语句用了SELECT,然后未还的搜索User和User_Book两个表,WHERE语句是根据图书ID、借阅状态BorrowState=1和当前执行搜索的学生ID。
  • 已归还的图书视图,也用了SELECT,然后搜索User和User_Book两个表,WHERE语句是根据图书ID、借阅状态BorrowState=0和当前执行搜索的学生ID。
  • 这个页面没有信号槽函数,不过学习了数据库搜索视图,放置到QTableView中。