?? 8. 定時器.txt
字號:
return 0 ;
case WM_TIMER:
InvalidateRect (hwnd, NULL, TRUE) ;
return 0 ;
case WM_PAINT:
hdc = BeginPaint (hwnd, &ps) ;
SetMapMode (hdc, MM_ISOTROPIC) ;
SetWindowExtEx (hdc, 276, 72, NULL) ;
SetViewportExtEx (hdc, cxClient, cyClient, NULL) ;
SetWindowOrgEx (hdc, 138, 36, NULL) ;
SetViewportOrgEx (hdc, cxClient / 2, cyClient / 2, NULL) ;
SelectObject (hdc, GetStockObject (NULL_PEN)) ;
SelectObject (hdc, hBrushRed) ;
DisplayTime (hdc, f24Hour, fSuppress) ;
EndPaint (hwnd, &ps) ;
return 0 ;
case WM_DESTROY:
KillTimer (hwnd, ID_TIMER) ;
DeleteObject (hBrushRed) ;
PostQuitMessage (0) ;
return 0 ;
}
return DefWindowProc (hwnd, message, wParam, lParam) ;
}
DIGCLOCK窗口如圖8-1所示。
圖8-1 DIGCLOCK的屏幕顯示
雖然,在圖8-1中您看不到時鐘的數(shù)字是紅色的。DIGCLOCK的窗口消息處理程序在處理WM_CREATE消息處理期間建立了一個紅色的畫刷并在處理WM_DESTROY消息處理期間清除它。WM_CREATE消息也為DIGCLOCK設(shè)定了一個一秒的定時器,該定時器在處理WM_DESTROY消息處理期間被終止(待會將討論對GetLocaleInfo的呼叫)。
在收到WM_TIMER消息后,DIGCLOCK的窗口過程調(diào)用InvalidateRect簡單地使整個窗口無效。這不是最佳方法,因為每秒整個窗口都要被擦除和重畫,有時會引起顯示器的閃爍。依據(jù)目前的時間使窗口需要更新的部分無效是最好的解決方法。然而,在邏輯上這樣做的確很復(fù)雜。
在處理WM_TIMER消息處理期間使窗口無效會迫使所有程序的真正活動轉(zhuǎn)入WM_PAINT。DIGCLOCK在WM_PAINT消息一開始將映像方式設(shè)定為MM_ISOTROPIC。這樣,DIGCLOCK將使用水平方向和垂直方向相等的軸。這些軸(由SetWindowExtEx呼叫設(shè)定)是水平276個單位,垂直72個單位。當(dāng)然,這些軸定得有點太隨意了,但它們是按照時鐘數(shù)字元的大小和間距安排的。
DIGCLOCK將窗口原點設(shè)定為(138,36),這是窗口范圍的中心;將視埠原點設(shè)定為(cxClient / 2,cyClient / 2)。這意味著時鐘的顯示位于DIGCLOCK顯示區(qū)域的中心,但是該DIGCLOCK也可以使用在顯示屏左上角的原點(0, 0)的軸。
然后WM_PAINT將目前畫刷設(shè)定為之前建立的紅畫刷,將目前畫筆設(shè)定為NULL_PEN,并呼叫DIGCLOCK中的函數(shù)DisplayTime。
取得目前時間
DisplayTime函數(shù)開始呼叫Windows函數(shù)GetLocalTime,它帶有一個的SYSTEMTIME結(jié)構(gòu)的參數(shù),在WINBASE.H中定義為:
typedef struct _SYSTEMTIME
{
WORD wYear ;
WORD wMonth ;
WORD wDayOfWeek ;
WORD wDay ;
WORD wHour ;
WORD wMinute ;
WORD wSecond ;
WORD wMilliseconds ;
}
SYSTEMTIME, * PSYSTEMTIME ;
很明顯,SYSTEMTIME結(jié)構(gòu)包含日期和時間。月份由1開始遞增(也就是說,一月是1),星期由0開始遞增(星期天是0)。wDay成員是本月目前的日子,也是由1開始遞增的。
SYSTEMTIME主要用于GetLocalTime和GetSystemTime函數(shù)。GetSystemTime函數(shù)傳回目前的世界時間(Coordinated Universal Time,UTC),大概與英國格林威治時間相同。GetLocalTime函數(shù)傳回當(dāng)?shù)貢r間,依據(jù)計算機(jī)所在的時區(qū)。這些值的精確度完全決定于使用者所調(diào)整的時間精確度以及是否指定了正確的時區(qū)。可以雙擊工作列的時間顯示來檢查計算機(jī)上的時區(qū)設(shè)定。 第二十三章會有一個程序,能夠通過Internet精確地設(shè)定時間。
Windows還有SetLocalTime和SetSystemTime函數(shù),以及在/Platform SDK/Windows Base Services/General Library/Time中說明的其它與時間有關(guān)的函數(shù)。
顯示數(shù)字和冒號
如果DIGCLOCK使用一種仿真7段顯示的字體將會簡單一些。否則,它就得使用Polygon函數(shù)做所有的工作。
DIGCLOCK中的DisplayDigit函數(shù)定義了兩個數(shù)組。fSevenSegment數(shù)組有7個BOOL值,用于從0到9的每個十進(jìn)制數(shù)。這些值指出了哪一段需要顯示(為1),哪一段不需要顯示(為0)。在這個數(shù)組中,7段由上到下、由左到右排序。7段中的每個段都是一個6邊的多邊形。ptSegment數(shù)組是一個POINT結(jié)構(gòu)的數(shù)組,指出了7個段中每個點的圖形坐標(biāo)。每個數(shù)字由下列程序代碼畫出:
for (iSeg = 0 ; iSeg < 7 ; iSeg++)
if ( fSevenSegment [iNumber][iSeg])
Polygon (hdc, ptSegment [iSeg], 6) ;
類似地(但更簡單),DisplayColon函數(shù)在小時與分鐘、分鐘與秒之間畫一個冒號。數(shù)字是42個單位寬,冒號是12個單位寬,因此6個數(shù)字與2個冒號,總寬度是276個單位,SetWindowExtEx呼叫中使用了這個大小。
回到DisplayTime函數(shù),原點位于最左數(shù)字位置的左上角。DisplayTime呼叫DisplayTwoDigits,DisplayTwoDigits呼叫DisplayDigit兩次,并且在每次呼叫OffsetWindowOrgEx后,將窗口原點向右移動42個單位。類似地,DisplayColon函數(shù)在畫完冒號后,將窗口原點向右移動12個單位。用這種方法,不管對象出現(xiàn)在窗口內(nèi)的哪個地方,函數(shù)對數(shù)字和冒號都使用同樣的坐標(biāo)。
這個程序的其它技巧是以12小時或24小時的格式顯示時間以及當(dāng)最左邊的小時數(shù)字為0時不顯示它。
國際化
盡管像DIGCLOCK這樣顯示時間是非常簡單的,但是要顯示復(fù)雜的日期和時間還是要依賴Windows的國際化支持。格式化日期和時間的最簡單的方法是呼叫GetDateFormat和GetTimeFormat函數(shù)。這些函數(shù)在/Platform SDK/Windows Base Services/General Library/String Manipulation/String Manipulation Reference/String Manipulation Functions中有記載,但是它們在/Platform SDK/Windows Base Services/International Features/National Language Support中進(jìn)行了說明。這些函數(shù)接受SYSTEMTIME結(jié)構(gòu)并且依據(jù)使用者在「控制臺」的「區(qū)域設(shè)定」 程序中所做的選擇而將日期和時間格式化。
DIGCLOCK不能使用GetDateFormat函數(shù),因為它只知道顯示數(shù)字和冒號,然而,DIGCLOCK應(yīng)該能夠根據(jù)使用者的參數(shù)選擇來顯示12小時或24小時的格式,并禁止(或不禁止)開頭的小時數(shù)字。您可以從GetLocaleInfo函數(shù)中取得這種信息。雖然GetLocaleInfo在/Platform SDK/Windows Base Services/General Library/String Manipulation/String Manipulation Reference/String Manipulation Functions中有記載,但是這個函數(shù)使用的標(biāo)識符在/Platform SDK/Windows Base Services/International Features/National Language Support/National Language Support Constants中有說明。
DIGCLOCK在處理WM_CREATE消息時,最初呼叫GetLocaleInfo兩次,第一次使用LOCALE_ITIME標(biāo)識符(確定使用的是12小時還是24小時格式),然后使用LOCALE_ITLZERO標(biāo)識符(在小時顯示中禁止前面顯示0)。GetLocaleInfo函數(shù)在字符串中傳回所有的信息,但是在大多數(shù)情況下把字符串轉(zhuǎn)變?yōu)檎麛?shù)并不是非常容易。DIGCLOCK把字符串儲存在兩個靜態(tài)變量中并把它們傳遞給DisplayTime函數(shù)。
如果使用者更改了任何系統(tǒng)設(shè)定,則會將WM_SETTINGCHANGE消息傳送給所有的應(yīng)用程序。DIGCLOCK通過再次呼叫GetLocaleInfo處理這個消息。以這種方式,您可以在「控制臺」的「區(qū)域設(shè)定」 程序中進(jìn)行不同的設(shè)定來實驗一下。
在理論上,DIGCLOCK也應(yīng)該使用LOCALE_STIME標(biāo)識符呼叫GetLocaleInfo。這會傳回使用者為時間的小時、分鐘和秒等單個部分選擇的字符。因為DIGCLOCK被設(shè)定為僅顯示冒號,所以不管選擇了什么,都會得到冒號。要指出時間是A.M.或P.M.,應(yīng)用程序可以使用帶有LOCALE_S1159和LOCALE_S2359標(biāo)識符的GetLocaleInfo函數(shù)。這些標(biāo)識符使程序獲得適合于使用者國家/地區(qū)和語言的字符串。
我們也可以讓DIGCLOCK處理WM_TIMECHANGE消息,這樣它將系統(tǒng)時間與日期發(fā)生變化的消息通知應(yīng)用程序。DIGCLOCK因WM_TIMER消息而每秒更新一次,實際上沒有必要這樣作,對WM_TIMECHANGE消息的處理使得每分鐘更新一次的時鐘變得更為合理。
建立模擬時鐘
模擬時鐘不必關(guān)心國際化問題,但是由于圖形所引起的復(fù)雜性卻抵消了這種簡化。為了正確地產(chǎn)生時鐘,您需要知道一些三角函數(shù)。CLOCK如程序8-4所示。
程序8-4 CLOCK
CLOCK.C
/*---------------------------------------------------------------------------
CLOCK.C -- Analog Clock Program
(c) Charles Petzold, 1998
---------------------------------------------------------------------------*/
#include <windows.h>
#include <math.h>
#define ID_TIMER 1
#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") ;
HWND hwnd;
MSG msg;
WNDCLASS wndclass ;
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.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, hInstance, 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, -cyClient / 2, NULL) ;
SetViewportOrgEx (hdc, cxClient / 2, cyClient / 2, NULL) ;
}
void RotatePoint (POINT pt[], int iNum, int iAngle)
{
int i ;
POINT ptTemp ;
for (i = 0 ; i < iNum ; i++)
{
ptTemp.x = (int) (pt[i].x * cos (TWOPI * iAngle / 360) +
pt[i].y * sin (TWOPI * iAngle / 360)) ;
ptTemp.y = (int) (pt[i].y * cos (TWOPI * iAngle / 360) -
pt[i].x * sin (TWOPI * iAngle / 360)) ;
pt[i] = ptTemp ;
}
}
void DrawClock (HDC hdc)
{
int iAngle ;
POINT pt[3] ;
for (iAngle = 0 ; iAngle < 360 ; iAngle += 6)
{
pt[0].x = 0 ;
pt[0].y = 900 ;
RotatePoint (pt, 1, iAngle) ;
pt[2].x = pt[2].y = iAngle % 5 ? 33 : 100 ;
pt[0].x - = pt[2].x / 2 ;
pt[0].y - = pt[2].y / 2 ;
pt[1].x = pt[0].x + pt[2].x ;
pt[1].y = pt[0].y + pt[2].y ;
SelectObject (hdc, GetStockObject (BLACK_BRUSH)) ;
Ellipse (hdc, pt[0].x, pt[0].y, pt[1].x, pt[1].y) ;
}
}
void DrawHands (HDC hdc, SYSTEMTIME * pst, BOOL fChange)
{
static POINT pt[3][5] ={0, -150, 100, 0, 0, 600, -100, 0, 0, -150,
0, -200, 50, 0, 0, 800, -50, 0, 0,-200,
0,0, 0, 0, 0, 0, 0, 0, 0, 800 } ;
int i, iAngle[3] ;
POINT ptTemp[3][5] ;
iAngle[0] = (pst->wHour * 30) % 360 + pst->wMinute / 2 ;
iAngle[1] = pst->wMinute * 6 ;
iAngle[2] = pst->wSecond * 6 ;
memcpy (ptTemp, pt, sizeof (pt)) ;
for (i = fChange ? 0 : 2 ; i < 3 ; i++)
{
RotatePoint (ptTemp[i], 5, iAngle[i]) ;
Polyline (hdc, ptTemp[i], 5) ;
}
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam,LPARAM lParam)
{
static int cxClient, cyClient ;
static SYSTEMTIME stPrevious ;
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -