Click here to Skip to main content
15,900,907 members
Articles / Desktop Programming / MFC
Article

A transparent clock and Check on Work Attendance

Rate me:
Please Sign up or sign in to vote.
3.55/5 (8 votes)
6 Dec 20032 min read 287.5K   2.7K   28   11
A clock demo, but it can check on work too. With some clicks on the Hour blocks, you can realize it.

Sample Image - clock.gif

Introduction

For a long time, I wanted to write a clock for my machine. I dislike the feel of digital clocks, days to days, years to years, work work and work... May be, I am AN OLD ANTIQUE! I just like the freedom in the fresh blue sky!

A transparent clock? Should it appear like a piece of glass? Or some pixels block on the desktop, and transparency around the hour hand and minute hand? How can the second hand go around the center point and hold the transparency around it obviously?

Well, I decide to write it under Windows 2000, because I can use the transparent layer attributes. To realize it, I have to fulfill these:

First, in the OnCreate section, register a hot key for turning it on when you double click on the hour hand or minute hand, and force it disappear. Next, change the window style to WS_EX_TOOLWINDOW, thus, make it disappear from the task bar. Last, setting the WS_EX_LAYERED attributes, and it shows me the result. Following is the code in OnCreate section.

int CClockDlg::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
    if (CDialog::OnCreate(lpCreateStruct) == -1)
        return -1;
    
    m_bHotkeyReg=RegisterHotKey(GetSafeHwnd(), 
      theApp.m_hatomHotKey,MOD_ALT|MOD_CONTROL|MOD_SHIFT,VK_F12);

    CRect rc;
    GetWindowRect(&rc);
    HDC hdc=::GetDC(GetSafeHwnd());
    CSize size(GetDeviceCaps(hdc,LOGPIXELSX),GetDeviceCaps(hdc,LOGPIXELSY));
    int w=(size.cx/size.cy)*rc.Height();
    SetWindowPos(NULL,0,0,w,rc.Height(), 
      SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOREDRAW);
    ::ReleaseDC(GetSafeHwnd(),hdc);
    m_size=CSize(w,rc.Height());

    SetWindowLong(GetSafeHwnd(),GWL_EXSTYLE,WS_EX_TOOLWINDOW);
    DWORD Style = ::GetWindowLong(GetSafeHwnd(),GWL_EXSTYLE);
    Style |= WS_EX_LAYERED;
    ::SetWindowLong(GetSafeHwnd(),GWL_EXSTYLE,Style);
    
    ::SetLayeredWindowAttributes( GetSafeHwnd(),
        m_ColorBackground,  //本颜色将删除
        (BYTE)(255.f * m_fTransPercent/100.f),LWA_ALPHA|LWA_COLORKEY);

    return 0;
}

Is it easy? If you don't know about the comments of the code, that is apprehensible, because I am a Chinese man, and my English so poor, but I'll try my best to make it clear for you.

From the APP file, you can see two procedures SayInChinese and PlayWaveSound. I ignore those here because I made them work in my early project (it can speak in Chinese words). So, you can make it speak in English by the next step.

In the first step, before you can use these codes, you should do the following things:

  1. Copy clock.mdb file to ./Debug sub folder and ./Release folder.
  2. Rebuild it and run.

Use one class named CClockRSet. It is derived from CDaoRecordset. I use it to record information of attendance. Use two procedures to perform it:

BOOL GetTodayID(UINT& uID)
{
    SYSTEMTIME st;
    GetLocalTime(&st);
    CString sql;
    sql.Format("select * from [KaoQin] where (YEAR(RiQi)=%d 
             and MONTH(RiQi)=%d and DAY(RiQi)=%d)",
             st.wYear,st.wMonth,st.wDay);
    CClockRSet rs;
    rs.Open(AFX_DB_USE_DEFAULT_TYPE,sql,CRecordset::readOnly);
    ASSERT(rs.IsOpen());
    if(!rs.IsOpen()) return FALSE;
    BOOL bRet=FALSE;
    if(rs.GetRecordCount()>0)
    {
        rs.MoveFirst();
        uID=rs.m_ID;
        bRet=TRUE;
    }
    if(rs.IsOpen()) rs.Close();
    return bRet;
}

It checks for the first record while checking in today, and returns the ID. Returns zero for failure.

if(!GetTodayID(m_uTodayID))
{
    if(FillClock(BAODAO,m_uTodayID))
    {
        VERIFY(GetTodayID(m_uTodayID));
        ASSERT(m_uTodayID!=0);
    }
}

An entry making one record today:

BOOL CClockDlg::PreTranslateMessage(MSG* pMsg) 
{
    if(pMsg->message==WM_KEYDOWN)
    {
        if(pMsg->wParam==VK_ESCAPE)
        {
            //SHORT stat1=GetKeyState(VK_LCONTROL);
            //SHORT stat2=GetKeyState(VK_LSHIFT);
            //if(stat1 & 0x0800 && stat2 & 0x0800)
            //{
                ShowWindow(SW_MINIMIZE);
                ShowWindow(SW_HIDE);
                return TRUE;
            //}
        }
        //if(pMsg->wParam==VK_F1)
        //{
        //    theApp.ShowHelp();
        //    return TRUE;
        //}
    }
    return CDialog::PreTranslateMessage(pMsg);
}

