绘制成绩分布图.docx
《绘制成绩分布图.docx》由会员分享,可在线阅读,更多相关《绘制成绩分布图.docx(24页珍藏版)》请在冰豆网上搜索。
绘制成绩分布图
Visualc++可视化程序设计报告
——绘制成绩分布图
系别:
电气系
班级:
电子092班
姓名:
杜力朝
学号:
2009315228
绘制成绩分布图报告
一、设计内容介绍:
本示例主要的功能是通过给定的一组成绩数据绘制出成绩的分布图(包括直方图和圆饼图)。
这里没有用到数据库,因为是一个简单的成绩分布图,而用数据库的话在转到另一台机子上还要重新建表,对于学生信息也要涉及到录入问题,不容易实际操作,所以本示例采用了更为方便的通过手动录入数据或者是从文件(txt格式文本文档,第一个数为总人数,其余为分数,分数之间用逗号隔开)导入数据两种方式绘制出其统计图。
录入的成绩可查看可修改,直方图采用了立体图模式,看起来更直观,各个分数段都有明显的分数人数标注,看起来直观形象。
饼形图也同样标注了各颜色对应的分数段及人数,看起来很直观。
数据录入部分与绘图部分集成在一个对话框上面,没有在单个文档中调用,减少了不必要的繁琐过程。
二、设计思路方法、过程
在早期一段时间做过界面设计,多媒体的制作以及网络部分的程序,而对于绘图一部分一直以来都为触及到,所以在这方面采用了查找样本借鉴修改的方法来完成本实验。
通过查阅资料分析一共得到两个差不多的实例,其中一个是创建一个绘图类,然后在绘制过程中创建该类,将该类实例化以后绘制出相应的图形;另一个是直接从给出的数据中绘出直方图和饼图来。
因为学C++还未达到一定层次,对于建类来说并不是一件容易的事,所以采用了第二种直接绘图的方法,即在绘制按钮中添加所有绘制代码。
因为给定的是一个在单文档中绘制的实例,而且数据是直接绘出的,本实例是需要从所给分数数据中统计出各分数段人数,然后将其分布图绘制出来,所以在将其源码打出来以后,在其基本框架上加以修改而来,将当文档换成为基本对话框,并且数据经过处理以后显示在对话框右侧部分。
所给实例只是一个简单的绘制图形的实例,仅在单机是进行绘制,而本例则在导入数据或者录入数据后绘制图形,所以在调试过程中加入了很多变量,和局部变量。
通过全局变量将局部变量联系到一起,从而实现了要求所述的基本功能。
老师说,一个软件的好与坏区别在其制作时考虑是否全面,要将其可能出现的用户所要求的所有情况都加以完善,所以本示例在基本功能完成后的完善部分做了很大一部分工作,主要有在绘制时没有绘制数据时报错提醒,导入时没有导入文件报错提醒,文件录入查看及录入修改,重新录入等,录入人数不可大于总人数,在录入区域及标注上也做了相应调整,还有在程序从得到焦点重绘图形的过程上加以了完善。
最后程序基本上就成型了,不过程序还有一点问题就是在导入文件时的问题现在还未想到解决方法。
在导入数据的时候要做一个检测,检测路径框是否为空,如果为空则提示错误,不为空则导入数据,这时如果路径框中路径不对的话,那么导入数据时程序便会终止。
因为不知道怎么见得文件时候合法,所以这个问题暂时还没有解决方案。
不过一般按正确操作的话是不会出现问题的。
三、设计结果示范
本示例主要有两个结果:
1)、直方图绘制效果图
2)、圆饼图绘制效果图
四、设计心得体会
通过这个小小的程序设计,对程序设计有了一个新的认识,也使自己在生活过程中用一种新的思维审视世界。
曾听过这样的一段话:
在玩游戏的过程中看到的不是画面,而是各种数据在有规律的运动,计算,时刻进行着各种循环和假设判断,我玩的已不是游戏,而是方程式……这段话深刻的反映出了游戏的设计过程。
同样也折射出了程序设计的原理。
在绘图这方面以前只设计过简单的直线曲线自由线画图,而对于这样的数据自动绘图没有涉及到,在设计出来前就曾想过做一个图像处理程序,不过在看过程序的复杂性以及时间短暂的原因,没有着手哪方面的准备,在这之后课题为绘图设计正好可以弥补这方面的空缺。
设计时由于没有任何的基础,所以搜索了很多资料,最终锁定了一个比较简单的模板,将模板打出来是很简单的事情,但是真正有趣的是后面的调试完善工作,其中各种环节都是围绕绘制矩形和绘制扇形而来,大量的工作就是将数据导入数组并将其处理分组得到其人数以后将其转化为矩形以及为立体而设计的平行四边形的坐标,扇形的坐标。
其他的一些完善代码大部分都是辅助的,不过在设计过程中也是十分有趣的。
在设计过程中遇到了很多问题,从最简单的矩形图数据分析到后来的文件操作以及画刷颜色的随机选取,模拟按键消息,各种错误的报错提醒等等问题都一一排除,通过查阅大量资料来进行各种完善。
因为学过其他的编程软件,所以在编程的过程中,也在比较两者的区别,各自的特点以及缺点,不同和相同之处。
C++的各类分的比较详细,在创建变量时也是通过对话框创建,结构性比较好,适合于团队协作;而另一个编程软件(delphi)则在类于类之间比较模糊,所有的工作都在Form上操作,不过这样操作起来比较方便,整合的控件也相当多,直接摆放即可,适合个人应用。
不管哪种软件都是为编程而服务的,其目的是一样的,只是在操作上不同而已,通过两个软件的比较是自己更好的掌握了其类的概念,控件的用途,在函数以及属性的设计上都有了更深的理解。
语言都是想通的,在学过一门语言以后其他的便可以读懂了。
最开始学习的是C++,而后又接触了delphi,在刚开始学习delphi的过程中基本上都可以读懂其中的代码,而后再学习VisualC++便轻松多了。
在刚开始讲课的时候几乎不知道怎么去分析处理,因为有了前者使用的习惯,所以在后者使用时总会想当然的以为该那样,其实两者在程序使用中有很大的不同,即使到了考试时也不是很明白该怎么去使用它。
直到现在设计这个小程序时才系统的将这个软件大致的掌握了其使用方法。
在变量声明以及转换显示,属性的动态修改等等都有了一个新的认识。
那么剩下的在编程方面的思想以及windowsAPI接口函数的使用都是相通的。
只不过实现的过程不一样而已。
在设计过程中遇到了很多错误,软件自身带有的DEBUG功能有一定的好处,但是这个功能不怎么会用,而在查阅资料的时候记得看到过用MessageBox来调试程序的用法,如果出错了,那么就用MessageBox来看看到底程序执行到了什么步骤,出现对话框,则说明之前的都执行过了,如果没出现,则程序没执行到这里就已经产生了错误。
MessageBox还可以显示所处理的数据或者字符串的变化。
用起来相当简单。
这个小技巧是自己在调试过程中效率提高了不少,在源代码中,这些MessageBox并没有删除,只是注释掉了,在以后分析的时候这也是相当有帮助的。
五、出现的错误及改正方法
1.刚开始是将浏览和导入的函数集成到一个按钮“浏览”里面,但是在调试中发现如果单击浏览而不选中文件的话,会出现错误二终止。
通过分析其主要原因是因为函数理念有导入数据的代码,单击后会一并执行,如果没有选中文件那么就无法导入数据产生错误。
解决方法:
将浏览和导入功能分装到两个按钮里面,并在导入数据的时候检测是否有文件导入。
如果没有则报错提醒。
2.在绘制直方图或圆饼图时如果没有数据则会出现错误而自动终止程序,原因是数组当中没有数据而无法绘图。
解决方法:
定义全局变量,在导入后全局变量变为1,绘制时检测全局变量是否为1,是则绘制,否则报错提醒。
3.在录入过程中难免会录入错误,所以查看录入和重新录入就显得很重要了。
而在录入的过程中会出现录入人数大于所规定的人数的,这样在数组存放数据时就会产生错误。
解决方法:
增加重新录入和查看录入成绩的功能,为了方便起见,每行显示五个成绩,而且在录入时对录入人数做了一个判断,如果大于所输人数,则提示成绩录入完毕。
4.在做圆饼图时发现如果某一分数段的人数为0的话,就会产生这样的结果
这种结果并不是我们想要的。
图中可以看到本来没有的分数段70-80覆盖掉了原有的<60和60-70的分数段。
解决方法:
在绘制过程中对绘制扇形一部分做了检测,发现如果所绘制分数段如果为零则不绘制这段扇形。
这样结果便正常了。
5.由于绘图部分并不是在OnPaint函数里所写的,而是直接在按钮里添加的,所以在程序重新得到焦点重绘的过程中会产生这样的错误:
一部分被挡住的图形丢失了。
虽说可以重新点击绘图重绘,不过这样的事情最好不要发生。
解决方法:
在程序重绘的过程中在OnPaint事件里添加如下代码:
if(DrawMode==1)
{
PostMessage(WM_COMMAND,IDC_BUTTON1,BN_CLICKED);
}
elseif(DrawMode==2)
PostMessage(WM_COMMAND,IDC_BUTTON2,BN_CLICKED);
这是模拟单机事件的代码,在重绘过程中自动模拟单机事件。
DrawMode是定义的一个绘图模式,若单机绘制直方图,DrawMode=1;单机绘制圆饼图,DrawMode=2。
在重绘过程中自动模拟但是过程。
省去了使用者再次点击的过程。
六、设计中的关键源程序
1、绘制直方图代码:
voidCMy09228DulichaoDlg:
:
OnButton1()
{
if(True==0)
{
MessageBox("未导入数据!
","错误!
");
}
else
{
CClientDC*pDC=newCClientDC(this);
constintx0=280,y0=35,x1=780,y1=395;//定义绘制的矩形区域
//CFileDialogofn(TRUE,NULL,"*.txt",OFN_FILEMUSTEXIST|OFN_PATHMUSTEXIST,"Textfiles(*.txt)\0*.txt\0");
//ofn.DoModal();
//FILE*fp=fopen(ofn.GetPathName(),"rt");
intN,*a;
inti,x,y;
CStrings;
//for(fscanf(fp,"%d,",&N),a=newint[N],i=0;i//{fscanf(fp,"%d,",&a[i]);
//}
//fclose(fp);
N=Num;
a=newint[N];
//inta[N];
for(i=0;i{
a[i]=Score[i];
//CStringstr1;
//str1.Format("%d",a[i]);
//MessageBox(str1,"");//将全局变量Score[i]里的数据传递给局部变量a[i]
}
//MessageBox()"","";
intSc[5];
Sc[0]=0;
Sc[1]=0;
Sc[2]=0;
Sc[3]=0;
Sc[4]=0;
for(i=0;i{
if(a[i]<60)
{
Sc[0]++;
}
else
if(a[i]<70)//统计各个分段中的人数
{
Sc[1]++;
}
else
if(a[i]<80)
{
Sc[2]++;
}
else
if(a[i]<90)
{
Sc[3]++;
}
else
{
Sc[4]++;
}
}
intMax=0;
for(i=0;i<5;i++)
{
if(Sc[i]>Max)//确定五个分段中最大的个数
Max=Sc[i];
}
//MessageBox("adfasdf","sss");//调试所用
constintdx=(x1-x0)/5,M=30,dy=(y1-y0)/10,h=dx/4,dh=(y1-y0)/Max;
CPointv[4];
CBrushbr;
CPenpen,*p_pen;
#defineRrand()%56+200
BYTEr,g,b;
br.CreateSolidBrush(RGB(126,126,126));
pDC->SelectObject(&br);
//srand((unsigned)time(NULL));
CRectrc;
GetClientRect(&rc);
pDC->Rectangle(x0-40,0,rc.right,rc.bottom);//画出绘制区域
br.DeleteObject();
pDC->MoveTo(x0,y1);
pDC->LineTo(x=x1+80,y1);
pDC->MoveTo(x,y1);
pDC->LineTo(x-12,y1-3);
pDC->MoveTo(x,y1);
pDC->LineTo(x-12,y1+3);//画出x轴
pDC->MoveTo(x0,y1);
pDC->LineTo(x0,0);
pDC->MoveTo(x0,0);
pDC->LineTo(x0-3,12);
pDC->MoveTo(x0,0);
pDC->LineTo(x0+3,12);//画出y轴
pen.DeleteObject();
//pDC->SetROP2(R2_MASKPEN);
//pen.CreatePen(PS_DOT,1,RGB(70,70,70));
//p_pen=pDC->SelectObject(&pen);
pDC->MoveTo(x1+55,0);
pDC->LineTo(x1+55,y1);
pen.CreatePen(PS_SOLID,2,RGB(0,0,0));
p_pen=pDC->SelectObject(&pen);
for(i=0,x=x0;i<5;++i,x+=dx)
{
br.CreateSolidBrush(RGB(r=R,g=R,b=R));
pDC->SelectObject(&br);
pDC->Rectangle(x,y1-dh*Sc[i],x+dx,y1);//画出矩形区域
v[0].x=x+dx;v[0].y=y1-dh*Sc[i];
v[1].x=v[0].x+h;v[1].y=v[0].y-h;
v[2].x=v[1].x-dx;v[2].y=v[1].y;
v[3].x=v[2].x-h;v[3].y=v[2].y+h;//画出顶部平行四边形区域
pDC->Polygon(v,4);
pDC->SelectStockObject(WHITE_BRUSH);
br.DeleteObject();
if(i==4||Sc[i]>Sc[i+1])
{
br.CreateSolidBrush(RGB(r*0.7,g*0.7,b*0.7));
pDC->SelectObject(&br);
v[2].x=v[1].x;v[2].y=v[1].y+dh*Sc[i];
v[3].x=x+dx;v[3].y=y1;
pDC->Polygon(v,4);//画出侧边四边形区域
pDC->SelectStockObject(WHITE_BRUSH);br.DeleteObject();
}
}
pDC->SelectObject(p_pen);
pen.DeleteObject();
pen.CreatePen(PS_DOT,1,RGB(70,70,70));
p_pen=pDC->SelectObject(&pen);
pDC->SetROP2(R2_MASKPEN);
for(y=y1,i=0;y>=y0;++i,y-=dh)
{
if(y!
=y0&&y!
=y1)
{
pDC->MoveTo(x0,y);
pDC->LineTo(x1+h+30,y);///沿y轴标注
}
s.Format("%3d",i);
pDC->TextOut(250,y-8,s);
}
CStringren;
ren="人";
s.Format("%s",ren);
pDC->TextOut(260,10,s);
pDC->SelectObject(p_pen);
pen.DeleteObject();
CStringStr[5];
Str[0]="<60分";
Str[1]="60-70分";
Str[2]="70-80分";
Str[3]="80-90分";//x轴标注
Str[4]=">90分";
for(i=0;i<5;++i)
{
s.Format("%s",Str[i]);
pDC->TextOut(x0+i*dx+(dx-s.GetLength()*8)/2,y1+4,s);//沿x轴标注
s.Format("%d人",Sc[i]);
pDC->TextOut(x0+i*dx+(dx-s.GetLength()*8)/2+6,y1-20-dh*Sc[i],s);//顶层标注人数
}
CStringfen;
fen="分数";
s.Format("%s",fen);
pDC->TextOut(x0+i*dx+(dx-s.GetLength()*8)/2+30,y1+4,s);
deletepDC;//删除指针
if(a!
=NULL)deletea;
//Num=1;//将最大数清零
DrawMode=1;
True=1;
}
}
2.绘制饼形图源代码
if(True==0)
{
MessageBox("未导入数据!
","错误!
");
}
else
{
CClientDC*pDC=newCClientDC(this);
constdoublePI=3.1415926536,_2PI=PI+PI;//圆周率/定义
#defineSums[n-1]
constintr=170,p=20;
constintx0=r+20,y0=r+20,x1=x0-r,y1=y0-r,x2=x0+r,y2=y0+r;
intn=5,*data=newint[n],*s=newint[n];
staticintpromin=0;
///////////////////////////////*
intN,*a;
inti,x,y;
//CStrings;
N=Num;
a=newint[N];
//inta[N];
for(i=0;i{
a[i]=Score[i];//将全局变量Score[i]里的数据传递给局部变量a[i]
}
intSc[5];
Sc[0]=0;
Sc[1]=0;
Sc[2]=0;
Sc[3]=0;
Sc[4]=0;
for(i=0;i{
if(a[i]<60)
{
Sc[0]++;
}
else
if(a[i]<70)//统计各个分段中的人数
{
Sc[1]++;
}
else
if(a[i]<80)
{
Sc[2]++;
}
else
if(a[i]<90)
{
Sc[3]++;
}
else
{
Sc[4]++;
}
}
intMax=0;
for(i=0;i<5;i++)
{
if(Sc[i]>Max)//确定五个分段中最大的个数
Max=Sc[i];
}
///////////////////////////////////////////////
data[0]=Sc[0];
data[1]=Sc[1];//将分数段传给data数组
data[2]=Sc[2];
data[3]=Sc[3];
data[4]=Sc[4];
intx3,y3,x4,y4;
doubletheta3,theta4,thetam,tx,ty;
//#defineR156+rand()%100
//BYTEr1,g,b;
CBrushbr,*p_br;
CRectrc;
GetClientRect(&rc);
TEXTMETRICtxMetric;
pDC->GetTextMetrics(&txMetric);
//br.CreateSolidBrush(RGB(r1=R,g=R,b=R));
//pDC->SelectObject(&br);
CStringstr;
br.CreateSolidBrush(RGB(126,126,126));
pDC->SelectObject(&br);
pDC->Rectangle(240,0,rc.right,rc.bottom);
br.DeleteObject();
//绘图区域
for(s[0]=data[0],i=1;is[i]=s[i-1]+data[i];//求总和
srand((unsigned)time(NULL));
CStringStr1[5];
Str1[0]="<60分";
Str1[1]="60-70分";
Str1[2]="70-80分";
Str1[3]="80-90分";//x轴标注
Str1[4]=">90分";
for(theta3=0.0,x3=x0+r/*x0=r+20*/,y3=y0/*y0=r+20*/,i=0;i{
theta4=_2PI*s[i]/Sum;//角度值
x4=x0+r*cos(theta4);//三角值
y4=y0+r*sin(theta4);//三角值
thetam=(theta3+theta4)*0.5;//三角平均值
if(i!
=promin)
tx=ty=0;
else
{
tx=p*cos(thetam)*(_2PI-theta4+theta3)/_2PI;
ty=p*sin(thetam)*(_2PI-theta4+theta3)/_2PI;
}
if(data[i]!
=0)
{
//#defineRrand()%56+200
br.CreateSolidBrush(RGB(255+i*50,300+i*80,50+i*60));
p_br=pDC->SelectObject(&br);
pDC->Pie(x1+tx+250,y1+ty,x2+tx+250,y2+ty/*边缘矩阵*/,x4+tx+250,y4+ty/*起始点*/,x3+tx+250,y3+ty/*终止点*/);//第一个是边缘矩阵,第二个是起始点,第三个是终止点
str.Format("%d",data[i]);//各个分段数据
pDC->TextOut(x0+tx-txMetric.tmAveCharWidth*str.GetLength()*0.5+r*0.7*cos(thetam)+250,y0+ty-(txMetric.tmExternalLeading+txMetric.tmHeight)*0.5+r*0.7*si