VC的一個屏保例子

用清明上河圖做屏保程序

參考http://it.sohu.com/2004/03/15/29/article219442988.shtml

前幾天得到清明上河圖的圖片,感覺它太長,於是不容易欣賞,希望能慢慢的自動的移動,最好加點音樂,有這個想法之後就開始做了一個看清明上河圖的小軟件,可以放大、縮小、移動速度增快或是變慢、暫停、再開始、或是反方向移動等功能。之後就想到不如用其作爲屏保,於是查找了怎麼製作屏保程序。參考了上面鏈接地址的方法。不過我認爲我這種方法要簡單得多,按照它上面的方法我沒有成功。不過挺感謝他的。
其實屏保程序就是一個執行文件,將後綴名改爲.scr,然後放到C:\windows\system32\下就可以了,最後再在桌面的屬性中選擇自己的屏保程序就可以了,簡單吧,哈哈。
在這裏我要說的是用VC來做清明上河圖的屏保程序,圖片是清明上河圖,自己慢慢移動顯示的,我還加了音樂,這更加有意思了。


準備工作:
1、清明上河圖的圖片,將其轉化成.bmp格式。
2、準備一個wav格式的歌曲,命名爲“清明上河圖屏保音樂.wav”其他格式的可以用千千靜聽轉化。(不需要背景音樂的話這個就不需要了)
3、就是編程環境VC,我的程序是在Visual C++ 6.0下面編譯的。


在這裏,我是假設你沒有學過VC,一點都不懂VC的情況下寫的,對於會用VC的朋友有的可以不用看,主要看代碼部分。


方法如下:
一、單擊VC中上菜單“file”,選擇“new”,出來一個對話框,選擇“Projects”——>“MFC appWizard[exe]”,然後在右邊的Project name上輸入工程名稱“清明上河圖屏保”。在下面Location中選擇要保存工程的地方。然後單擊“OK”。(如圖1)

(圖1)

二、現在出來了第二個對話框,我們只需要建立一個基於對話框的就可以了,這樣方便省事。溝選“Dialog based”,單擊“next”。(如圖2)

(圖2)

三、出來第三個對話框,取消掉已構選的“Aboat box”,溝選也可以,只是對於次程序沒有用。單擊“finish”。(如圖3)

(圖3)

四、好了,工程文件建立完了,出來了設計窗口,(如圖4)將對話框上的控件刪除掉(用鼠標選擇,然後按“Delete”鍵就可以刪除掉)。(如圖5)

(圖4)

(圖5)

五、用MFC寫的程序有的時候在別的機器上不能用,於是要做如下設置:選擇菜單“Project”——>“Setting...”(如圖6)。之後出來一個對話框,選擇“General”,然後在“Microsoft Foundation Classes:”下選擇“Use MFC in a Static Library”,最後單擊“OK”就可以了。(如圖7)

(圖6)

(圖7)

六、載入我們的清明上河圖圖片,就是之前準備的bmp格式的圖片,只能是這個格式纔可以這樣載入圖片。鼠標右擊“Icon”選擇“Import...”(如圖8)。之後出來一個文件對話框,你將“文件類型”改爲“所有文件(*.*)”。你找到你之前準備的清明上河圖圖片,然後單擊“Impot”,之後會彈出一個窗口,單擊“確定”就行了。

(圖8)

對於一些基礎的設置就完成了,之後的步驟就是添加代碼了。

七、找到左中位置的“Class view”(或者是Class...) ,單擊小窗口中的圖標展開類結構(如圖9)。雙擊“CMyDlg”,添加如下代碼到如下地方(如圖10中的黑色部分)
private:
UINT s_showH;//這是顯示屏的高度
UINT s_showW;//這是顯示屏的寬度度
UINT w_showX;//圖像要顯示的位置(從左到右的座標)
CDC* w_pdcmem;//位圖內存,也就是和清明上河圖關聯的內存,
CBitmap w_bitmap;//清明上河圖位圖
CPoint w_point;       //記錄鼠標的位置
void DrawBitmap();//畫圖的函數,這裏手動添加了。這樣方便快捷
(說明一下,這裏添加的這個函數DrawBitmap(),是將圖片畫處理的函數,這裏直接這樣聲明瞭。後面手動添加代碼。)

(圖9)

(圖10)

