《Windows编程之定时器用于时钟.pdf》由会员分享,可在线阅读,更多相关《Windows编程之定时器用于时钟.pdf(22页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、WindowsWindows编程之定时器用于时钟编程之定时器用于时钟时钟是定时器最明显的应用,因此让我们来看看两个时钟,一个数字时钟,一个模拟时钟。建立数字时钟建立数字时钟程序8-3所示的 DIGCLOCK 程序,使用类似 LED 的7个显示方块显示了目前的时间。程序8-3DIGCLOCKDIGCLOCK.C/*-DIGCLOCK.C-Digital Clock(c)Charles Petzold,1998-*/#include#define ID_TIMER1LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);int WINAPI WinMai
2、n(HINSTANCE hInstance,HINSTANCE hPrevInstance,PSTRszCmdLine,intiCmdShow)static TCHAR szAppName=TEXT(DigClock);HWNDhwnd;MSGmsg;WNDCLASSwndclass;wndclass.style=CS_HREDRAW|CS_VREDRAW;wndclass.lpfnWndProc=WndProc;wndclass.cbClsExtra=0;wndclass.cbWndExtra=0;wndclass.hInstance=hInstance;wndclass.hIcon=Loa
3、dIcon(NULL,IDI_APPLICATION);wndclass.hCursor=LoadCursor(NULL,IDC_ARROW);wndclass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);wndclass.lpszMenuName=NULL;wndclass.lpszClassName=szAppName;if(!RegisterClass(&wndclass)MessageBox(NULL,TEXT(Program requires Windows NT!),szAppName,MB_ICONERROR);return
4、 0;hwnd=CreateWindow(szAppName,TEXT(Digital Clock),WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,hInstance,NULL);ShowWindow(hwnd,iCmdShow);UpdateWindow(hwnd);while(GetMessage(&msg,NULL,0,0)TranslateMessage(&msg);DispatchMessage(&msg);return msg.wParam;void Dis
5、playDigit(HDC hdc,int iNumber)static BOOLfSevenSegment 107=1,1,1,0,1,1,1,/00,0,1,0,0,1,0,/11,0,1,1,1,0,1,/21,0,1,1,0,1,1,/30,1,1,1,0,1,0,/41,1,0,1,0,1,1,/51,1,0,1,1,1,1,/61,0,1,0,0,1,0,/71,1,1,1,1,1,1,/81,1,1,1,0,1,1 ;/9static POINT ptSegment 76=7,6,11,2,31,2,35,6,31,10,11,10,6,7,10,11,10,31,6,35,2,
6、31,2,11,36,7,40,11,40,31,36,35,32,31,32,11,7,36,11,32,31,32,35,36,31,40,11,40,6,37,10,41,10,61,6,65,2,61,2,41,36,37,40,41,40,61,36,65,32,61,32,41,7,66,11,62,31,62,35,66,31,70,11,70 ;intiSeg;for(iSeg=0;iSeg 7;iSeg+)if(fSevenSegment iNumberiSeg)Polygon(hdc,ptSegment iSeg,6);void DisplayTwoDigits(HDC h
7、dc,int iNumber,BOOL fSuppress)if(!fSuppress|(iNumber/10!=0)DisplayDigit(hdc,iNumber/10);OffsetWindowOrgEx(hdc,-42,0,NULL);DisplayDigit(hdc,iNumber%10);OffsetWindowOrgEx(hdc,-42,0,NULL);void DisplayColon(HDC hdc)POINT ptColon 24=2,21,6,17,10,21,6,25,2,51,6,47,10,51,6,55 ;Polygon(hdc,ptColon 0,4);Poly
8、gon(hdc,ptColon 1,4);OffsetWindowOrgEx(hdc,-12,0,NULL);void DisplayTime(HDC hdc,BOOL f24Hour,BOOL fSuppress)SYSTEMTIME st;GetLocalTime(&st);if(f24Hour)DisplayTwoDigits(hdc,st.wHour,fSuppress);elseDisplayTwoDigits(hdc,(st.wHour%=12)?st.wHour:12,fSuppress);DisplayColon(hdc);DisplayTwoDigits(hdc,st.wMi
9、nute,FALSE);DisplayColon(hdc);DisplayTwoDigits(hdc,st.wSecond,FALSE);LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)static BOOLf24Hour,fSuppress;static HBRUSHhBrushRed;static intcxClient,cyClient;HDChdc;PAINTSTRUCTps;TCHARszBuffer 2;switch(message)caseWM_CREATE:hBrushRed
10、=CreateSolidBrush(RGB(255,0,0);SetTimer(hwnd,ID_TIMER,1000,NULL);/fall throughcaseWM_SETTINGCHANGE:GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_ITIME,szBuffer,2);f24Hour=(szBuffer0=1);GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_ITLZERO,szBuffer,2);fSuppress=(szBuffer0=0);InvalidateRect(hwnd,NULL,TRUE);retu
11、rn 0;caseWM_SIZE:cxClient=LOWORD(lParam);cyClient=HIWORD(lParam);return 0;caseWM_TIMER:InvalidateRect(hwnd,NULL,TRUE);return 0;caseWM_PAINT:hdc=BeginPaint(hwnd,&ps);SetMapMode(hdc,MM_ISOTROPIC);SetWindowExtEx(hdc,276,72,NULL);SetViewportExtEx(hdc,cxClient,cyClient,NULL);SetWindowOrgEx(hdc,138,36,NUL
12、L);SetViewportOrgEx(hdc,cxClient/2,cyClient/2,NULL);SelectObject(hdc,GetStockObject(NULL_PEN);SelectObject(hdc,hBrushRed);DisplayTime(hdc,f24Hour,fSuppress);EndPaint(hwnd,&ps);return 0;caseWM_DESTROY:KillTimer(hwnd,ID_TIMER);DeleteObject(hBrushRed);PostQuitMessage(0);return 0;return DefWindowProc(hw
13、nd,message,wParam,lParam);DIGCLOCK 窗口如图8-1所示。虽然,在图8-1中您看不到时钟的数字是红色的。DIGCLOCK 的窗口消息处理程序在处理WM_CREATE消息处理期间建立了一个红色的画刷并在处理WM_DESTROY消息处理期间清除它。WM_CREATE 消息也为 DIGCLOCK 设定了一个一秒的定时器,该定时器在处理 WM_DESTROY 消息处理期间被终止(待会将讨论对 GetLocaleInfo 的呼叫)。在收到 WM_TIMER 消息后,DIGCLOCK 的窗口过程调用 InvalidateRect 简单地使整个窗口无效。这不是最佳方法,因为每
14、秒整个窗口都要被擦除和重画,有时会引起显示器的闪烁。依据目前的时间使窗口需要更新的部分无效是最好的解决方法。然而,在逻辑上这样做的确很复杂。在处理 WM_TIMER 消息处理期间使窗口无效会迫使所有程序的真正活动转入 WM_PAINT。DIGCLOCK 在 WM_PAINT 消息一开始将映像方式设定为 MM_ISOTROPIC。这样,DIGCLOCK 将使用水平方向和垂直方向相等的轴。这些轴(由 SetWindowExtEx 呼叫设定)是水平276个单位,垂直72个单位。当然,这些轴定得有点太随意了,但它们是按照时钟数字元的大小和间距安排的。DIGCLOCK 将窗口原点设定为(138,36),
15、这是窗口范围的中心;将视埠原点设定为(cxClient/2,cyClient/2)。这意味着时钟的显示位于 DIGCLOCK 显示区域的中心,但是该 DIGCLOCK也可以使用在显示屏左上角的原点(0,0)的轴。然后 WM_PAINT 将目前画刷设定为之前建立的红画刷,将目前画笔设定为 NULL_PEN,并呼叫DIGCLOCK 中的函数 DisplayTime。取得目前时间取得目前时间DisplayTime 函数开始呼叫 Windows 函数 GetLocalTime,它带有一个的 SYSTEMTIME 结构的参数,在 WINBASE.H 中定义为:typedef struct _SYSTEM
16、TIMEWORDwYear;WORDwMonth;WORDwDayOfWeek;WORDwDay;WORDwHour;WORDwMinute;WORDwSecond;WORDwMilliseconds;SYSTEMTIME,*PSYSTEMTIME;很明显,SYSTEMTIME 结构包含日期和时间。月份由1开始递增(也就是说,一月是1),星期由0开始递增(星期天是0)。wDay 成员是本月目前的日子,也是由1开始递增的。SYSTEMTIME 主要用于 GetLocalTime 和 GetSystemTime 函数。GetSystemTime 函数传回目前的世界时间(Coordinated Un
17、iversal Time,UTC),大概与英国格林威治时间相同。GetLocalTime 函数传回当地时间,依据计算机所在的时区。这些值的精确度完全决定于使用者所调整的时间精确度以及是否指定了正确的时区。可以双击工作列的时间显示来检查计算机上的时区设定。第二十三章会有一个程序,能够通过 Internet 精确地设定时间。Windows 还有 SetLocalTime 和 SetSystemTime 函数,以及在/Platform SDK/Windows BaseServices/General Library/Time 中说明的其它与时间有关的函数。显示数字和冒号显示数字和冒号如果 DIGCL
18、OCK 使用一种仿真7段显示的字体将会简单一些。否则,它就得使用 Polygon 函数做所有的工作。DIGCLOCK 中的 DisplayDigit 函数定义了两个数组。fSevenSegment 数组有7个 BOOL 值,用于从0到9的每个十进制数。这些值指出了哪一段需要显示(为1),哪一段不需要显示(为0)。在这个数组中,7段由上到下、由左到右排序。7段中的每个段都是一个6边的多边形。ptSegment 数组是一个 POINT 结构的数组,指出了7个段中每个点的图形坐标。每个数字由下列程序代码画出:for(iSeg=0;iSeg 7;iSeg+)if(fSevenSegment iNumb
19、eriSeg)Polygon(hdc,ptSegment iSeg,6);类似地(但更简单),DisplayColon 函数在小时与分钟、分钟与秒之间画一个冒号。数字是42个单位宽,冒号是12个单位宽,因此6个数字与2个冒号,总宽度是276个单位,SetWindowExtEx 呼叫中使用了这个大小。回 到 DisplayTime 函 数,原 点 位 于 最 左 数 字 位 置 的 左 上 角。DisplayTime 呼 叫DisplayTwoDigits,DisplayTwoDigits 呼 叫 DisplayDigit 两 次,并 且 在 每 次 呼 叫OffsetWindowOrgEx 后
20、,将窗口原点向右移动42个单位。类似地,DisplayColon 函数在画完冒号后,将窗口原点向右移动12个单位。用这种方法,不管对象出现在窗口内的哪个地方,函数对数字和冒号都使用同样的坐标。这个程序的其它技巧是以12小时或24小时的格式显示时间以及当最左边的小时数字为0时不显示它。国际化国际化尽管像 DIGCLOCK 这样显示时间是非常简单的,但是要显示复杂的日期和时间还是要依赖Windows 的国际化支持。格式化日期和时间的最简单的方法是呼叫 GetDateFormat 和GetTimeFormat 函 数。这 些 函 数 在/Platform SDK/Windows Base Servi
21、ces/GeneralLibrary/String Manipulation/String Manipulation Reference/String ManipulationFunctions 中有记载,但是它们在/Platform SDK/Windows Base Services/InternationalFeatures/National Language Support 中进行了说明。这些函数接受 SYSTEMTIME 结构并且依据使用者在控制台的区域设定 程序中所做的选择而将日期和时间格式化。DIGCLOCK 不能使用 GetDateFormat 函数,因为它只知道显示数字和冒号,
22、然而,DIGCLOCK应该能够根据使用者的参数选择来显示12小时或24小时的格式,并禁止(或不禁止)开头的小时数字。您可以从 GetLocaleInfo 函数中取得这种信息。虽然 GetLocaleInfo 在/PlatformSDK/Windows Base Services/General Library/String Manipulation/String ManipulationReference/String Manipulation Functions 中有记载,但是这个函数使用的标识符在/PlatformSDK/WindowsBaseServices/International
23、Features/NationalLanguageSupport/National Language Support Constants 中有说明。DIGCLOCK 在 处 理 WM_CREATE 消 息 时,最 初 呼 叫 GetLocaleInfo 两 次,第 一 次 使 用LOCALE_ITIME 标识符(确定使用的是12小时还是24小时格式),然后使用 LOCALE_ITLZERO标识符(在小时显示中禁止前面显示0)。GetLocaleInfo 函数在字符串中传回所有的信息,但是在大多数情况下把字符串转变为整数并不是非常容易。DIGCLOCK 把字符串储存在两个静态变量中并把它们传递给
24、 DisplayTime 函数。如果使用者更改了任何系统设定,则会将 WM_SETTINGCHANGE 消息传送给所有的应用程序。DIGCLOCK 通过再次呼叫 GetLocaleInfo 处理这个消息。以这种方式,您可以在控制台的区域设定 程序中进行不同的设定来实验一下。在理论上,DIGCLOCK 也应该使用 LOCALE_STIME 标识符呼叫 GetLocaleInfo。这会传回使用者为时间的小时、分钟和秒等单个部分选择的字符。因为 DIGCLOCK 被设定为仅显示冒号,所以不管选择了什么,都会得到冒号。要指出时间是 A.M.或 P.M.,应用程序可以使用带有LOCALE_S1159和
25、LOCALE_S2359标识符的 GetLocaleInfo 函数。这些标识符使程序获得适合于使用者国家/地区和语言的字符串。我们也可以让 DIGCLOCK 处理 WM_TIMECHANGE 消息,这样它将系统时间与日期发生变化的消息通知应用程序。DIGCLOCK 因 WM_TIMER 消息而每秒更新一次,实际上没有必要这样作,对WM_TIMECHANGE 消息的处理使得每分钟更新一次的时钟变得更为合理。建立模拟时钟建立模拟时钟模拟时钟不必关心国际化问题,但是由于图形所引起的复杂性却抵消了这种简化。为了正确地产生时钟,您需要知道一些三角函数。CLOCK 如程序8-4所示。程序8-4 CLOCK
26、CLOCK.C/*-CLOCK.C-Analog Clock Program(c)Charles Petzold,1998-*/#include#include#define ID_TIMER1#define TWOPI(2*3.14159)LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,PSTR szCmdLine,int iCmdShow)static TCHAR szAppName=TEXT(Clock);HWN
27、Dhwnd;MSGmsg;WNDCLASSwndclass;wndclass.style=CS_HREDRAW|CS_VREDRAW;wndclass.lpfnWndProc=WndProc;wndclass.cbClsExtra=0;wndclass.cbWndExtra=0;wndclass.hInstance=hInstance;wndclass.hIcon=NULL;wndclass.hCursor=LoadCursor(NULL,IDC_ARROW);wndclass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);wndclass
28、.lpszMenuName=NULL;wndclass.lpszClassName=szAppName;if(!RegisterClass(&wndclass)MessageBox(NULL,TEXT(Program requires Windows NT!),szAppName,MB_ICONERROR);return 0;hwnd=CreateWindow(szAppName,TEXT(Analog Clock),WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,hIn
29、stance,NULL);ShowWindow(hwnd,iCmdShow);UpdateWindow(hwnd);while(GetMessage(&msg,NULL,0,0)TranslateMessage(&msg);DispatchMessage(&msg);return msg.wParam;void SetIsotropic(HDC hdc,int cxClient,int cyClient)SetMapMode(hdc,MM_ISOTROPIC);SetWindowExtEx(hdc,1000,1000,NULL);SetViewportExtEx(hdc,cxClient/2,
30、-cyClient/2,NULL);SetViewportOrgEx(hdc,cxClient/2,cyClient/2,NULL);void RotatePoint(POINT pt,int iNum,int iAngle)inti;POINT ptTemp;for(i=0;i iNum;i+)ptTemp.x=(int)(pti.x*cos(TWOPI*iAngle/360)+pti.y*sin(TWOPI*iAngle/360);ptTemp.y=(int)(pti.y*cos(TWOPI*iAngle/360)-pti.x*sin(TWOPI*iAngle/360);pti=ptTem
31、p;void DrawClock(HDC hdc)intiAngle;POINT pt3;for(iAngle=0;iAngle wHour*30)%360+pst-wMinute/2;iAngle1=pst-wMinute*6;iAngle2=pst-wSecond*6;memcpy(ptTemp,pt,sizeof(pt);for(i=fChange?0:2;i 3;i+)RotatePoint(ptTempi,5,iAnglei);Polyline(hdc,ptTempi,5);LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM
32、wParam,LPARAM lParam)static intcxClient,cyClient;static SYSTEMTIME stPrevious;BOOLfChange;HDChdc;PAINTSTRUCTps;SYSTEMTIMEst;switch(message)caseWM_CREATE:SetTimer(hwnd,ID_TIMER,1000,NULL);GetLocalTime(&st);stPrevious=st;return 0;caseWM_SIZE:cxClient=LOWORD(lParam);cyClient=HIWORD(lParam);return 0;cas
33、eWM_TIMER:GetLocalTime(&st);fChange=st.wHour!=stPrevious.wHour|st.wMinute!=stPrevious.wMinute;hdc=GetDC(hwnd);SetIsotropic(hdc,cxClient,cyClient);SelectObject(hdc,GetStockObject(WHITE_PEN);DrawHands(hdc,&stPrevious,fChange);SelectObject(hdc,GetStockObject(BLACK_PEN);DrawHands(hdc,&st,TRUE);ReleaseDC
34、(hwnd,hdc);stPrevious=st;return 0;caseWM_PAINT:hdc=BeginPaint(hwnd,&ps);SetIsotropic(hdc,cxClient,cyClient);DrawClock(hdc);DrawHands(hdc,&stPrevious,TRUE);EndPaint(hwnd,&ps);return 0;caseWM_DESTROY:KillTimer(hwnd,ID_TIMER);PostQuitMessage(0);return 0;return DefWindowProc(hwnd,message,wParam,lParam);
35、CLOCK 屏幕显示如图8-2。等方向性(isotropic)映像对于这样的应用来说是理想的,CLOCK.C 中的 SetIsotropic 函数负责设定此模式。在呼叫 SetMapMode 之后,SetIsotropic 将窗口范围设定为1000,并将视端口范围设定为显示区域的一半宽度和显示区域的负的一半高度。视端口原点被设定为显示区域的中心。我在第五章中讨论过,这将建立一个笛卡儿坐标系,其点(0,0)位于显示区域的中心,在所有方向上的范围都是1000。RotatePoint 函数是用到三角函数的地方,此函式的三个参数分别是一个或者多个点的数组、数组中点的个数以及以度为单位的旋转角度。函式以
36、原点为中心按顺时针方向(这对一个时钟正合适)旋转这些点。例如,如果传给函式的点是(0,100)即12:00的位置而角度为90度,那么该点将被变换为(100,0)即3:00。它使用下列公式来做到这一点:x=x*cos(a)+y*sin(a)y=y*cos(a)-x*sin(a)RotatePoint 函数在绘制时钟表面的点和表针时都是有用的,我们将马上看到这一点。DrawClock 函数绘制60个时钟表面的点,从顶部(12:00)开始,其中每个点离原点900单位,因此第一个点位于(0,900),此后的每个点按顺时针依次增加6度。这些点中的 l2个直径为100个单位;其余的为33个单位。使用 El
37、lipse 函数来画点。DrawHands 函数绘制时钟的时针、分针和秒针。定义表针轮廓(当它们垂直向上时的形状)的坐标存放在一个 POINT 结构的数组中。根据时间,这些坐标使用 RotatePoint 函数进行旋转,并用 Windows 的 Polyline 函数进行显示。注意时针和分针只有当传递给 DrawHands 的bChange 参数为 TRUE 时才被显示。当程序更新时钟的表针时,大多数情况下时针和分针不需要重画。现在让我们将注意力转到窗口消息处理程序。在 WM_CREATE 消息处理期间,窗口消息处理程序取得目前时间并将它存放在名为 dtPrevious 的变量中,这个变量将在以后被用于确定时针或者分针从上次更新以来是否改变过。第一次绘制时钟是在第一个 WM_PAINT 消息处理期间,这只不过是依次呼叫 SetIsotropic、DrawClock 和 DrawHands,后者的 bChange 参数被设定为 TRUE。在 WM_TIMER 消息处理期间,WndProc 首先取得新的时间并确定是否需要重新绘制时针和分针。如果需要,则使用一个白色画笔和上一次时间绘制所有的表针,从而有效地擦除它们。否则,只对秒针使用白色画笔进行擦除,然后,再使用一个黑色画笔绘制所有的表针。来自:谷融网址:
限制150内