2024年2月19日发(作者:)
集中上机实习报告
指导教师: 王利
姓 名: 赵蕊
学 号: 2010211864
班 级: 0411003
时 间: 2012.5
模拟时钟程序
一. 需求分析
1.题目内容:
编写一个模拟时钟程序,此程序在屏幕左方有一指针式钟面,右方有两个矩形框,上面以数字方式显示日期和时间,该时间应与指针显示的时间一致,下方的矩形框作为秒表。用菜单选项或按钮设置时间和秒表。时间不必与机器系统时间相同,只要可任意设置即可。
2.题目分析:
(1)模拟时钟是一种集计时器和时钟显示于一体的程序。编写一个指针式时钟程序,此程序在屏幕左方有一个指针式钟面,右方有两个矩形框,上面以数字方式显示日期、星期和时间。指针式的时钟表盘为圆形,并且圆周上有分布均匀的60个刻度,刻度要求显示清楚,钟面上有长度不相同的指针,即时针、分针、秒针,指针的运动要求具有规律性,且为顺时针。数字钟显示时间的格式是年月日星期时分秒,小时为24进制,分钟和秒是60进制,指针式的时钟和数字式的时钟显示的时间同步。按下时钟控制设置时间菜单项可弹出一对话框,用于设置当前的时间、日期和星期;按下秒表控制按钮后,秒表显示窗中显示从0开始的时间,单位为百分之一秒。再次按下秒表控制按钮后计时停止,该窗口显示累计时间。
(2) 本实验主要涉及到的知识点有:时钟指针运动算法、屏幕重绘方法、定时器消息、鼠标消息、菜单命令、对话框、画笔/画刷、显示文字等。指针运动算法和屏幕重绘方法是本程序主要难点所在。
3.最终效果如下图:
二.概要设计
1.程序系统模块划分
模拟时钟转动程序
100指针式时钟
80数字式时钟
60秒表
东部西部北部
图401功能模块图
200第一季度第三季度
2.程序算法分析
本程序不论何种指针,每次转动均以π/30弧度(一秒的角度)为基本单位,且都以表盘中心为转动圆心。计算指针端点(x, y)的公式如下:
x =圆心x坐标 + 指针长度 * cos (指针方向角)
y =圆心y坐标 + 指针长度 * sin (指针方向角)
指针长度是指自圆心至指针一个端点的长度,由于指针要跨越圆心,因此一个指针需要计算两个端点。
三个指针的运动是相关联的,秒针转一圈引起分针运动一格,分针每转12格引起时针运动一格,而因此应该使用一个定时器消息来处理指针的运动。若用三个定时器消息分别处理时针、分针和秒针的运动,就会使问题复杂化且不易实现三个指针联动的正确规律。采用一个定时器消息可以很容易实现指针联动算法。
由于屏幕的重绘速度快,如果采用全屏删除式重绘则闪烁十分明显,显示效果不佳。本程序采用非删除式重绘,假定指针将要移动一格,则先采用表盘背景色(这里为浅绿色)重绘原来指针以删除原来位置的指针,再采用指针的颜色在当前位置绘制指针;如果指针没有动,则直接绘制指针。
另外,秒表需要采用单独的定时器消息控制,秒表重绘速度很快,屏幕闪烁明显,故本程序采用了双缓冲绘图。
3.系统流程图
开始
CClockApp theAppCClockApp::CClockApp()构造函数
进入WinMain函数
CClockApp::InitInstance()
创建画笔画刷并初始化时间变量
调用时钟处理程序完成时钟绘制
创建指针时钟
创建数字钟字体 创建秒表字体
时分秒针运动
对数字钟进行输出
对秒表进行初始显示
调用消息处理函数刷新窗口显示(1秒)
判断是否启动
判断是否设置时间
调用消息处理函数刷新窗口显示(1/100秒)
是否停止
设置时间
停止
三. 详细设计
1.模块描述
(1)指针式时钟
指针式的时钟表盘为圆形,并且圆周上有分布均匀的12个数字刻度和48个较小的原点刻度,刻度显示清楚,用来表示小时、分钟和秒数。钟面上有长度不相同的指针,即时针、分针、秒针,指针的运动具有规律性,为顺时针。
(2)数字式时钟
数字钟显示时间的格式是时分秒,小时为24进制,分钟和秒是60进制,指针式的时钟和数字式的时钟显示的时间同步,且俩时钟可以同时设置。
(3)秒表
按下秒表控制按钮后,秒表显示窗中显示从0开始的时间,单位为百分之一秒。再次按下秒表控制按钮后计时停止,该窗口显示累计时间。
2.函数描述
(1)
CPoint p;
double angle = nvalue *PI/30 ;
p.x = m_Center.x + (int)(nlenth*sin(angle));
p.y = m_Center.y - (int)(nlenth*cos(angle));
return p;
用来得到时钟刻度点和时针、分针、秒针的端点
(2)hour = hour*5;
hour = hour + minute/12;
m_OldHour[0] = m_Hour[0];
m_OldHour[1] = m_Hour[1];
m_Hour[0] = GetPoint(int(m_Radious/2),hour);
m_Hour[1] = GetPoint(7,hour + 30);
m_OldMin[0] = m_Minute[0];
m_OldMin[1] = m_Minute[1];
m_Minute[0] = GetPoint(int(m_Radious*7/10),minute);
m_Minute[1] = GetPoint(10,minute + 30);
m_OldSec[0] = m_Second[0];
m_OldSec[1] = m_Second[1];
m_Second[0] = GetPoint(int(m_Radious*8/10),second);
m_Second[1] = GetPoint(30, second + 30);
用来得到时针、分针、秒针的当前位置和上一时刻位置并将其保存
(3) CRect rc;
CDC dcMem;
GetClientRect(&rc);
CBitmap bmp;
CompatibleDC(pDC);
CompatibleBitmap(pDC,(),());
CBitmap *pOldBit=Object(&bmp);
lidRect(rc,RGB(255,255,255));
„„„„.//画图代码
pDC->BitBlt(0,0,(),(),&dcMem,0,0,SRCCOPY);
DC();
Object();
利用双缓冲防止屏幕画面闪烁
四. 实现环境及工具
Win7 环境下
VC++ 6.0 MFC
五. 程序测试
1. 对时间设置测试
按下确定后测试其设置为需要设置的时间,并正常运行。
2. 对秒表测试
点下启动后它正常运行;
点下停止后它停止运行并记录当前累积时间。
再次按下启动按钮时它计时清零并重新开始计时。
3. 整体3部分测试
同时运行时三部分均正常运行,并且屏幕也不闪烁。
六.心得体会
通过本次实验,对c++编程有了进一步的了解和能够更加熟练的编程,还学习了MFC的一些画图处理函数,以及一些运行机理,能够基本掌握用MFC画图,且能够实现一些基本的功能。
在本次设计的初期,只实现了小部分功能,对画图及其不了解,且对窗口设置的不合理倒是画面相当丑陋,后来通过请教同学、老师、上网查资料,最终设计出比较满意的画面。
还有,本次设计的秒表设计,由于它的刷新频率太高,导致屏幕闪烁严重,画面看起来很不舒畅,后面经查资料发现双缓冲技术能够解决这一问题,也将这个问题成功解决。
觉得本次设计中的采用不同的计时器来控制不同的模块效果比较好,对每一模块进行独立的控制很短单,而且思路清晰。
总之在本次实验中我学到了很多知识,而且培养了自己的自学能力,逼着自己进行查阅大量的资料。在本次实验中自己对程序一直修改,一直增加功能和改善画面,大大的提高了自己的动手能力。
附:源代码及详细注释 (由于使用的MFC向导形成工程只附部分核心代码)
在public:
int year;
int month;
中加
int day;
int hour;
int second;
int minute;
int watch;
int wday;
CPen
m_HouPen,m_NoHouPen,m_MinPen,m_NoMinPen,m_SecPen,m_NoSecPen,m_circle,m_RectPen;
HBRUSH
m_HurkBrush,m_CircleBrush,m_RecBrush,m_RectBrush,m_RectBrush1,m_RectBrush2;
CPoint m_Center;
double m_Radious;
CPoint m_Hour[2],m_OldHour[2];
CPoint m_Minute[2],m_OldMin[2];
CPoint m_Second[2],m_OldSec[2];
CRect m_WatchStart;
CRect m_WatchStop;
void SetClock(int hour,int minute,int second);
CPoint GetPoint(int nlenth,int nvalue);
#include "stdafx.h"
#include "Clock.h"
#define PI 3.14159265
#include
#include"SetTimeDlg.h"
#include "ClockDoc.h"
#include "ClockView.h"
#include
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
中初始化设置变量
/////////////////////////////////////////////////////////////////////////////
// CSetTimeDlg dialog
CSetTimeDlg::CSetTimeDlg(CWnd* pParent /*=NULL*/)
: CDialog(CSetTimeDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CSetTimeDlg)
long now = time(NULL);
tm local = *localtime(&now);
m_Day = _mday;;
m_Hour = _hour;
m_Minute = _min;
m_Month = _mon + 1;
m_Second = _sec;
m_Year = _year+1900;
m_Wday = _wday;
//}}AFX_DATA_INIT
}
void CSetTimeDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CSetTimeDlg)
DDX_Text(pDX, ID_DAY, m_Day);
DDV_MinMaxInt(pDX, m_Day, 1, 31);
DDX_Text(pDX, ID_HOUR, m_Hour);
DDV_MinMaxInt(pDX, m_Hour, 0, 23);
DDX_Text(pDX, ID_MINUTE, m_Minute);
DDV_MinMaxInt(pDX, m_Minute, 0, 59);
DDX_Text(pDX, ID_MONTH, m_Month);
DDV_MinMaxInt(pDX, m_Month, 1, 12);
DDX_Text(pDX, ID_SECOND, m_Second);
DDV_MinMaxInt(pDX, m_Second, 0, 59);
DDX_Text(pDX, ID_YEAR, m_Year);
DDV_MinMaxInt(pDX, m_Year, 0, 3000);
DDX_Text(pDX, ID_WDAY, m_Wday);
DDV_MinMaxInt(pDX, m_Wday, 0, 6);
//}}AFX_DATA_MAP
}
/////////////////////////////////////////////////////////////////////////////
// CClockView
IMPLEMENT_DYNCREATE(CClockView, CView)
BEGIN_MESSAGE_MAP(CClockView, CView)
//{{AFX_MSG_MAP(CClockView)
ON_COMMAND(ID_START, OnStart)
ON_COMMAND(ID_STOP, OnStop)
ON_COMMAND(ID_STETIME, OnStetime)
ON_WM_TIMER()
ON_WM_LBUTTONDOWN()
//}}AFX_MSG_MAP
// Standard printing commands
ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_PREVIEW,
CView::OnFilePrintPreview)
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CClockView construction/destruction
CClockView::CClockView()
{ //初始化时间为系统时间
long now = time(NULL);
tm local = *localtime(&now);
year = _year+1900;
month = _mon + 1;
day = _mday;
minute = _min;
hour = _hour;
second = _sec;
wday = _wday;
//创建画笔、画刷
m_Pen(PS_SOLID,5 ,RGB(255,50,0));
m_Pen(PS_SOLID,5 ,RGB( 204,255, 102 ));
m_Pen(PS_SOLID,3 ,RGB(0,0,255));
m_Pen(PS_SOLID,3 ,RGB( 204,255, 102 ));
m_Pen(PS_SOLID,1 ,RGB(0,0,0));
m_Pen(PS_SOLID,1 ,RGB( 204,255, 102 ));
m_Pen(PS_SOLID,2 ,RGB(0,0,0));
m_Pen(PS_SOLID,3 ,RGB(204,102,0));
m_HurkBrush = CreateSolidBrush( RGB( 255,0, 0 ));
m_CircleBrush = CreateSolidBrush( RGB( 204,255, 102 ));
m_RecBrush = CreateSolidBrush( RGB( 255,255, 51 ));
m_RectBrush = CreateSolidBrush( RGB( 102,51, 0 ));
m_RectBrush1 = CreateSolidBrush( RGB( 255,51, 0));
m_RectBrush2 = CreateSolidBrush( RGB( 0,102, 204));
m_Center.x = 200;
m_Center.y = 200;
m_Radious = 185;
SetClock(hour,minute,second);
watch = 0;
m_WatchStart =CRect(480,310,560,340);
m_WatchStop= CRect(590,310,670,340);
// TODO: add construction code here
}
CClockView::~CClockView()
{
}
void CClockView::SetClock(int hour,int minute,int second)
{
hour = hour*5;//时针每过1小时转5*PI/3
hour = hour + minute/12; //时针每过12分钟转PI/3
//保存时钟原位置
m_OldHour[0] = m_Hour[0];
m_OldHour[1] = m_Hour[1];
//计算时针当前位置
m_Hour[0] = GetPoint(int(m_Radious/2),hour);
m_Hour[1] = GetPoint(7,hour + 30);
//保存分针位置
m_OldMin[0] = m_Minute[0];
m_OldMin[1] = m_Minute[1];
//计算分针位置
m_Minute[0] = GetPoint(int(m_Radious*7/10),minute);
m_Minute[1] = GetPoint(10,minute + 30);
//保存秒针位置
m_OldSec[0] = m_Second[0];
m_OldSec[1] = m_Second[1];
//计算秒针位置
m_Second[0] = GetPoint(int(m_Radious*8/10),second);
m_Second[1] = GetPoint(30, second + 30);
}
CPoint CClockView::GetPoint(int nlenth,int nvalue)
{ // 用来计算指针的俩端点的位置和 表盘上刻度的位置
CPoint p;
double angle = nvalue *PI/30 ;
p.x = m_Center.x + (int)(nlenth*sin(angle));
p.y = m_Center.y - (int)(nlenth*cos(angle));
return p;
}
BOOL CClockView::PreCreateWindow(CREATESTRUCT& cs)
{
// TODO: Modify the Window class or styles here by modifying
// the CREATESTRUCT cs
return CView::PreCreateWindow(cs);
}
/////////////////////////////////////////////////////////////////////////////
// CClockView drawing
//*********画表***********//
void CClockView::OnDraw(CDC* pDC)
{
SetTimer(1,1000,NULL);
CClockDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
CRect rc;
CDC dcMem;
GetClientRect(&rc);
CBitmap bmp; //内存中承载临时图象的位图
CompatibleDC(pDC); //依附窗口DC创建兼容内存DC
//创建兼容位图(必须用pDC创建,否则画出的图形变成黑色)
CompatibleBitmap(pDC,(),());
CBitmap *pOldBit=Object(&bmp); //按原来背景填充客户区,不然会是黑色
lidRect(rc,RGB(255,255,255));
//画图,添加你要画图的代码,不过用dcMem画,而不是pDc
//画表盘最外面的大圆并填充颜色
Object(m_circle);
e(m_Center.x - 195,m_Center.y
-195,m_Center.x + 195,m_Center.y + 195);
Object(m_CircleBrush);
e(m_Center.x - 195,m_Center.y
-195,m_Center.x + 195,m_Center.y + 195);
//画60个刻度
for(int i = 0; i < 60 ; i++)
{
Object(m_SecPen);
//画小时刻度
if(i%5 ==0)
{
CString m_Number;
ode(TRANSPARENT); //将textout背景透明
m_("%2d",(i/5==0 ? 12 : i/5));
t(pt.x-7,pt.y-7,m_Number);
}//画小时之间的刻度
else
{
CPoint pt = GetPoint(m_Radious,i);
Object(m_HurkBrush);
e(pt.x - 2,pt.y -2,pt.x + 2,pt.y + 2);
}
}
//画时针
if(m_OldHour[0] != m_Hour[0])
{ //用表盘背景色覆盖原来的时针
Object(m_NoHouPen);
dcMem. MoveTo(m_OldHour[0]);
(m_OldHour[1]);
//时针绘制
Object(m_HouPen);
dcMem. MoveTo(m_Hour[0]);
(m_Hour[1]);
}
else
{
Object(m_HouPen);
dcMem. MoveTo(m_Hour[0]);
(m_Hour[1]);
}
//画分针
if(m_OldMin[0] != m_Minute[0])
{ //用表盘背景覆盖原来的分针
Object(m_NoMinPen);
dcMem. MoveTo(m_OldMin[0]);
(m_OldMin[1]);
//分针绘制
Object(m_MinPen);
dcMem. MoveTo(m_Minute[0]);
(m_Minute[1]);
}
else
{ Object(m_MinPen);
dcMem. MoveTo(m_Minute[0]);
(m_Minute[1]);
}
//画秒针
if(m_OldSec[0] != m_Second[0])
{
//用表盘背景覆盖原来的秒针
Object(m_NoSecPen);
dcMem. MoveTo(m_OldSec[0]);
(m_OldSec[1]);
//秒针绘制
Object(m_SecPen);
dcMem. MoveTo(m_Second[0]);
(m_Second[1]);
}
else
{ Object(m_SecPen);
dcMem. MoveTo(m_Second[0]);
(m_Second[1]);
}
//数字时钟显示
//画一个矩形框然后将其用颜色填充
Object(m_RectPen);
gle(450,30,700,180);
Object(m_RectBrush);
gle(450,30,700,180);
//显示“当前时间”以及日期、星期、时间
t(530,10,"当前时间");
CString m_Date, m_Time,m_Wday;
m_("%4d年%2d月%2d日",year,month,day);
t(510,70,m_Date);
switch(wday)
{ case 0:
m_("星期日");
t(540,88,m_Wday);
break;
case 1:
m_("星期一");
t(540,88,m_Wday);
break;
case 2:
m_("星期二");
t(540,88,m_Wday);
break;
case 3:
m_("星期三");
t(540,88,m_Wday);
break;
case 4:
m_("星期四");
t(540,88,m_Wday);
break;
case 5:
m_("星期五");
t(540,88,m_Wday);
break;
}
m_("%4d时%2d分%2d秒",hour,minute,second);
t(510,118,m_Time);
//秒表显示
//画一个矩形框然后将其用颜色填充
Object(m_RecBrush);
gle(450,220,700,370);
//输出秒表、计时
t(545,200,"秒表");
int minSec = watch%100;
int Sec = (watch/100)%60;
int Min = (watch/100)/60;
m_("%02d:%02d:%02d",Min,Sec,minSec);
t(535,260,m_Time);
//画俩个不同颜色的矩形框
Object(m_RectBrush1);
gle(&m_WatchStart);
Object(m_RectBrush2);
gle(&m_WatchStop);
//在俩矩形框内写"启动"停止"
t(m_ + 18,m_ +
5,"启动");
t(m_ + 18,m_ +
5,"停止");
pDC->BitBlt(0,0,(),(),&dcMem,0,0,SRCCOPY); //将内存DC上的图象拷贝到前台
//绘图完成后的清理
DC(); //删除DC
Object(); //删除位图
}
/////////////////////////////////////////////////////////////////////////////
// CClockView printing
BOOL CClockView::OnPreparePrinting(CPrintInfo* pInfo)
{
// default preparation
return DoPreparePrinting(pInfo);
}
void CClockView::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo*
/*pInfo*/)
{
// TODO: add extra initialization before printing
}
void CClockView::OnEndPrinting(CDC* /*pDC*/, CPrintInfo*
/*pInfo*/)
{
// TODO: add cleanup after printing
}
/////////////////////////////////////////////////////////////////////////////
// CClockView diagnostics
#ifdef _DEBUG
void CClockView::AssertValid() const
{
CView::AssertValid();
}
void CClockView::Dump(CDumpContext& dc) const
{
CView::Dump(dc);
}
CClockDoc* CClockView::GetDocument() // non-debug version
is inline
{
ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CClockDoc)));
return (CClockDoc*)m_pDocument;
}
#endif //_DEBUG
/////////////////////////////////////////////////////////////////////////////
// CClockView message handlers
//菜单系统时间
void CClockView::OnStart()
{
// TODO: Add your command handler code here
long now = time(NULL);
tm local = *localtime(&now);
year = _year+1900;
month = _mon + 1;
day = _mday;
minute = _min;
hour = _hour;
second = _sec;
wday = _wday;
SetTimer(1,1000,NULL);
}
//菜单时间停止
void CClockView::OnStop()
{
// TODO: Add your command handler code here
KillTimer(1);
}
//菜单时间设置
void CClockView::OnStetime()
{
// TODO: Add your command handler code here
CSetTimeDlg SetDlg;
if(l()==IDOK)
{ day = SetDlg.m_Day ;
hour = SetDlg.m_Hour ;
minute = SetDlg.m_Minute;
month = SetDlg.m_Month;
second = SetDlg.m_Second ;
year = SetDlg.m_Year ;
wday = SetDlg.m_Wday;
}
SetClock(hour,minute,second);
Invalidate(TRUE);
}
void CClockView::OnTimer(UINT nIDEvent)
{
// TODO: Add your message handler code here and/or call
default
//时钟定时器消息处理
if(nIDEvent == 1||nIDEvent == 3)
{
second++;
if(second > 59)
{
}
if(minute > 59)
{
minute = 0;
second = 0;
minute++;
hour++;
}
if(hour > 23)
{
}
if(wday > 6)
{
hour = 0;
day++;
wday++;
}
wday = 0;
switch(month)
{
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
if(day > 31)
{
day = 1;
month++;
}
break;
case 4:
case 6:
case 9:
case 11:
if(day > 30)
{
day = 1;
month++;
}
break;
case 2:
if(year%4 == 0&&day > 29)
{day = 1;
month++;}
if(year%4 != 0&&day > 28)
{day = 1;
month++;}
break;
}
if(month > 12)
{ month = 1;
}
SetClock(hour,minute,second);
Invalidate(FALSE);
}
year++;
//秒表定时器消息处理
if(nIDEvent== 2)
{
}
}
// 按钮时间定时器
void CClockView::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call
default
if(m_ct(point))
{
watch = 0;
CView::OnTimer(nIDEvent);
watch++;
Invalidate(FALSE);
}
if(m_ct(point))
SetTimer(2,10,NULL);
SetTimer(3,1000,NULL);
{
}
CView::OnLButtonDown(nFlags, point);
KillTimer(2);
KillTimer(3);
}


发布评论