MFC - 课程设计 - 学生成绩管理系统(带直方图/折线图数据显示)

如题

********菜鸡的课设,仅供交流参考
在这里插入图片描述

在这里插入图片描述
(2)程序功能模块的设计
1.学生管理系统的实现:
在我的程序里,MFC主对话框“学生管理系统”中拥有“保存”,“打开”,“添加”,“删除”,“按高数成绩排序”,“按C++成绩排序”,“修改”,“确定”,“反选”一共9个按钮,以及“学号”,“姓名”,“高数”,“C++”四个编辑框,还有一个list control列表用于显示数据。四个编辑框绑定了4个CString变量,利于直接获取数据,
在students managementDlg.cpp中拥有list control初始化的代码以及8个按钮分别对应实现的代码。在student.h头文件,里面用于存放一个“学生类”,在public中存放学生的姓名,学号,高数成绩,c++成绩,(分CString以及long long)两个类型存储,用一个convert成员函数实现从CString数据转变为long long存储以便实现排序功能。
在manageff.h头文件中,存放一个用于管理“学生类”的“管理类”,在private中存储len(管理学生的容器总大小),count(已存学生数量),public中实现构造函数(初始化),学生类指针(容器),获取容器大小的函数getcount,向容器中添加学生的函数add,指定位置删除容器中的学生函数del,根据高数成绩排序的函数math_sort,根据c++成绩排序的函数Cjj_sort以及判断容器中是否有相同学号的人的函数getsame(实现当添加时容器中已存在学号相同的学生则无法添加的功能)
在mangement.cpp中实现“学生类”以及“管理类”中的函数。

2.数据用直方图以及折线图显示的实现:
在MFC单文档中的View.h的类CView的public添加两个按钮CButton函数以及一个函数Open。在View.cpp中的OnDraw函数里实现的绘图,在类向导中添加一个OnCreate函数,并且实现按钮的显示。最后实现两个按钮的函数(打开“学生管理系统”中保存的save.txt,根据提取save.txt中的数据更改各个分段的高度以显示出直方图以及折现图)。

(3)带有详细注释的自己编写的关键源程序:
1.学生管理系统:

//将四个编辑框分别绑定四个CString变量:Id,name,math,Cjj

1.Student.h头文件:

#include <afxtempl.h>
#define ll long long
using namespace std;
#pragma once

class student
{
public:
	//除了name以外所有数据均存一份CString跟一份long long
	ll Idq;
	ll mathq;
	ll Cjjq;
	CString name;//姓名
	CString Id;//学号
	CString math;//高数成绩
	CString Cjj;//C++成绩
	void convert();//将存储的CString转换成long long
};

2.Manageff.h头文件:

#pragma once
#include "stdafx.h"
#include "Student.h"

class studentmanag
{
	//学生类数组容器类
private:
	int len;//容器总大小
	int count;//容器已存的大小

public:
	studentmanag();//构造函数
	student * group;//学生数组容器

	int getcount();//获取容器已存大小
	void add(student x);//向容器添加一个学生
	void del(int x);//从容器中选择删除一个学生
	void math_sort();//按高数成绩排序数组
	void Cjj_sort();//按C++成绩排序数组
	bool getsame(student a);//判断容器中是否有学号相同的人
};

3.Management.cpp:

#include "stdafx.h"
#include "Student.h"
#include "manageff.h"
#include <algorithm>

using namespace std;

void student::convert()
{
	//将CString转成long long型存储
	Idq = _ttoi(Id);
	mathq = _ttoi(math);
	Cjjq = _ttoi(Cjj);
	return;
}

studentmanag::studentmanag()
{
	//初始化
	group = new student[100];
	len = 100;
	count = 0;
}

int studentmanag::getcount()
{
	//获取容器已存大小
	return this->count;
}

void studentmanag::add(student x)
{
	//容器添加操作
	count++;
	if (count >= len)//如果现有大小等于容器大小,则扩大容器
	{
		student *p = new student[len + 10];//开辟新容器
		len += 10;
		for (int i = 0; i < count - 1; i++)//将新容器复制旧容器
		{
			p[i] = group[i];
		}
		delete[]group;//删除旧容器的空间
		group = p;//绑定新容器
	}
	group[count - 1] = x;
	return;
}

