学生信息管理系统,代码与设计流程详解——C++(Qt)
前言
本篇文章主要讲解了整个学生系统的设计流程和思路,基于C++和Qt图形界面。是大二上学期的C++课程设计,主要使用类和链表以及Qt的基础控件的使用,可能会存在一些漏洞,欢迎相互学习和指正。
0.部分功能界面展示
- 初始界面
- 导入界面
- 打开界面
- 查找界面
- 统计界面
1.学生类
class CStudent
{
public:
/*学生信息(数据域)*/
int ID; //学号
QString name; //名字
QString gender; //性别
int age; //年龄
QString college; //学院
float Math; //高数成绩
float English; //英语成绩
float Cpp; //C++成绩
CStudent *next; //指针域
CStudent(); //构造函数
//CStudent(string na,float math);
~CStudent(); //析构函数
QString Jud_Grade(float gr);
CStudent operator=(CStudent &s); //运算符重载
};
CStudent CStudent::operator=(CStudent &s)
{
CStudent s1;
s1.ID = s.ID;
s1.name = s.name;
s1.gender = s.gender;
s1.age = s.age;
s1.college = s.college;
s1.Math = s.Math;
s1.English = s.English;
s1.Cpp = s.Cpp;
return s1;
}
QString CStudent::Jud_Grade(float gr)
{
if (gr>=90.0&&gr<=100.0)
return "A";
if (gr >= 80.0&&gr < 90.0)
return "B";
if (gr >= 70.0&&gr < 80.0)
return "C";
if (gr >= 60.0&&gr < 70.0)
return "D";
if (gr >= 0&&gr < 60.0)
return "F";
return "0";
}
- 主要是一些相关的学生信息内容,值得关注的是
CStudent *next;
因为采用链表存储学生信息,其中一种较为方便的方法就是把学生类作为结点,指针域也直接指向学生。 Jud_Grade
和operator=(CStudent &s)
后续会用到- 全都是public类是因为后面可以直接在界面对学生信息进行修改。
2.链表类
class CStudentlist
{
private:
CStudent *head; //链表头
public:
CStudentlist(); //构造函数 链表初始化
~CStudentlist(); //析构函数
int Stu_num(); //学生个数 “length()”
CStudent *Stu_find(int i); //查找学生 “find()”
void Stu_add(CStudent *stu); //添加学生 “insert()"
void Stu_del(int i); //删除学生 “delete()”
void Stu_show(); //显示信息
//void Createlist(); //从前建立链表
};
CStudentlist::CStudentlist()
{
/*初始化结点*/
head = new CStudent;
if (!head)
{
cout << "创建结点失败" << endl;
}
head->next = NULL; //头结点指针域为空
}
CStudentlist::~CStudentlist()
{
/*释放结点*/
CStudent *p = head->next;
CStudent *temp;
while (p != NULL)
{
temp = p;
p = p->next;
delete temp;
}
delete head;
}
int CStudentlist::Stu_num()
{
CStudent *s = head;
int count = 0;
if (s->next == NULL) return 0;
else count = 1;
s = s->next; //指向首元结点
while (s->next != NULL)
{
count++;
s = s->next;
}
return count; //返回结点个数即学生个数
}
CStudent *CStudentlist::Stu_find(int i)
{
CStudent *s = head;
int j = 0;
while ((s->next != NULL) && (j < i))
{
s = s->next;
j++;
}
if (i == j) return s;
else
{
cout << "找不到该学生!" << endl;
return 0;
}
}
void CStudentlist::Stu_add(CStudent *stu) //默认从最后添加结点
{
CStudent *s1 = head;
CStudent *s2 = new CStudent; //申请新学生
while (s1->next != NULL)
{
s1 = s1->next;
}
if (!s2) exit(1); //申请失败则退出
else
{
s2 = stu; //此时用到运算符重载
s2->next = NULL;
s1->next = s2;
}
}
void CStudentlist::Stu_del(int i)
{
CStudent *s1 = Stu_find(i - 1); //找i-1结点
if ((s1 != NULL) && (s1->next != NULL))
{
CStudent *s2 = s1->next; //调整指针域
s1->next = s2->next;
delete s2;
}
else
cout << "没有该结点!" << endl;
}
void CStudentlist::Stu_show()
{
if (Stu_num() > 0)
{
CStudent *s = head->next;
for (int i = 1;i <= Stu_num();i++)
{
qDebug() << "第" << i << "名学生为:" << s->name << endl;
s = s->next;
}
}
}
- 一些链表的基础功能函数,值得注意的是在函数内采用new CStudent之后,由于CStudentlist类的析构函数最后会全部释放掉,若在函数内
delete
又或者在学生类的析构函数释放,最后会由于重复释放而产生报错。 - 主要是要掌握一定的链表基础概念之后,把链表函数里面的结点修改成学生类即可。
3.初始界面和新建学生
- 首先使用Qt的设计器设计初始界面,简单的设计了三个按钮,背景图片比较中二不要介意哈哈哈。
class QtStu_System : public QWidget
{
Q_OBJECT
public:
QtStu_System(QWidget *parent = Q_NULLPTR);
~QtStu_System();
void create_new(); //创建窗口
void widget_return(); //返回窗口
void file_open(); //文件打开
private:
Ui::QtStu_SystemClass *ui;
QtStu_list *ne_list;
};
- 然后继续使用设计器设计主界面
- 其实就是表格加按钮
- 初始界面代码
extern CStudentlist Stu; int a = 0; QtStu_System::QtStu_System(QWidget *parent) : QWidget(parent) { ui->setupUi(this); setWindowTitle("Stu_System"); connect(ui->Button_close, &QPushButton::clicked, this, &QtStu_System::close); //关闭主窗口 ne_list = new QtStu_list; connect(ui->Button_new, &QPushButton::clicked, this, &QtStu_System::create_new); //新建窗口 connect(ui->Button_open, &QPushButton::clicked, this,&QtStu_System::file_open); connect(ne_list,&QtStu_list::re_signal,this, &QtStu_System::widget_return); } QtStu_System::~QtStu_System() { delete ne_list; } void QtStu_System::create_new() { int stu_num = Stu.Stu_num(); //得到学生数量 for (int i = 1;i <= stu_num;i++) { Stu.Stu_del(1); } this->hide(); //本窗口关闭 ne_list->show(); //新建窗口打开 ne_list->Set_Text(); } void QtStu_System::widget_return() { ne_list->hide(); //新建框窗口关闭 this->show(); //本窗口打开 }
- 关键就是在新建的时候,确保把链表中的学生全部删除,然后关闭本窗口,打开主界面。
4.打开文档导入学生
- 我的这个系统就两个创建的模式,一是新建,二就是导入提前写好的txt文档。图片见第0步
- 相关代码
void QtStu_System::file_open() { //CStudentlist Stu; int stu_num=0; //学生数量 int id; //学号 QString na; //名字 QString ge; //性别 int ag; //年龄 QString co; //学院 float Ma; //高数成绩 float En; //英语成绩 float Cp; //C++成绩 QString path = QFileDialog::getOpenFileName(this, "save", "../", "BIN(*.bin)");//TXT(*.txt)||BIN(*.bin) try { if (path.isEmpty() != false) { throw a; } //关联文件对象 QFile file(path); bool isok = file.open(QIODevice::ReadOnly); if (true == isok) { //创建数据流,和file文件关联 //往数据流中读数据,相当于往文件里读数据 QDataStream stream(&file); //创建二进制流 /*通过行读取统计学生个数*/ while (stream.atEnd() == 0) { stream >> id >> na >> ge >> ag >> co >> Ma >> En >> Cp; qDebug() << id << na << ge << ag << co << Ma << En << Cp; stu_num++; CStudent *s = new CStudent; //创建新学生 (只要添加到链表,链表会自动delete) s->ID = id; s->name = na; s->gender = ge; s->age = ag; s->college = co; s->Math = Ma; s->English = En; s->Cpp = Cp; Stu.Stu_add(s); } qDebug() << stu_num; qDebug() << Stu.Stu_num(); file.close(); } this->hide(); //本窗口关闭 ne_list->show(); //新建窗口打开 ne_list->Set_Text(); //设定文件 } catch (int) { qDebug() << "打开文件失败!"; } }
- 得益于Qt的强大,你可以直接弹出文件选择的窗口选择文件导入,而控制台则要手动输入文件路径。
- 需要了解的是,我们现在还并没有把学生显示到主界面的表格上,这一步的关键是要把所有的学生信息保存在一个全局的学生链表当中
5.把学生信息放到主界面上
void QtStu_list::Set_Text()
{
int stu_num = 0;
stu_num = Stu.Stu_num(); //得到学生数量
qDebug() << "xuesheng:"<<stu_num;
ui_list->tableWidget->setRowCount(stu_num); //设置表格行数
for (int i=1;i <= stu_num;i++)
{
CStudent *s;
s = Stu.Stu_find(i);
//ui_list->tableWidget->item(i-1, 0)->setTextAlignment(Qt::AlignHCenter);
ui_list->tableWidget->setItem(i - 1, 0, new QTableWidgetItem(QString::number(s->ID)));
ui_list->tableWidget->setItem(i-1, 1, new QTableWidgetItem(s->name));
ui_list->tableWidget->setItem(i - 1, 2, new QTableWidgetItem(s->gender));
ui_list->tableWidget->setItem(i - 1, 3, new QTableWidgetItem(QString::number(s->age)));
ui_list->tableWidget->setItem(i - 1, 4, new QTableWidgetItem(s->college));
ui_list->tableWidget->setItem(i - 1, 5, new QTableWidgetItem(QString::number(s->Math)));
ui_list->tableWidget->setItem(i - 1, 6, new QTableWidgetItem(QString::number(s->English)));
ui_list->tableWidget->setItem(i - 1, 7, new QTableWidgetItem(QString::number(s->Cpp)));
//delete s;
}
}
- 先得到学生的个数,然后遍历输入表格即可。
6.设计功能:导入
- 导入的意思就是,在主界面原有学生(或空)的基础上导入新的学生,本代码不含重复处理。
void QtStu_list::Read_file()
{
int stu_num = 0; //学生数量
int id; //学号
QString na; //名字
QString ge; //性别
int ag; //年龄
QString co; //学院
float Ma; //高数成绩
float En; //英语成绩
float Cp; //C++成绩
QString path = QFileDialog::getOpenFileName(this, "save", "../", "TXT(*.txt)");//TXT(*.txt)||BIN(*.bin)
if (path.isEmpty() == false)
{
//关联文件对象
QFile file(path);
bool isok = file.open(QIODevice::ReadOnly);
if (true == isok)
{
//创建数据流,和file文件关联
//往数据流中读数据,相当于往文件里读数据
QTextStream stream(&file);
QTextCodec *codec = QTextCodec::codecForName("UTF-8"); //设置中文编码
stream.setCodec(codec);
//QDataStream stream(&file); //创建二进制流
/*通过行读取统计学生个数*/
while (stream.atEnd() == 0)
{
stream >> id >> na >> ge >> ag >> co >> Ma >> En >> Cp;
na = codec->fromUnicode(na);
ge = codec->fromUnicode(ge);
co = codec->fromUnicode(co);
qDebug() << id << na << ge << ag << co << Ma << En << Cp;
//qDebug() <<stream.readLine();
stu_num++;
CStudent *s = new CStudent; //创建新学生 (只要添加到链表,链表会自动delete)
s->ID = id;
s->name = na;
s->gender = ge;
s->age = ag;
s->college = co;
s->Math = Ma;
s->English = En;
s->Cpp = Cp;
Stu.Stu_add(s);
}
qDebug() << stu_num;
qDebug() << Stu.Stu_num();
file.close();
}
}
Set_Text();
}
- 其实代码几乎和打开文件是一样的,只是最后一句加了
Set_Text()
,原理就是把导入的学生信息先放到学生链表,然后在重新更新界面即可。
7.设计功能:添加
- 添加的功能是在主界面的表格上新建一行,然后自行在界面上输入信息。
void QtStu_list::Add_Stu()
{
ui_list->tableWidget->insertRow(Stu.Stu_num()); //表格插入一行
QTableWidgetItem *newitem = new QTableWidgetItem;
ui_list->tableWidget->setItem(Stu.Stu_num(), 0, newitem);
ui_list->tableWidget->editItem(newitem); //第一行启动编辑
//CStudent *s;
qDebug() << 111;
}
8.设计功能:删除
- 删除即删除界面上的学生。
- 首先要有一个输入学生的窗口。
- 相关代码
void QtStu_list::Del_Stu()
{
QString d_name;
bool a =0;
int i = 0;
d_name = d.get_text();
//qDebug() << d_name;
CStudent *s;
for (i = 1;i <= Stu.Stu_num();i++)
{
s = Stu.Stu_find(i);
if (d_name == s->name)
{
a = 1;
break;
}
}
if (a == 0)
{
e.show();
}
if (a)
{
Stu.Stu_del(i); //链表删除学生
Set_Text();
d.hide();
}
}
- 其实抛开图形界面,真的没有特别复杂。就是找到这个学生的名字,把他在学生的链表中删除,然后在更新界面即可。
- 当然,找不到学生你也可以新创一个窗口
e.show()
,我这里就不展示了。
9.设计功能:查找
- 查找即查找学生,并且显示学生的一定信息。
- 首先要设计查找学生的界面,图片在第0步有展示
- 相关代码
void QtStu_find::Stu_find()
{
int F_id;
int i = 0;
int a = 0;
CStudent *s;
F_id = ui_Find->lineEdit->text().toInt();
for (i = 1;i <= Stu.Stu_num();i++)
{
s = Stu.Stu_find(i);
if (F_id == s->ID)
{
a = 1;
break;
}
}
if (a == 0)
{
e1.show();
}
if (a)
{
ui_Find->label_name->setText(s->name);
ui_Find->label_gender->setText(s->gender);
ui_Find->label_age->setText(QString::number(s->age));
ui_Find->label_co->setText(s->college);
ui_Find->label_ma->setText(QString::number(s->Math));
ui_Find->label_en->setText(QString::number(s->English));
ui_Find->label_cp->setText(QString::number(s->Cpp));
ui_Find->label_mag->setStyleSheet("color: red");
ui_Find->label_eng->setStyleSheet("color: red");
ui_Find->label_cpg->setStyleSheet("color: red");
ui_Find->label_mag->setText(s->Jud_Grade(s->Math));
ui_Find->label_eng->setText(s->Jud_Grade(s->English));
ui_Find->label_cpg->setText(s->Jud_Grade(s->Cpp));
if ((s->Math + s->English + s->Cpp) / 3 >= 80.0)
{
ui_Find->label_bad->hide();
ui_Find->label_good->show();
}
else
{
ui_Find->label_good->hide();
ui_Find->label_bad->show();
}
}
}
- 繁杂的主要是图形界面信息的设置,其实就是利用了链表的查找学生的函数,然后把学生的信息放到一个个框里面。学生等级设定的代码可以看看第1步。
10.设计功能:统计
- 首先利用图形设计器设计好窗口,窗口展示见第0步。
- 相关代码
void QtStu_count::Click_fen() { QBarSet *set1 = new QBarSet("数学"); QBarSet *set2 = new QBarSet("英语"); QBarSet *set3 = new QBarSet("C++"); *set1 << Lowsix("Math") << sixtoseven("Math") << seventoeight("Math") << eighttonine("Math") << ninetoten("Math"); *set2 << Lowsix("English") << sixtoseven("English") << seventoeight("English") << eighttonine("English") << ninetoten("English"); *set3 << Lowsix("Cpp") << sixtoseven("Cpp") << seventoeight("Cpp") << eighttonine("Cpp") << ninetoten("Cpp") ; QBarSeries *series = new QBarSeries(); series->append(set1); series->append(set2); series->append(set3); series->setLabelsVisible(true); //判断是否在图柱上显示数值 series->setLabelsPosition(QAbstractBarSeries::LabelsInsideEnd); //在柱上显示数值方式 QChart *chart = new QChart(); chart->addSeries(series); chart->setTitle("各学科分数段"); chart->setAnimationOptions(QChart::SeriesAnimations); QStringList categories; categories << "0-60" << "60-70" << "70-80" << "80-90" << "90-100" ; QBarCategoryAxis *axis = new QBarCategoryAxis(); axis->append(categories); m_typeAxisY = new QValueAxis;//创建Y轴 //m_typeAxisY->setRange(0, 5);//显示数值范围 m_typeAxisY->setTickCount(6); //设置多少格 m_typeAxisY->setLabelFormat("%d"); //设置刻度的格式 m_typeAxisY->setTitleText("学生人数"); //设置Y轴标题 // m_typeAxisY->setGridLineVisible(true); //设置是否显示网格线 // m_typeAxisY->setMinorTickCount(5); //设置每格小刻度数目 chart->setAxisY(m_typeAxisY, series); // chart->createDefaultAxes();//创建默认的左侧的坐标轴(根据 QBarSet 设置的值) chart->setAxisX(axis, series);//设置坐标轴 chart->legend()->setVisible(true); //设置图例为显示状态 chart->legend()->setAlignment(Qt::AlignBottom);//设置图例的显示位置在底部 ui_count->widget->setRenderHint(QPainter::Antialiasing);; ui_count->widget->setChart(chart); //return a.exec(); } int QtStu_count::Lowsix(QString str) { int i=0; int num = 0; CStudent *s; if (str == "Math") { for (i = 1;i < Stu.Stu_num();i++) { s = Stu.Stu_find(i); if (s->Math >= 0 && s->Math < 60) num++; } } else if (str == "English") { for (i = 1;i < Stu.Stu_num();i++) { s = Stu.Stu_find(i); if (s->English >= 0 && s->English < 60) num++; } } else if (str == "Cpp") { for (i = 1;i < Stu.Stu_num();i++) { s = Stu.Stu_find(i); if (s->Cpp >= 0 && s->Cpp < 60) num++; } } return num; } int QtStu_count::sixtoseven(QString str) { int i = 0; int num = 0; CStudent *s; if (str == "Math") { for (i = 1;i < Stu.Stu_num();i++) { s = Stu.Stu_find(i); if (s->Math >= 60 && s->Math < 70) num++; } } else if (str == "English") { for (i = 1;i < Stu.Stu_num();i++) { s = Stu.Stu_find(i); if (s->English >= 60 && s->English < 70) num++; } } else if (str == "Cpp") { for (i = 1;i < Stu.Stu_num();i++) { s = Stu.Stu_find(i); if (s->Cpp >= 60 && s->Cpp < 70) num++; } } return num; }
- 这部分代码其实没有太大参考价值,我也没有把全部复制过来,主要也觉得自己写得有点繁杂。其实就是统计了每个学科不同分数段的人数,然后利用Qt的表格相关的api函数搞出来就好了。
结语
- 其实关键还是在于链表的存储。依靠整个链表的存储,其实所有功能都能简单的实现,剩下的只是你想如何花里胡哨罢了。
- 看完这篇文章的你就能做一个像我这样稍微像点样的是不可能的,想要学习相关的c++知识,一定还是要靠自己尝试和练习。本文也只是提供作者本人做这个东西的一些思路,简单来说就是三步:创建好学生的链表;Qt构建窗口;利用链表的函数把信息输入到窗口上。