Perform hiding your clock by pressing ESC key.

void CClockDlg::OnLButtonDown(UINT nFlags, CPoint point) 
{
    CDialog::OnLButtonDown(nFlags, point);
    CRect rcTest;
    for(int i=1;i<=12;i++)
    {
        //test if you are clicking at the right place
        rcTest=GetHourRect(i);
        if(rcTest.PtInRect(point))
        {
            if(i==12)
            {
                if(m_uTodayID!=0)
                    FillClock(WUFAN,m_uTodayID);
            }
            else if(i==1)
            {
                if(m_uTodayID!=0)
                    FillClock(PMBAODAO,m_uTodayID);
            }
            else if(i==6)
            {
                if(m_uTodayID!=0)
                    FillClock(XIABAN,m_uTodayID);
            }
            else if(i==7)
            {
                if(m_uTodayID!=0)
                    FillClock(NIGHTBAODAO,m_uTodayID);
            }

            DWORD dwFlag=SND_RESOURCE;
            //Play the music
            ::PlaySound(MAKEINTRESOURCE(IDR_WAVE_DIDA), 
                           AfxGetResourceHandle(),dwFlag);
            if(m_uDiDaTimer!=0)
            {
                KillTimer(m_uDiDaTimer);
                m_uDiDaTimer=0;
            }
            m_uDiDaCount+=i;
            const int OKPASSWORD=1+2+3+4+5+6;
            TRACE("%d m_uDiDaCount=%d/%d\n",i,m_uDiDaCount,OKPASSWORD);
            // it is mystery!:)
            //如果密码不正确,启动Timer
            if(m_uDiDaCount!=OKPASSWORD)
            {
                //下面的输入必须在10秒钟内完成,否则输入将被清理
                m_uDiDaTimer=SetTimer(2,1000*10,NULL);
            }
            else
            {
                ::PlaySound(MAKEINTRESOURCE(IDR_WAVE_DONE), 
                              AfxGetResourceHandle(),dwFlag);
                DoLoginAction();
                m_uDiDaCount=0;
            }
            return;
        }
    }
    //Move the window while you drag it
    PostMessage(WM_NCLBUTTONDOWN, HTCAPTION, MAKELPARAM(point.x,point.y));
}

In the OnTimer procedure, it does something to make it speak Chinese. It can say something like :"Good morning sir, it'is XXXX clock now.", but it says in Chinese. You can download the Chinese speaking API next time.

CString ss;
if(st.wHour>=0 && st.wHour<3) ss="凌晨";
if(st.wHour>=3 && st.wHour<6) ss="清晨";
if(st.wHour>=6 && st.wHour<9) ss="早晨";
if(st.wHour>=9 && st.wHour<12) ss="中午";
if(st.wHour>=12 && st.wHour<15) ss="下午";
if(st.wHour>=15 && st.wHour<18) ss="傍晚";
if(st.wHour>=18 && st.wHour<21) ss="晚上";
if(st.wHour>=21 && st.wHour<=23) ss="深夜";
if(st.wMinute!=m_nLastMinute)
{
    //是否允许报时
    if(st.wMinute==30)
    {
        //是否允许半小时报时
        if(theApp.GetProfileInt("settings","CanAlarmHalfHour",FALSE))
        {
            CString sFile=theApp.GetProfileString("settings",
                                      "HalfHourAlarmFile","");
            if(!sFile.IsEmpty() && PathFileExists(sFile))
            {
                CString sExt=PathFindExtension(sFile);
                if(sExt.CompareNoCase(".wav")==0)
                {
                    theApp.PlayWaveSound(sFile);
                }
            }
            if(theApp.GetProfileInt("settings",
                      "UseChineseVoiceInAlarm",TRUE))
            {
                CString s;
                s.Format("%s %d点%d分",
                                 ss,st.wHour,st.wMinute);
                theApp.SayInChinese(s);
            }
        }
    }
}
if(m_nLastHour != st.wHour)
{
    //是否允许报时
    if(theApp.GetProfileInt("settings","CanAlarmHour",TRUE))
    {
        CString sFile=theApp.GetProfileString("settings",
                                         "HourAlarmFile","");
        if(!sFile.IsEmpty() && PathFileExists(sFile))
        {
            CString sExt=PathFindExtension(sFile);
            if(sExt.CompareNoCase(".wav")==0)
            {
                theApp.PlayWaveSound(sFile);
            }
        }
        if(theApp.GetProfileInt("settings",
                       "UseChineseVoiceInAlarm",TRUE))
        {
            CString sE;
            sE="正";
            if(st.wMinute!=0)
                sE.Format("%d分",st.wMinute);
            CString s;
            s.Format("%s %d点%s",ss,st.wHour,sE);
            theApp.SayInChinese(s);
        }
    }
}

Make it more transparent while you right click it.