void studentmanag::del(int x)
{
	//容器删除操作
	count--;
	int times = count - x;
	while (times--)
	{
		group[x] = group[x + 1];
		x++;
	}
	return;
}

bool cmp1(student x, student y)
{
	//用于排序高数成绩的cmp
	if (x.mathq == y.mathq)
	{
		return x.Cjjq > y.Cjjq;
	}
	return x.mathq > y.mathq;
}

void studentmanag::math_sort()
{
	//按高数成绩排序
	sort(group, group + count, cmp1);
	return;
}

bool cmp2(student x, student y)
{
	//用于排序c++成绩的cmp
	if (x.Cjjq == y.Cjjq)
	{
		return x.mathq > y.mathq;
	}
	return x.Cjjq > y.Cjjq;
}

void studentmanag::Cjj_sort()
{
	//按C++成绩排序
	sort(group, group + count, cmp2);
	return;
}

bool studentmanag::getsame(student a)
{
	//判断容器中是否存在学号相同的人
	ll num = a.Idq;
	bool flag = 0;
	for (int i = 0; i < count; i++)
	{
		if (this->group[i].Idq == num)
		{
			flag = 1;
			break;
		}
	}
	return flag;
}

4.students managementDlg.cpp中关键源代码:
OnInitDialog函数中添加:

//这是listcontrol的初始化,初始化添加勾选框
	m_list.SetExtendedStyle(LVS_EX_FULLROWSELECT|LVS_EX_CHECKBOXES);
	m_list.InsertColumn(0, _T("学号"),0,80);
	m_list.InsertColumn(1, _T("姓名"),0, 80);
	m_list.InsertColumn(2, _T("高数成绩"),0, 99);
	m_list.InsertColumn(3, _T("C++成绩"),0, 97);

按钮实现:

 void CstudentsmanageDlg::OnBnClickedButton3()
{
	//添加按钮

	UpdateData(TRUE);
	//将编辑框中的数据写入学生类
	student a;
	a.Cjj = Cjj;
	a.Id = Id;
	a.math = math;
	a.name = name;
	a.convert();

	if (stdmanag.getsame(a))
	{
		CString k = "学号重复了!";
		MessageBox(k);
		//弹出信息框k
	}
	else
	{
		stdmanag.add(a);//将该学生加入容器中

		//将数据在列表中显示出来
		int ncount = m_list.GetItemCount();
		m_list.InsertItem(ncount, a.Id);
		m_list.SetItemText(ncount, 1, a.name);
		m_list.SetItemText(ncount, 2, a.math);
		m_list.SetItemText(ncount, 3, a.Cjj);
	}
}

void CstudentsmanageDlg::OnBnClickedButton2()
{
	//删除按钮

	for (int i = 0; i < m_list.GetItemCount(); i++)
	{
		//判断是否勾选
		BOOL check = m_list.GetCheck(i);
		if (check)
		{
			stdmanag.del(i);
			//调用删除函数
			m_list.DeleteItem(i);
			//删除列表中该行数据
			i--;
		}
	}
}

void CstudentsmanageDlg::OnBnClickedButton1()
{
	//修改按钮

	for (int i = 0; i < m_list.GetItemCount(); i++)
	{
		BOOL check = m_list.GetCheck(i);
		if (check)
		{
			//如果勾选了该项
			UpdateData(TRUE);

			student a;
			a.Cjj = Cjj;
			a.Id = Id;
			a.math = math;
			a.name = name;
			a.convert();
			stdmanag.group[i] = a;
			//根据编辑框中数据更新容器中学生数据

			int ncount = m_list.GetItemCount();
			m_list.SetItemText(i, 0, a.Id);
			m_list.SetItemText(i, 1, a.name);
			m_list.SetItemText(i, 2, a.math);
			m_list.SetItemText(i, 3, a.Cjj);
			//更新列表数据显示
		}
	}
}

