Microsoft Foundation Classes: creating watch application
Submitted by pavel7_7_7 on Tuesday, February 11, 2014 - 07:54.
MFC provides large number of classes and functions that can help you to create an application with GUI and animation. In this Article I'll speak about the basic steps to create an watch application.
- In the application we need to now the current system time. To hold this information next variables are declared in the document class:
time_t variable is converted to the tm struct and then you can achieve data for the current hour, minute, second, day, date, month and year. This is done in the getTime() function:
- public:
- time_t t;
- struct tm* now;
- int hour,
- minute,
- second;
- int day,
- date,
- month,
- year;
Now we have all the necessary data to draw the watch.- void CwatchesDoc::getTime()
- {
- t = time(0);
- now = localtime(&t);
- hour = now->tm_hour;
- minute = now->tm_min;
- second = now->tm_sec;
- day = now->tm_wday;
- date = now->tm_mday;
- month = now->tm_mon;
- year = now->tm_year;
- }
-
The first step in the drawing an MFC application is to get the pointer to the document object in the
OnDraw
function:To draw the watch and the calendar I used a lot of different brushes, pens and other graphical objects. Here is the declaration of these objects in the- CwatchesDoc* pDoc = GetDocument();
- ASSERT_VALID(pDoc);
- if (!pDoc)
- return;
OnDraw
function:The next step is to get the frame rectangle and set the rectangle for the watch and calendar area:- CBrush *fontBrush,
- *watchBrush,
- *centerBrush,
- *dayBrush,
- *dateBrush,
- *monthBrush,
- *yearBrush;
- fontBrush = new CBrush(RGB(184,184,184));
- watchBrush = new CBrush(RGB(0,178,238));
- centerBrush = new CBrush(RGB(196,69,19));
- dayBrush = new CBrush(RGB(138,43,226));
- dateBrush = new CBrush(RGB(142,229,238));
- monthBrush = new CBrush(RGB(127,0,255));
- yearBrush = new CBrush(RGB(205,51,51));
- CPen *watchPen,
- *centerPen,
- *hourPen,
- *minutePen,
- *secondPen;
- watchPen = new CPen(PS_SOLID,5,RGB(0,0,0));
- centerPen = new CPen(PS_SOLID,5,RGB(136,69,19));
- hourPen = new CPen(PS_SOLID,10,RGB(0,0,0));
- minutePen = new CPen(PS_SOLID,5,RGB(92,51,23));
- secondPen = new CPen(PS_SOLID,3,RGB(255,0,0));
- CRect *rect;
- rect = new CRect();
- this->GetClientRect(rect);
- int width = rect->Width();
- int height = rect->Height();
- CRect *watchRect;
- watchRect = new CRect(0,0,(int)0.4*width,(int)0.4*width);
-
Now we have the initial data to draw the static elements of the application. We will draw now the watch application. But for this we need some extra data to be able do draw the hour hands in the correct way:
To draw the calendar you can create 2 arrays of string for day and month representation:
- int radius = 0.2*width - 30;
- int radiusHour = radius - 70;
- int radiusMinute = radius - 40;
- int radiusSecond = radius - 30;
- int theta = -60; // start angle
- double alpha = -90;
- int beta = - 90;
- int gamma = -90;
- int cx = 0.2*width + 5;
- int cy = 0.2*width;
- int x,
- y;
- CString day[7] = {L"Monday",L"Tuesday",L"Wednesday",L"Thursday",L"Friday",L"Saturday",L"Sunday"};
- CString month[12] = {L"January", L"February", L"March",L"April",L"May",L"June",L"July",L"August",L"September",L"October",L"November",L"December"};
-
Let's start the drawing. First step is to draw background:
And now the frame of the watch:
- pDC->FillRect(rect,fontBrush);
To draw the numbers on the clock face this code is used. The coordinates of the numbers are calculated using the variables defined above:- pDC->SelectObject(watchPen);
- pDC->SelectObject(watchBrush);
- pDC->Ellipse(CRect(0,0,0.4*width,0.4*width));
To draw the lines for hour hand used a formula to find the coordinates of the point on a circle by the angle:- for(int i = 0; i != 12; ++i,theta+=30)
- {
- x=cx+radius*cos(theta*M_PI/180);
- y=cy+radius*sin(theta*M_PI/180);
- t.Format(_T("%d"), i+1);
- RECT posK = {x - 10,y - 10,0,0};
- pDC->SetBkMode(TRANSPARENT);
- pDC->DrawText(t,t.GetLength(),&posK,DT_CALCRECT);
- pDC->DrawText(t,t.GetLength(),&posK,0);
- }
Here, the line is moved from the center of the clock face to the (x,y) point. The same calculations are done for the minute and hour hand:- pDC->SelectObject(hourPen);
- for(int i = 0; i != pDoc->hour;++i,alpha+=30);
- for(int i = 0; i != pDoc->minute;++i,alpha+=0.5);
- pDC->MoveTo(cx,cy);
- x=cx+radiusHour*cos(alpha*M_PI/180);
- y=cy+radiusHour*sin(alpha*M_PI/180);
- pDC->LineTo(x,y);
Now the representation of the clock face is draw. The next step is to draw the data, day, month and year:- pDC->SelectObject(minutePen);
- for(int i = 0; i != pDoc->minute;++i,beta+=6);
- pDC->MoveTo(cx,cy);
- x=cx+radiusMinute*cos(beta*M_PI/180);
- y=cy+radiusMinute*sin(beta*M_PI/180);
- pDC->LineTo(x,y);
- pDC->SelectObject(secondPen);
- for(int i = 0; i != pDoc->second;++i,gamma+=6);
- pDC->MoveTo(cx,cy);
- x=cx+radiusSecond*cos(gamma*M_PI/180);
- y=cy+radiusSecond*sin(gamma*M_PI/180);
- pDC->LineTo(x,y);
- pDC->SelectObject(dayBrush);
- pDC->Rectangle(CRect(width*0.5,0,width,0.1*height));
- pDC->SetBkMode(TRANSPARENT);
- pDC->DrawText(day[pDoc->day - 1],day[pDoc->day - 1].GetLength(),CRect(width*0.5,0,width,0.1*height),DT_CALCRECT);
- pDC->DrawText(day[pDoc->day - 1],day[pDoc->day - 1].GetLength(),CRect(width*0.5,0,width,0.1*height),DT_CENTER|DT_SINGLELINE|DT_VCENTER);
- pDC->SelectObject(dateBrush);
- pDC->Rectangle(CRect(width*0.5,0.15*height,width*0.6,0.25*height));
- t.Format(_T("%d"), pDoc->date);
- pDC->SetBkMode(TRANSPARENT);
- pDC->DrawText(t,t.GetLength(),CRect(width*0.5,0.15*height,width*0.6,0.25*height),DT_CALCRECT);
- pDC->DrawText(t,t.GetLength(),CRect(width*0.5,0.15*height,width*0.6,0.25*height),DT_CENTER|DT_SINGLELINE|DT_VCENTER);
- pDC->SelectObject(monthBrush);
- pDC->Rectangle(CRect(width*0.6,0.15*height,width*0.8,0.25*height));
- pDC->SetBkMode(TRANSPARENT);
- pDC->DrawText(month[pDoc->month],month[pDoc->month].GetLength(),CRect(width*0.6,0.15*height,width*0.8,0.25*height),DT_CALCRECT);
- pDC->DrawText(month[pDoc->month],month[pDoc->month].GetLength(),CRect(width*0.6,0.15*height,width*0.8,0.25*height),DT_CENTER|DT_SINGLELINE|DT_VCENTER);
- pDC->SelectObject(yearBrush);
- pDC->Rectangle(CRect(width*0.8,0.15*height,width,0.25*height));
- t.Format(_T("%d"), 1900 + pDoc->year);
- pDC->SetBkMode(TRANSPARENT);
- pDC->DrawText(t,t.GetLength(),CRect(width*0.8,0.15*height,width,0.25*height),DT_CALCRECT);
- pDC->DrawText(t,t.GetLength(),CRect(width*0.8,0.15*height,width,0.25*height),DT_CENTER|DT_SINGLELINE|DT_VCENTER);
-
If you launch the current application, you will see that the watch is not moving. You can obtain the time of the program launch and nothing is changed. So, we need to update the frame on every second. For this a timer is created:
And now we need to handle the event, generated by timer. For this, timer event is added to the message map an the function
- secondTimer = SetTimer(WM_USER+203,1000,NULL);
OnTimer()
is defined:This function calls the Invalidate function for the watch rectangle and redraws it according to the new value of time variable. Note During the creation of this project I suffered a great problem: I was unable to define a timer and a run-time error appeared when I defined the timer in the c-tor of the view class or in any other functions. The only solution found was to define timer in the next way:- void CwatchesView::OnTimer(UINT_PTR nIDEvent)
- {
- CwatchesDoc* pDoc = GetDocument();
- ASSERT_VALID(pDoc);
- if (!pDoc)
- return;
- CRect *rect;
- rect = new CRect();
- this->GetClientRect(rect);
- int width = rect->Width();
- int height = rect->Height();
- pDoc->getTime();
- this->InvalidateRect(CRect(0,0,width*0.4,height),1);
- CView::OnTimer(nIDEvent);
- }
Maybe it's only my mistake, but if you suffer any similar problems, you can act in this way- void CwatchesView::OnInitialUpdate()
- {
- CView::OnInitialUpdate();
- hourTimer = SetTimer(WM_USER+200,3600000,NULL);
- }
This is a very simple example of the animation in MFC. But these basic functions can allow you to create a variety of applications, that have animation.
Comments
Add new comment
- Add new comment
- 63 views