八、添加消息響應函數,這裏我們通過鼠標單擊來添加,如圖11,右鍵單擊“CMyDlg”,選擇“Add Windows Message Handler...”,這是出來一個添加Windows 消息的對話框,如圖12,在右邊找到如下項,分別雙擊,之後就添加完成了,都添加完成之後,單擊右邊的“OK”,就完成了消息的處理函數。
WM_TIMER       //計時器消息
WM_KEYDOWN       //鍵盤消息
WM_LBUTTONDOWN       //鼠標左鍵按下
WM_MBUTTONDOWN       //鼠標中鍵按下
WM_MOUSEMOVE       //鼠標移動
WM_RBUTTONDOWN       //鼠標右鍵按下
WM_SYSKEYDOWN       //系統鍵消息
WM_DESTROY       //銷燬消息
WM_ACTIVEVATEAPP //只能允許一個程序在運行。

(圖11)

(圖12)

九、完成消息的處理。
1.如圖13,只要雙擊圖中畫圈的函數名稱,就可以進入到函數體中。分別對坐邊畫上圈的函數添加一行代碼 PostMessage(WM_CLOSE); 這是關閉窗口,退出程序運行的操作。操作後代碼如下:
void CMyDlg::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
// TODO: Add your message handler code here and/or call default
PostMessage(WM_CLOSE);
CMyDlg::OnKeyDown(nChar, nRepCnt, nFlags);
}

void CMyDlg::OnLButtonDown(UINT nFlags, CPoint point) 
{
// TODO: Add your message handler code here and/or call default
PostMessage(WM_CLOSE);
CMyDlg::OnLButtonDown(nFlags, point);
}

void CMyDlg::OnMButtonDown(UINT nFlags, CPoint point) 
{
// TODO: Add your message handler code here and/or call default
PostMessage(WM_CLOSE);
CMyDlg::OnMButtonDown(nFlags, point);
}

void CMyDlg::OnRButtonDown(UINT nFlags, CPoint point) 
{
// TODO: Add your message handler code here and/or call default
PostMessage(WM_CLOSE);
CMyDlg::OnRButtonDown(nFlags, point);
}

void CMyDlg::OnSysKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
// TODO: Add your message handler code here and/or call default
PostMessage(WM_CLOSE);
CMyDlg::OnSysKeyDown(nChar, nRepCnt, nFlags);
}

(圖13)

2.對OnMouseMove(UINT nFlags, CPoint point)要做處理,如下:表示要等鼠標移動200個象素之後才退出屏保
void CMyDlg::OnMouseMove(UINT nFlags, CPoint point) 
{
// TODO: Add your message handler code here and/or call default
if(abs(point.x-w_point.x)>=200 ||
       abs(point.y-w_point.y)>=200)
{
       PostMessage(WM_CLOSE);
}
}