void CstudentsmanageDlg::OnBnClickedButton4()
{
	//按高数成绩排序按钮

	int count = stdmanag.getcount();
	stdmanag.math_sort();
	//调用排序

	m_list.DeleteAllItems();
	//删除所有列表中所有数据
	for (int i = 0; i < count; i++)
	{
		//容器中所有容器数据添加进列表
		m_list.InsertItem(i, stdmanag.group[i].Id);
		m_list.SetItemText(i, 1, stdmanag.group[i].name);
		m_list.SetItemText(i, 2, stdmanag.group[i].math);
		m_list.SetItemText(i, 3, stdmanag.group[i].Cjj);
	}
}

void CstudentsmanageDlg::OnBnClickedButton5()
{
	//按C++成绩排序按钮

	int count = stdmanag.getcount();
	stdmanag.Cjj_sort();
	//调用排序

	m_list.DeleteAllItems();
	//删除所有列表中所有数据

	for (int i = 0; i < count; i++)
	{
		//容器中所有容器数据添加进列表
		m_list.InsertItem(i, stdmanag.group[i].Id);
		m_list.SetItemText(i, 1, stdmanag.group[i].name);
		m_list.SetItemText(i, 2, stdmanag.group[i].math);
		m_list.SetItemText(i, 3, stdmanag.group[i].Cjj);
	}
}

void CstudentsmanageDlg::OnBnClickedButton7()
{
	//打开按钮

	CFileDialog saveDlg(TRUE, ".txt", "save", OFN_OVERWRITEPROMPT, "文本文档(*.txt)|*.txt|所有文件(*.*)|*.*||", NULL);
	//设置保存文件对话框格式
	
	if (saveDlg.DoModal() == IDOK)
	{
		//如果按了保存

		//获取文件路径,并且设置只读
		CString path = saveDlg.GetPathName();
		CStdioFile file(path, CFile::modeRead);

		CString date;
		student ui;
		int i = 0, j = 0;

		while (file.ReadString(date))//当还能读取到数据时
		{
			//将数据显示在列表中
			if (i % 4 == 0)
			{
				m_list.InsertItem(j, date);
			}
			else
			{
				m_list.SetItemText(j, i % 4, date);
			}

			//将数据添加进容器
			if (i % 4 == 0)
			{
				//第一行为学号
				if (j != 0)
				{
					//除了第一个数据之外,当为4倍数时即说明ui已获取完一个学生数据
					//然后添加进容器
					ui.convert();
					stdmanag.add(ui);
				}
				ui.Id = date;
			}
			else if (i % 4 == 1)
			{
				//第二行为姓名
				ui.name = date;
			}
			else if (i % 4 == 2)
			{
				//第三行为高数成绩
				ui.math = date;
			}
			else if (i % 4 == 3)
			{
				//第四行为C++成绩
				ui.Cjj = date;
			}
			i++;
			if (i % 4 == 0) j++;
			//四个数据一换行
		}
		//将最后一个数据传进容器
		ui.convert();
		stdmanag.add(ui);
	}
}

void CstudentsmanageDlg::OnBnClickedButton8()
{
	//保存按钮

	CFileDialog saveDlg(FALSE, ".txt", "save", OFN_OVERWRITEPROMPT, "文本文档(*.txt)|*.txt|所有文件(*.*)|*.*||", NULL);
	//设置保存文件对话框格式

	if (saveDlg.DoModal() == IDOK)
	{
		//如果按了保存

		CString path = saveDlg.GetPathName();//获取文件路径
		CString k = "文件已保存在路径";
		CString k2;
		k2.Format("%s%s", k, path);//k+k2
		MessageBox(k2);
		//弹出信息框k2

		CStdioFile file(path,CFile::modeWrite|CFile::modeCreate);
		//创建或者写入文件
		
		//保存数据
		CString cs, hh = "\n";
		for (int i = 0; i < m_list.GetItemCount(); i++)
		{
			//i行
			for (int j = 0; j < 4; j++)
			{
				//j列
				cs = m_list.GetItemText(i, j);
				file.WriteString(cs);
				//写入一个数据
				file.WriteString(hh);
				//换行
			}
		}
		file.Close();
		//关闭文件
	}
}