void CClockDlg::OnRButtonDown(UINT nFlags, CPoint point) 
{
    CDialog::OnRButtonDown(nFlags, point);

    ::SetWindowLong(GetSafeHwnd(),GWL_EXSTYLE,
        (LONG)(::GetWindowLong(GetSafeHwnd(),GWL_EXSTYLE) & ~WS_EX_LAYERED));

    ::SetWindowLong(GetSafeHwnd(),GWL_EXSTYLE,
        (LONG)(::GetWindowLong(GetSafeHwnd(),GWL_EXSTYLE) | WS_EX_LAYERED));

    if(m_fTransPercent>0.f) 
        m_fTransPercent-=5.f;
    if(m_fTransPercent<10.f)
        m_fTransPercent=10.f;

    ::SetLayeredWindowAttributes( GetSafeHwnd(),
        m_ColorBackground,    //本颜色将删除
        (BYTE)(255.f * m_fTransPercent/100.f),LWA_ALPHA|LWA_COLORKEY);

    BLENDFUNCTION bl={AC_SRC_OVER,0, 
         (BYTE)(255.f * m_fTransPercent/100.f),AC_SRC_ALPHA};
    ::UpdateLayeredWindow(GetSafeHwnd(),
        NULL,
        NULL,NULL,
        NULL,
        NULL,
        m_ColorBackground,
        &bl,
        ULW_ALPHA|ULW_COLORKEY);
    theApp.WriteProfileInt("Settings","ClockTransparent",(int)m_fTransPercent);
}
void CClockDlg::OnHotKey(WPARAM wParam, LPARAM lParam)
{
    WORD wL,wH;
    wL=LOWORD(lParam);
    wH=HIWORD(lParam);
    if((wL & MOD_ALT) && (wL & MOD_CONTROL) && (wL & MOD_SHIFT))
    {
        if(wH==VK_F12)
        {
            if(!::IsWindowVisible(GetSafeHwnd()))
            {
                ShowWindow(SW_RESTORE);
                ShowWindow(SW_SHOW);
            }
        }
    }
}

The hotkey for showing it again.

What can I say more? It is obvious and clear to you, and I will upload the Chinese speaking API (CSAPI) next week.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
China China
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralKingPower Pin
KingPeng2-Jul-06 6:42
KingPeng2-Jul-06 6:42 
Generalwhy SetLayeredWindowAttributes can't used in 8bits(256) color quality Pin
Eureka Jim11-Aug-04 4:11
Eureka Jim11-Aug-04 4:11 
GeneralNot for NT4.0 Pin
Ammar8-Dec-03 21:28
Ammar8-Dec-03 21:28 
GeneralGreat Pin
aamironline8-Dec-03 17:10
aamironline8-Dec-03 17:10 
GeneralUpdated Pin
chenhuasheng7-Dec-03 14:41
chenhuasheng7-Dec-03 14:41 
GeneralDeleted it, but could not get rid of it. Pin
WREY2-Dec-03 7:44
WREY2-Dec-03 7:44 
Following the initial compile and link of the sample, the first error that popped up was that it couldn't find the ".mdb" file. (Little wonder why. It was coded in the program under a different name than what was downloaded.) After dismissing the error message, the clock sample appeared.

After clicking the hour hand, and then the minute hand, and nothing happened, I double clicked the center of the clock and it immediately disappeared (towards the left bottom corner of the screen). It did not get minimized; it disappeared! To get it re-displayed, I had to run the program again, whereupon I tried right-clicking the hands and again NOTHING HAPPENED! At this point I decided to take a look at the code to find out what was going on.

Looking at some of the handlers didn't reveal anything major, and I made a few cosmetic changes in other parts of the program to enhance readability. Having done that, I tried recompiling it and that's when I discovered it couldn't link because the ".exe" file could not be opened. IOW, the program was still running.

A check with Task Manager to see if that was the case revealed the opposite; the sample was NOT running. Repeated efforts to re-compile and link were to no avail; it just wouldn't link.

Tried deleting the ".exe" file from Windows Explorer, and got an error message about the file being "in use", even though that was NOT the case because I had already shutdown Visual Studio.

I brought Visual Studio back up and tried re-compiling the sample again, and again I got the same message about it not being able to link.

Short of re-booting my machine to wash away whatever anomaly this sample created, I figured it wasn't worth the time nor the effort.

Clearly there's some cleaning up needful to be done to this program, and until I see an "Update" done to it, it's NOT going to get run on my machine again!!

Dead | X|

William

Fortes in fide et opere!
GeneralRe: Deleted it, but could not get rid of it. Pin
chenhuasheng2-Dec-03 14:47
chenhuasheng2-Dec-03 14:47 
GeneralBug Pin
PeterHarrie1-Dec-03 23:23
PeterHarrie1-Dec-03 23:23 
GeneralRe: Bug Pin
chenhuasheng2-Dec-03 15:00
chenhuasheng2-Dec-03 15:00 
Generalnot enough explanation Pin
Mingming Lu1-Dec-03 17:06
Mingming Lu1-Dec-03 17:06 
GeneralRe: not enough explanation Pin
chenhuasheng2-Dec-03 15:01
chenhuasheng2-Dec-03 15:01 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.