第七章 借书与还书(二)
https://gitee.com/mayonaka/LibraryManageSystem
百度云:https://pan.baidu.com/s/1G95yPyGG080b6yXcjc8B0g
提取码:4q8b
上一章我们建了一个基类用来作为还书界面与借书界面的基类,这一章,我们就来具体实现还书界面和借书界面。
首先我们来思考一下借书和还书的流程,,借书的时候打开借书界面,选择要借的书,然后点确定按钮;还书的时候,打开还书界面,选择要换的书,然后点确定。两个操作在实现方面也很类似,借书操作要把借的书记录下来,最后保存到数据库中,还书操作要把还的书记录下来,最后从数据库中把还的书删掉。
下面开始编写借书界面:
1. 新建一个C++类,类名为BorrowBook。
2. 让BorrowBook类public继承Form类,在BorrowBook.cpp 文件中添加#include”ui_form.h”,并把Form类的ui属性改为protected,否则在BorrowBook类中访问不了ui变量。
3. 类的属性:
a) userBook:储存用户已借阅的书,在完成借书操作后,会把借阅的书添加到userBook中去。
b) borrowBook:用来储存借书操作中借阅的书,方便之后回写到数据库中去。如果没有这个变量,在回写操作中就要遍历userBook链表以确定哪本书是借的。
c) book:用来储存了所有书的信息。
4. 类的方法:
a) ShowAllBooks:显示所有的书,让用户来借。
b) BorrowBookSlot:用户在点击确定按钮后,进入此函数,记录借的书。
注意要在类的声明中加Q_OBJECT宏,否则无法在父类和子类间进行信号与槽的连接。
#include "form.h"
#include "book.h"
#include "userbook.h"
class BorrowBook : public Form
{
Q_OBJECT
public:
// node储存的是用户已借阅的书
// node2用来储存当前操作借的书
// node3储存所有书的信息
BorrowBook(UserBook* node, UserBook* node2, Book* node3);
// 显示所有的书
void ShowAllBooks();
public slots:
// 当用户在借书界面点击确定按钮时,调用此函数
void BorrowBookSlot();
protected:
Book* book = NULL;
UserBook* userBook = NULL;
UserBook* borrowBook = NULL;
};
具体的实现:
1. 在构造函数中,先设置一下Tree Widget有几列,并为每一列设置一下标题。然后把确认按钮与BorrowBookSlot槽连接起来。
2. ShowAllBooks:遍历book链表,把所有的书的信息显示在Tree Widget中,每一行都要添加一个复选框,让用户来选择借阅的书。
3. BorrowBookSlot:遍历Tree Widget的每一行,检查复选框有没有被选中,如果被选中了,就添加到borrowBook中.
#include <QDate>
#include <QDebug>
#include <QMessageBox>
#include "borrowbook.h"
#include "ui_form.h"
BorrowBook::BorrowBook(UserBook* node, UserBook* node2, Book* node3)
{
this->setWindowTitle(QString::fromLocal8Bit("借书"));
// 为Tree Widget设置有多少列,并设置每一列的名称
// 设置Tree Widget共四列
ui->treeWidget->setColumnCount(4);
QStringList header;
header << QString::fromLocal8Bit("书籍编号")
<< QString::fromLocal8Bit("书籍名称")
<< QString::fromLocal8Bit("书籍作者")
<< QString::fromLocal8Bit("书籍类型");
// 设置Tree Widget每一列的标题
ui->treeWidget->setHeaderLabels(header);
this->userBook = node;
this->borrowBook = node2;
this->book = node3;
// 把确定按钮与借书操作连接起来
QObject::connect(ui->confirmPushButton, SIGNAL(clicked()),
this, SLOT(BorrowBookSlot()));
}
void BorrowBook::BorrowBookSlot()
{
// 遍历Tree Widget的每一行
// topLevelItemCount函数用于获得Tree Widget有多少行
for (int i = 0; i < ui->treeWidget->topLevelItemCount(); i++)
{
QTreeWidgetItem* item = ui->treeWidget->topLevelItem(i);
// 判断改行的复选框是否被选中
// 如果选中,就添加到userBook,borrowBook中
if (item->checkState(0) == Qt::Checked)
{
UserBookType* node = new UserBookType();
node->SetId(this->userBook->userId);
node->SetBookId(item->text(0).toInt());
QDate date;
date = date.currentDate();
qDebug() << date.year() << " " << date.month() << " " << endl;
node->SetYear(date.year());
node->SetMonth(date.month());
node->SetDay(date.day());
this->borrowBook->AddNode(node);
// 把复选框置回到未选中的状态
item->setCheckState(0, Qt::Unchecked);
}
}
this->close();
}
void BorrowBook::ShowAllBooks()
{
if (this->book == NULL)
{
return;
}
// 获得头节点
BookType* node = (BookType*)this->book->GetNode(-1);
// 获得第一个节点
node = (BookType*)node->GetNext();
// 遍历整个链表,把所有book的信息显示到Tree Widget上
while (node != NULL)
{
QTreeWidgetItem* item = new QTreeWidgetItem();
item->setText(0, QString::number(node->GetId()));
item->setText(1, node->GetName());
item->setText(2, node->GetAuthor());
item->setText(3, node->GetCategory());
// 设置item的标志
// ItemIsEnabled设置item可用
// ItemIsSelectable设置item可选
// ItemIsUserCheckable设置item有复选框
item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable
| Qt::ItemIsUserCheckable);
// 设置item的初始状态
// 第一个参数表示要设置哪一列
// 第二个参数设置初始状态,我们设置为未选中
item->setCheckState(0, Qt::Unchecked);
// 把item添加到Tree Widget中
ui->treeWidget->insertTopLevelItem(0, item);
node = (BookType*)node->GetNext();
}
}
虽然借书的界面和基本逻辑都建立完了,但还是有两个问题:
1. 当用户点击确定按钮后,虽然会调用BorrowBookSLot函数,把借的书存到borrowBook链表里,但并没有存到数据库中。
2. 在用户点击确认按钮后,借书界面关闭,回到user界面,但user界面所显示的书并没有发生变化。
要解决两个问题也很简单:
1. Qt提供了一个函数closeEvent,这个函数可以拦截关闭事件,让我们可以自己处理关闭时间,首先在头文件borrowbook.h里添加一下函数的声明。然后在closeEvent函数里实现保存的操作.
注意:这里有一个地方比较容易犯错,我们实现的链表在添加节点的函数(AddNode函数)中使用的参数是指针,当添加节点的时候,添加的节点不要同时添加到两个链表中,具体的分析过程我在另一篇博客中介绍了。点击打开链接
// borrowbook.h
// 拦截关闭事件
void closeEvent(QCloseEvent* e);
// 关闭事件处理程序
void BorrowBook::closeEvent(QCloseEvent *)
{
// 弹出对话框,询问是否保存
QMessageBox::StandardButton msg = QMessageBox::information(this,
tr("Prompt"),
tr("Save You Book?"),
QMessageBox::Save,
QMessageBox::Cancel);
switch (msg)
{
case QMessageBox::Save:
{
// 把借的书添加到userBook链表中
UserBookType* node = (UserBookType*)this->borrowBook->GetNode(-1);
node = (UserBookType*)node->GetNext();
while (node != NULL)
{
UserBookType* node2 = new UserBookType();
node2->SetId(node->GetId());
node2->SetBookId(node->GetBookId());
node2->SetYear(node->GetYear());
node2->SetMonth(node->GetMonth());
node2->SetDay(node->GetDay());
this->userBook->AddNode(node2);
node = (UserBookType*)node->GetNext();
}
// 把borrowBook链表中的信息添加到数据库中
this->borrowBook->Save();
} break;
case QMessageBox::Cancel:
{
this->borrowBook->Clear();
} break;
default:
break;
}
}
在函数中我们使用了Clear函数,这是新给LinerList新添加的函数。
void LinerList::Clear()
{
NodeType* preNode = this->list->GetNext();
NodeType* nextNode = this->list->GetNext();
while (nextNode != NULL)
{
preNode = nextNode;
nextNode = nextNode->GetNext();
delete preNode;
}
this->length = 0;
this->list->SetNext(NULL);
}
2. 为了实现在用户借书之后刷新user界面,需要给user界面一个信号,当user界面接到这个信号时,就刷新界面。这里有两种方法:第一种方法是让借书界面关闭后自动销毁,这样就会产生一个destroyed的信号,user界面接到这个信号就刷新。第二个方法就是自己定义一个信号,不过这里并没有这个必要。
// userinterface.cpp
// 打开借书界面
void UserInterface::BorrowBookSlot()
{
BorrowBook* w = new BorrowBook(this->userBook, this->borrowBook, this->book);
// 设置当close时销毁,发出destoryed信号
w->setAttribute(Qt::WA_DeleteOnClose);
// 当收到借书界面的destroyed信号时,调用ShowAllUserBooks函数
QWidget::connect(w, SIGNAL(destroyed()), this, SLOT(ShowAllUserBooks()));
w->show();
w->ShowAllBooks();
}