void CstudentsmanageDlg::OnBnClickedButton9()
{
	//反选按钮
	for (int i = 0; i < m_list.GetItemCount(); i++)
	{
		m_list.SetCheck(i, !m_list.GetCheck(i));//设置是否勾中
	}
}

2.直方图以及折线图的显示:

在Doc.cpp中的OnNewDocument函数中添加以下代码(修改标题)

SetTitle(_T("直方图/折线图实现"));

1.在View.h中的Cview类的public添加以下代码:

afx_msg void OnClickButton1();
afx_msg void OnClickButton2();
//定义按钮1,2	
CButton m_button;
CButton m_button1;

2.View.cpp中关键源代码:

int a = 0, b = 0, c = 0, d = 0, e = 0;
//矩阵高度
//在Ondraw函数中添加以下代码:
	pDC->SetMapMode(MM_ISOTROPIC);
	pDC->SetViewportOrg(200,450);//设置原点
	pDC->MoveTo(100, 0);//从100,0画到1300,0
	pDC->LineTo(1300, 0);

	//设置画笔参数
	CBrush brush;
	//画笔颜色
	brush.CreateHatchBrush(HS_BDIAGONAL, RGB(150, 100, 45));	
	pDC->SelectObject(brush);

	int i = 1;

	//绘制矩形,分别为四个点座标
	pDC->Rectangle(200, 0, 400, 50*a);
	pDC->Rectangle(400, 0, 600, 50 * b);
	pDC->Rectangle(600, 0, 800, 50 * c);
	pDC->Rectangle(800, 0, 1000, 50 * d);
	pDC->Rectangle(1000, 0, 1200, 50 * e);

	//设置分段文本座标
	pDC->TextOut(250, 0, " <60");
	pDC->TextOut(450, 0, "60-70");
	pDC->TextOut(650, 0, "70-80");
	pDC->TextOut(850, 0, "80-90");
	pDC->TextOut(1050, 0, "90-100");
	
	//设置文字高度(多少人)
	CString y1,y2,y3,y4,y5;
	y1.Format("%d", a);
	y2.Format("%d", b);
	y3.Format("%d", c);
	y4.Format("%d", d);
	y5.Format("%d", e);

	//x轴从250-1050
	pDC->MoveTo(250, 50 * a + 100);
	pDC->LineTo(450, 50 * b + 100);
	pDC->LineTo(650, 50 * c + 100);
	pDC->LineTo(850, 50 * d + 100);
	pDC->LineTo(1050, 50 * e + 100);

	//设置文本位置
	if(a!=0)
	pDC->TextOut(250, 27 * a, y1 + "人");
	if (b != 0)
	pDC->TextOut(450, 27 * b, y2 + "人");
	if (c != 0)
	pDC->TextOut(650, 27 * c, y3 + "人");
	if (d != 0)
	pDC->TextOut(850, 27 * d, y4 + "人");
	if (e != 0)
	pDC->TextOut(1050, 27 * e, y5 + "人");
//在类向导中添加处理程序OnCreate
//在View.cpp中OnCreate函数添加以下代码:
	m_button.Create("高等数学",//设置按钮1标题
		WS_CHILD | WS_VISIBLE | WS_BORDER,

		//设置按钮1座标
		CRect(1200, 10, 1360, 60),
		this,
		IDC_BUTTON1);
	m_button.ShowWindow(SW_SHOWNORMAL);
	
	m_button1.Create("C++",//设置按钮2标题
		WS_CHILD | WS_VISIBLE | WS_BORDER,

		//设置按钮2座标
		CRect(1200, 70, 1360, 120),
		this,
		IDC_BUTTON2);
	m_button1.ShowWindow(SW_SHOWNORMAL);