3.對OnActivateApp(BOOL bActive, HTASK hTask) 函數補充,加入代碼後如下:
void CMyDlg::OnActivateApp(BOOL bActive, HTASK hTask) 
{
CWnd::OnActivateApp(bActive, hTask);
// TODO: Add your message handler code here
if (!bActive) //is being deactivated
       PostMessage(WM_CLOSE); 
}
4.對OnDestroy()函數補充,加入代碼後如下:
void CMyDlg::OnDestroy() 
{
CMyDlg::OnDestroy();

// TODO: Add your message handler code here
delete w_pdcmem;
KillTimer(1);//程序中用到了計時器。這裏關掉計時器
}
4.對OnTimer(UINT nIDEvent),這裏主要是時間片到的時候就調用DrawBitmap();重畫一次屏幕,這樣來實現圖片沿着一個方向移動。
void CMyDlg::OnTimer(UINT nIDEvent) 
{
// TODO: Add your message handler code here and/or call default
w_showX += 1;
if(w_showX>=BITMAP_WEIGHT){
       w_showX = 0;

DrawBitmap();

CDialog::OnTimer(nIDEvent);
}
解釋一下代碼:
w_showX += 1;w_showX變量是對清明上河圖要顯示區域的左位置。沒重畫一次,w_showX加1,也就是一個象素的距離,這樣使得圖片移動時更流暢,逼真。
if(w_showX>=BITMAP_WEIGHT){這是當顯示到結尾處的時候再次從開始位置處顯示。
w_showX = 0;

DrawBitmap();調用這個函數重畫。

十、之前對消息處理完成了,現在我們來處理界面,因爲我們是創建的對話框,而屏保是全屏顯示的,所以我們要做一下處理。添加如下代碼到CMyDlg::OnInitDialog()。(如圖14)
//設置全屏窗口
CRect m_rcMain;
GetWindowRect(&m_rcMain);//restore        the        src        screen's        size;   
         //刪除窗口的標題欄
LONG        style        =        GetWindowLong(m_hWnd,GWL_STYLE);   
style &= ~WS_CAPTION;   
SetWindowLong(m_hWnd,GWL_STYLE,style); //設置顯示窗口狀態  
//獲得顯示器屏幕的寬度  
s_showW        =        GetSystemMetrics(SM_CXSCREEN);   
s_showH        =        GetSystemMetrics(SM_CYSCREEN);   
//show        window   
SetWindowPos(NULL,-23,-3,s_showW+27,s_showH+6,SWP_NOZORDER);

//獲得鼠標的位置
::GetCursorPos(&w_point);
//隱藏鼠標
ShowCursor(FALSE);
完成的代碼如下:
BOOL CMyDlg::OnInitDialog()
{
CDialog::OnInitDialog();

// Set the icon for this dialog.       The framework does this automatically
//       when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE);        // Set big icon
SetIcon(m_hIcon, FALSE);       // Set small icon

// TODO: Add extra initialization here

//設置全屏窗口
CRect m_rcMain;
GetWindowRect(&m_rcMain);//restore        the        src        screen's        size;   
         //刪除窗口的標題欄
LONG        style        =        GetWindowLong(m_hWnd,GWL_STYLE);   
style &= ~WS_CAPTION;   
SetWindowLong(m_hWnd,GWL_STYLE,style); //設置顯示窗口狀態  
//獲得顯示器屏幕的寬度  
s_showW        =        GetSystemMetrics(SM_CXSCREEN);   
s_showH        =        GetSystemMetrics(SM_CYSCREEN);   
//show        window   
SetWindowPos(NULL,-23,-3,s_showW+27,s_showH+6,SWP_NOZORDER);
//獲得鼠標的位置
::GetCursorPos(&w_point);
//隱藏鼠標
ShowCursor(FALSE);

return TRUE;       // return TRUE       unless you set the focus to a control
}

(圖14)

十一、界面,消息都處理完成了,現在我們就來開始在屏幕上畫圖了。不過先別急,先到CMyDlg::OnPaint() 添加一些初始畫數據的代碼。完成之後如下:
void CMyDlg::OnPaint() 
{
if (IsIconic())
{
       CPaintDC dc(this); // device context for painting

       SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);

       // Center icon in client rectangle
       int cxIcon = GetSystemMetrics(SM_CXICON);
       int cyIcon = GetSystemMetrics(SM_CYICON);
       CRect rect;
       GetClientRect(&rect);
       int x = (rect.Width() - cxIcon + 1) / 2;
       int y = (rect.Height() - cyIcon + 1) / 2;

       // Draw the icon
       dc.DrawIcon(x, y, m_hIcon);
}
else
{
       CPaintDC dc(this); 
       w_pdcmem = new CDC;
       w_bitmap.LoadBitmap(IDB_BITMAP1);
       w_pdcmem->CreateCompatibleDC(&dc);       //建立與dc兼容的
       w_pdcmem->SelectObject(&w_bitmap);

       sndPlaySound( "清明上河圖屏保音樂.wav" , SND_ASYNC | SND_LOOP );
       w_showX = 0;
       DrawBitmap();

       SetTimer(1,1,NULL);
       CDialog::OnPaint();
}
}
說明一下:if段裏面的都是編譯器生成的。else裏面的是添加的代碼,
CPaintDC dc(this);
獲得屏幕畫圖DC
w_pdcmem = new CDC;
開闢一個內存區,用來和清明上河圖位圖相關聯。這樣直接從內存中拷貝到屏幕就可以了, 
w_bitmap.LoadBitmap(IDB_BITMAP1);
載入圖片,其中IDB_BITMAP1是我們剛開始載入圖片時圖片的ID
w_pdcmem->CreateCompatibleDC(&dc); 
建立與屏幕dc兼容的
w_pdcmem->SelectObject(&w_bitmap);
圖片與內存關聯
sndPlaySound( "清明上河圖屏保音樂.wav" , SND_ASYNC | SND_LOOP );
這是播放聲音的函數,是系統函數。加入你需要播放聲音的話,只是這個還不能實現,還需要在現在編輯的文件的最上方加入如下代碼:(如圖15)
#include "mmsystem.h"
#pragma comment( lib , "winmm.lib" )

w_showX = 0;
顯示的時候從開始位置開始顯示,你可以該爲其他數據,但這裏必須是在圖片長度範圍之內,
DrawBitmap();
開始的時候就畫一次。
SetTimer(1,1,NULL);
設置計時器,第一個參數1是計時器的ID號,第二個是時間(單位毫秒),這裏我設置爲1毫秒,可以根據自己的需要來設定。表示的是沒格這個時間段後自動調用OnTimer(UINT nIDEvent)函數。

(圖15)

十二、對最後一項的處理了。就是畫屏保的函數,還記得第七步中有個DrawBitmap()函數嗎,這就是畫圖用的,現在來寫代碼,
現在這裏說明一下:要補充一點,之前忘記了,我們必須要知道該清明上河圖的長度和寬度,可以在用鼠標放在圖片上,之後出來的詳細信息(如圖16左邊部分),或直接右擊該圖查看屬性得知(如圖16右邊部分),記下該數字,如圖15中的地方定義兩個宏:
#define BITMAP_HEIGHT 300 //原圖像高度
#define BITMAP_WEIGHT 9937 //原圖像寬度

(圖16)

1.在這裏函數裏畫屏幕,我們要得到屏幕DC,可以用CClientDC dc(this);來實現。
爲了繪製圖片的時候看起來是瀏覽的,我們先將要畫在屏幕上圖片的部分畫在內存裏,之後直接從內存裏拷貝到屏幕上來就不會出現閃爍情況,於是我們要定義一個內存變量來和dc兼容,在爲這塊內存設置大小,拷貝過來的時候才知道拷貝到什麼地方。這裏聲明一個位圖對象,這位圖就作爲在內存裏畫的對象:
CDC dcmem;
CBitmap bitmap;
//創建位圖s_showW,s_showH爲屏幕的寬度和高度
bitmap.CreateCompatibleBitmap(&dc,s_showW,s_showH);
dcmem.CreateCompatibleDC(&dc);
dcmem.SelectObject(&bitmap);
設置背景爲黑色:
dcmem.FillRect(&CRect(0,0,s_showW,s_showH),&CBrush(RGB(0,0,0)));
2.將要顯示的圖片部分拷貝到內存。由於清明上河圖很長,我們只能在屏幕上顯示一部分,我們將清明上河圖顯示到屏幕的中間,可以這樣來計算:(屏幕的高度-圖像的高度)/2就爲要顯示圖片的重座標。代碼如下:
UINT y = (s_showH-BITMAP_HEIGHT)/2;
dcmem.StretchBlt(
       0,y,s_showW, BITMAP_HEIGHT, 
       w_pdcmem,
       w_showX,0,s_showW,BITMAP_HEIGHT-12,
       SRCCOPY);

3.只是顯示圖片,是不是有點太傻了,輸出幾個漢字?在VC中用TextOut就可以輸出漢字,我這裏來動態的輸出漢字,也就是漢字隨着圖片會移動,移動完之後又再次出來。先設置輸出的字體
CFont font;
font.CreateFont(20,10,//nHeight,nWidth
       0,       //int nEscapement,
       60,       //int nOrientation, 
       30,       //int nWeight,
       FALSE, //BYTE bItalic,
       FALSE,       //BYTE bUnderline,
       FALSE,       //BYTE cStrikeOut,
       DEFAULT_CHARSET, //BYTE nCharSet,
       OUT_CHARACTER_PRECIS, //BYTE nOutPrecision,
       CLIP_DEFAULT_PRECIS, //BYTE nClipPrecision,
       DEFAULT_QUALITY,       //BYTE nQuality,
       FIXED_PITCH|FF_MODERN,        //BYTE nPitchAndFamily,
       "隸書"
       );
dcmem.SelectObject(&font);
再次定義靜態變量,記錄開始輸出漢字的座標,然後變化該變量,實現如下:
static long l = s_showW;
--l;
if(l>-1020){
       dcmem.SetTextColor(RGB(0,100,255));
       dcmem.TextOut(l,20,"製作:汪建友");
       dcmem.TextOut(l+200,20,"QQ:421068480");
       dcmem.TextOut(l+400,20,"E-mail:[email protected]");
       dcmem.TextOut(l+720,20,"空間:http://hi.baidu.com/neujyou");  
}
else{ 
       l = s_showW;
}
說明:1020是所有漢字的寬度,可以根據自己的需要修改,TextOut中的參數,第一個是橫座標,第二個是縱座標,第三個爲要輸出的字符串。當最後一個字符串顯示完成之後再次設置l = s_showW;,又可以再次現實。

4.將圖片繪製在屏幕上。
dc.BitBlt(20,0,s_showW,s_showH,&dcmem,0,0,SRCCOPY);//顯示在屏幕上

實現代碼如下:
void CMyDlg::DrawBitmap()

CClientDC dc(this);
CDC dcmem;
CBitmap bitmap;
bitmap.CreateCompatibleBitmap(&dc,s_showW,s_showH);

dcmem.CreateCompatibleDC(&dc);
dcmem.SelectObject(&bitmap);
dcmem.SetBkMode(0);

dcmem.FillRect(&CRect(0,0,s_showW,s_showH),
          &CBrush(RGB(0,0,0))
       );
UINT y = (s_showH-BITMAP_HEIGHT)/2;
dcmem.StretchBlt(
       0,y,s_showW, BITMAP_HEIGHT, 
       w_pdcmem,
       w_showX,0,s_showW,BITMAP_HEIGHT-12,
       SRCCOPY);

CFont font;
font.CreateFont(20,10,//nHeight,nWidth
       0,       //int nEscapement,
       60,       //int nOrientation, 
       30,       //int nWeight,
       FALSE, //BYTE bItalic,
       FALSE,       //BYTE bUnderline,
       FALSE,       //BYTE cStrikeOut,
       DEFAULT_CHARSET, //BYTE nCharSet,
       OUT_CHARACTER_PRECIS, //BYTE nOutPrecision,
       CLIP_DEFAULT_PRECIS, //BYTE nClipPrecision,
       DEFAULT_QUALITY,       //BYTE nQuality,
       FIXED_PITCH|FF_MODERN,        //BYTE nPitchAndFamily,
       "隸書"
       );
  
dcmem.SelectObject(&font);

static long l = s_showW;
--l;
if(l>-1020){
       dcmem.SetTextColor(RGB(0,100,255));
       dcmem.TextOut(l,20,"製作:汪建友");
       dcmem.TextOut(l+200,20,"QQ:421068480");
       dcmem.TextOut(l+400,20,"E-mail:[email protected]");
       dcmem.TextOut(l+720,20,"空間:http://hi.baidu.com/neujyou");  
}
else
       l = s_showW;

dc.BitBlt(20,0,s_showW,s_showH,&dcmem,0,0,SRCCOPY);//顯示在屏幕上

bitmap.DeleteObject();
}


