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);

}