//两个按钮的函数实现
void CDrawT12View::OnClickButton1()
{
	//按钮1实现
	
	//初始化
	a = b = c = d = e = 0;
	
	//打开文件并且操作
	CFileDialog fdlg(TRUE, ".dat", "save", OFN_OVERWRITEPROMPT, "数据文件(*.txt)|*.txt|所有文件(*.*)|*.*||", NULL);

	//如果成功读取文件
	if (fdlg.DoModal() == IDOK)
	{
		CStdioFile file(fdlg.GetPathName(), CFile::modeRead);
		CString cs;

		int i = 0;
		while (file.ReadString(cs))
		{
			//当还能读取到行
			if (i % 4 == 2)
			{
				//如果是高数成绩,则转换为数值再判断大小
				long long q = _ttoi(cs);
				if (q < 60) a++;
				else if (q >= 60 && q <= 70) b++;
				else if (q > 70 && q <= 80) c++;
				else if (q > 80 && q <= 90) d++;
				else if (q > 90) e++;
				
			}
			i++;
		}
	}
	InvalidateRect(NULL);
}

void CDrawT12View::OnClickButton2()
{
	//按钮2实现

	//初始化
	a = b = c = d = e = 0;

	//打开文件并且操作
	CFileDialog fdlg(TRUE, ".dat", "save", OFN_OVERWRITEPROMPT, "数据文件(*.txt)|*.txt|所有文件(*.*)|*.*||", NULL);

	//如果成功读取文件
	if (fdlg.DoModal() == IDOK)
	{
		CStdioFile file(fdlg.GetPathName(), CFile::modeRead);
		CString cs;

		int i = 0;
		while (file.ReadString(cs))
		{
			//当还能读取到行
			if (i % 4 == 3)
			{
				//如果是C++成绩,则转换为数值再判断大小
				long long q = _ttoi(cs);
				if (q < 60) a++;
				else if (q >= 60 && q <= 70) b++;
				else if (q > 70 && q <= 80) c++;
				else if (q > 80 && q <= 90) d++;
				else if (q > 90) e++;
			}
			i++;
		}
	}
	InvalidateRect(NULL);
}

(4)程序运行时的效果图:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
(5).实验结果分析,实验收获和体会:

这次mfc实验是对话框实现以及View单文档视图的综合应用

1.这次实验进行初期,我对MFC中工具箱中控件跟实际映射的变量以及函数应用不太熟练,经过实验,我熟悉了MFC的Dlg界面设计,以及各个控件之间的关联绑定。

2.这次实验需要大量使用MFC中各个控件的自带函数(例如读写txt文件,list control的函数方法等等),需要在网上查找资料,经过实验后,使得我对这些函数的使用更加的熟悉了。

3.经过实验,我巩固了头文件的编写与多cpp的编写形式。

4.在cpp文件运用头文件中书写的类时会出现很多问题:无法识别头文件中的类,将头文件stdafx.h书写在最前面include就解决了问题,具体原因未知。

5.试验初期,我运用了CArray(MFC自带的模板容器)来存储学生类,很快就完成完了要求,但是后来因为老师不允许使用STL容器以及其他容器,所以自己写了一个容器,并且通过自己实现了很多类容器的函数方法,让我更加提升了自己的代码能力。

6.实验要求实现MFC单文档内展示两个不同的直方/折现图,因此需要在单文档视图中添加按钮实现,于是我在网上查找资料学习到了在MFC单文档中安置按钮的方法,以及熟悉了在MFC中CBrush(画笔)的函数方法使用。

7.实现排序功能时,我写了多个cmp用于实现多个排序方法,再用algorithm头文件中的sort函数进行快排,实现便捷并且效率高。
8.在实现直方图/折线图的时候,实现界面的标题是未定义标题,觉得有点不太好看(截图在实验报告不美观),因此在网上查找资料,学会了更改标题的方法。

(6)实验的改进意见和建议:

1.实验要求中没有要求到的,我觉得挺重要的按钮:按学号查找,我的想法是在容器类中添加find函数用于查找参数中输入的int学号,再弹出对话框显示该学号学生的所有信息。

2.在学生管理系统中,虽然实验没有要求,但是我添加了反选的按钮,因为我发现在调试的时候,要一个个勾选删除比较麻烦,所以添加反选按钮实现一键删除功能。

3.我认为判断是否学号重复添加的功能是很需要的,因此我在类容器中写了个查找学号是否重复的函数,用于实现这个功能,若重复添加则弹出“学号重复”信息框。

总结:(之前觉得MFC是个上世纪的产物,觉得没用,课也没听,但是为了写这课程设计,还特地去学,枯辽,但是感觉学会了还行8)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章