所有代碼完成了,按一下鍵盤上的“F7”編譯,看看有有沒有問題,沒有問題的話,就按鍵“Ctrl+F5”運行查看結果,(如圖17)。沒有音樂,對嗎?將之前準備的“清明上河圖音樂.wav”拷貝放到你的這個程序工程的目錄下面,再運行看看...

(圖17)


程序是完成了,不過現在還不是屏保,也就是我們要將它設置爲屏保。要做最後的工作了,
1、要使在桌面屬性對話框中選擇此屏保時不運行(這裏不是說不讓它運行),要加最後一行代碼。回到VC編程界面,進入CMyApp::InitInstance()中編輯,(如圖18)
修改後代碼如下:
BOOL CMyApp::InitInstance()

       AfxEnableControlContainer();

       LPTSTR lpszArgv = __argv[1];
       if(lpszArgv[0]=='/')
        lpszArgv++;
       if(lstrcmpi(lpszArgv,_T("s"))==0){ 
        CMyDlg dlg;
        m_pMainWnd = &dlg;
        int nResponse = dlg.DoModal();
        return true;
       }
       return FALSE;
}

(圖18)


2.在你建立的工程目錄下找到“debug”——>“清明上河圖屏保.exe”,將“清明上河圖屏保.exe”重命名爲“清明上河圖屏保.scr”,scr是windows系統的屏保文件。
3.將“清明上河圖屏保.scr”和“清明上河圖屏保音樂.wav”文件一起拷貝到“C:”文件夾下面。
4.回到桌面,單擊鼠標右鍵,選擇“屬性”——>“屏幕保護程序”,
         在屏幕保護程序的下拉框中選擇“清明上河圖屏保”,最後單擊“應用”——>“確定”,就完成了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章