wxpython 圖像編程


使用圖像編程

這一章來了解一下我們可以使用圖片來作些什么事情.一幅圖勝過千言萬語,在wxWidgets,工具條,樹形控件,notebooks,按鈕,Html窗口和特定的繪畫代碼中,都會用到圖片.有時候它們還會在不可見的地方發揮作用,比如我們可以用它來創建雙緩沖區以避免閃爍.這一章里,我們會接觸到各種各樣的圖片類,還會談到怎樣覆蓋wxWidgets提供的默認圖片和圖標。

wxWidgets中圖片相關的類

wxWidgets支持四種和位圖相關的類:wxBitmap, wxIcon, wxCursor和wxImage.

wxBitmap是一個平台有關的類,它擁有一個可選的wxMask屬性以支持透明繪畫.在windows系統上,wxBitmap是通過設備無關位圖(DIBs)實現的,而在GTK+和X11平台上,每個wxBitmap則包含一個GDK的pixmap對象或者X11的pixmap對象.而在Mac平台上,則使用的是PICT.wxBitmap可以和wxImage進行互相轉換.

wxIcon用來實現各個平台上的圖標,一個圖標指的是一個小的透明圖片,可以用來代表不同的frame或者對話框窗口.在GTK+,X11和Mac平台上,icon就是一個小的總含有wxMask的wxBitmp,而在windows平台上,wxIcon則是封裝了HICON對象.

wxCursor則是一個用來展示鼠標指針的圖像,在GTK+平台上是用的GdkCursor,X11和Mac平台上用的是各自的Cursor,而在windows平台上則使用的是HCURSOR.wxCursor有一個熱點的概念(所謂熱點指的是圖片中用來精確代表指針單擊位置的那個點),也總是包含一個遮罩(mask).

wxImage則是四個類中唯一的一個平台無關的實現,它支持24bit位圖以及可選的alpha通道.wxImage可以從wxBitmap類使用wxBitmap::ConvertToImage函數轉換而來,也可以從各種各樣的圖片文件中加載,它所支持的圖片格式也是可以通過圖片格式處理器來擴展的.它擁有操作其圖片上某些bit的能力,因此也可以用來對圖片進行一個基本的操作.和wxBitmap不同,wxImage不可以直接被設備上下文wxDC使用,如果要在wxDC上繪圖,需要現將wxImage轉換成wxBitmap,然后就可以使用wxDC的DrawBitmap函數進行繪圖了.wxImage支持設置一個掩碼顏色來實現透明的效果,也支持通過alpha通道實現非常復雜的透明效果.

你可以在這些圖片類型之間進行相互轉換,盡管某些轉換操作是平台相關的.

注意圖片類中大量使用引用記數器,因此對圖片類進行賦值和拷貝的操作的系統開銷是非常小的,不過這也意味着對一個圖片的更改可能會影響到別的圖片.

所有的圖片類都使用下表列出的標准的wxBitmapType標識符來讀取或者保存圖片數據:

 

ht!|>m3.8cm|>m10.72cm<|位圖類型 wxBITMAP_TYPE_BMP Windows位圖文件 (BMP).
wxBITMAP_TYPE_BMP_RESOURCE 從windows可執行文件資源部分加載的Windows位圖.
wxBITMAP_TYPE_ICO Windows圖標文件(ICO).
wxBITMAP_TYPE_ICO_RESOURCE 從windows可執行文件資源部分加載的Windows圖標.
wxBITMAP_TYPE_CUR Windows光標文件(CUR).
wxBITMAP_TYPE_CUR_RESOURCE 從windows可執行文件資源部分加載的Windows光標.
wxBITMAP_TYPE_XBM Unix平台上使用的XBM單色圖片.
wxBITMAP_TYPE_XBM_DATA 從C++數據中構造的XBM單色位圖.
wxBITMAP_TYPE_XPM XPM格式圖片,最好的支持跨平台並且支持編譯到應用程序中去的格式.
wxBITMAP_TYPE_XPM_DATA 從C++數據中構造的XPM圖片.
wxBITMAP_TYPE_TIF TIFF格式位圖,在大圖片中使用比較普遍.
wxBITMAP_TYPE_GIF GIF格式圖片,最多支持256中顏色,支持透明.
wxBITMAP_TYPE_PNG PNG位圖格式, 一個使用廣泛的圖片格式,支持透明和alpha通道,沒有版權問題.
wxBITMAP_TYPE_JPEG JPEG格式位圖, 一個廣泛使用的壓縮圖片格式,支持大圖片,不過它的壓縮算法是有損耗壓縮,因此不適合對圖片進行反復加載和壓縮.
wxBITMAP_TYPE_PCX PCX圖片格式.
wxBITMAP_TYPE_PICT Mac PICT位圖.
wxBITMAP_TYPE_PICT_RESOURCE 從可執行文件資源部分加載的Mac PICT位圖.
wxBITMAP_TYPE_ICON_RESOURCE 僅在Mac OS X平台上有效, 用來加載一個標准的圖標(比如wxICON_INFORMATION)或者一個圖標資源.
wxBITMAP_TYPE_ANI Windows動畫圖標(ANI).
wxBITMAP_TYPE_IFF IFF位圖文件.
wxBITMAP_TYPE_MACCURSOR Mac光標文件.
wxBITMAP_TYPE_MACCURSOR_RESOURCE 從可執行文件資源部分加載的Mac光標.
wxBITMAP_TYPE_ANY 讓加載圖片的代碼自己確定圖片的格式.

 

使用wxBitmap編程

你可以使用wxBitmap來作下面的事情:

 

 

  • 通過設備上下文將其畫在一個窗口上.
  • 在某些類(比如wxBitmapButton, wxStaticBitmap, and wxToolBar)中將其作為一個圖片標簽.
  • 使用其作為雙緩沖區(在將某個圖形繪制到屏幕上之前先繪制在一塊緩沖區上).

 

某些平台(特別是windows平台)上限制了系統中bitmap資源的數目,因此如果你需要使用很多的bitmap,你最好使用wxImage類來保存它們,而只在使用的時候將其轉化成bitmap.

在討論怎樣創建wxBitmap之前,讓我們先來討論一下幾個主要的函數(如下表所示)

 

ht!|>m3.8cm|>m10.72cm<|wxBitmap相關函數 wxBitmap 代表一個bitmap,可以通過指定寬度和高度,或者指定另外一個bitmap,或者指定一個wxImage,XPM數據(char**), 原始數據(char[]), 或者一個指定類型的文件名的方式來創建.
ConvertToImage 轉換成一個wxImage,保留透明部分.
CopyFromIcon 從一個wxIcon創建一個wxBitmap.
Create 從圖片數據或者一個給定的大小創建一個bitmap.
GetWidth, GetHeight 返回圖片大小.
Getdepth 返回圖片顏色深度.
GetMask, SetMask 返回綁定的wxMask對象或者NULL.
GetSubBitmap 將位圖其中的某一部分創建為一個新的位圖.
LoadFile, SaveFile 從某種支持格式的文件加載或者保存到文件里.
Ok 如果bitmap的數據已經具備則返回True.

 

創建一個wxBitmap

可以通過下面的幾個途徑來創建一個wxBitmap對象.

你可以直接通過默認的構造函數創建一個不包含數據的bitmap,不過你幾乎不能用這個bitmap作任何事情,除非你調用Create或者LoadFile或者用另外一個bitmap賦值以便使其擁有具體的bitmap數據.

你還可以通過指定寬度和高度的方法創建一個位圖,這種情況下創建的位圖將被填充以隨機的顏色,你需要在其上繪畫以便更好的使用它,下面的代碼演示了怎樣創建一個200x100的圖片並且將其的背景刷新為白色.

 

 
// 使用當前的顏色深度創建一個200x100的位圖
wxBitmap bitmap(200, 100,  -1);
// 創建一個內存設備上下文
wxMemoryDC dc;
// 將創建的圖片和這個內存設備上下文關聯
dc.SelectObject(bitmap);
// 設置背景顏色
dc.SetBackground(*wxWHITE_BRUSH);
// 繪制位圖背景
dc.Clear();
// 解除設備上下文和位圖的關聯
dc.SelectObject(wxNullBitmap);

你也可以從一個wxImage對象創建一個位圖,並且保留這個image對象的顏色遮罩或者alpha通道:

 

 
// 加載一幅圖像
wxImage image(wxT("image.png"), wxBITMAP_TYPE_PNG);
// 將其轉換成bitmap
wxBitmap bitmap(image);

通過CopyFromIcon函數可以通過圖標文件創建一個bitmap:

 

 
// 加載一個圖標
wxIcon icon(wxT("image.xpm"), wxBITMAP_TYPE_XPM);
// 將其轉換成位圖
wxBitmap bitmap;
bitmap.CopyFromIcon(icon);

或者你可以通過指定類型的方式從一個圖片文件直接加載一個位圖:

 

 
// 從文件加載
wxBitmap bitmap(wxT("picture.png", wxBITMAP_TYPE_PNG);
if (!bitmap.Ok())
{
    wxMessageBox(wxT("Sorry, could not load file."));
}

wxBitmap可以加載所有的可以被wxImage加載的圖片類型,使用的則是各個平台定義的針對特定類型的加載方法.最常用的圖片格式比如"PNG,JPG,BMP和XPM"等在各個平台上都支持讀取和保存操作,不過你需要確認你的wxWidgets在編譯的時候已經打開了對應的支持.

目前支持的圖形類型處理函數如下表所示:

 

|>m3.8cm|>m10.72cm<|已支持的圖像類型 wxBMPHandler 用來加載windows位圖文件.
wxPNGHandler 用來加載PNG類型的文件.這種文件支持透明背景以及alpha通道.
wxJPEGHandler 用來支持JPEG文件
wxGIFHandler 因為版權方面的原因,僅支持GIF格式的加載.
wxPCXHandler 用來支持PCX. wxPCXHandler會自己計算圖片中顏色的數目,如果沒有超過256色,則采用8bits顏色深度,否則就采用24 bits顏色深度.
wxPNMHandler 用來支持PNM文件格式. 對於加載來說PNM格式可以支持ASCII和raw RGB兩種格式.但是保存的時候僅支持raw RGB格式.
wxTIFFHandler 用來支持TIFF.
wxIFFHandler 用來支持IFF格式.
wxXPMHandler 用來支持XPM格式.
wxICOHandler 用來支持windows平台圖標文件.
wxCURHandler 用來支持windows平台光標文件.
wxANIHandler 用來支持windows平台動畫光標文件.

在Mac OS X平台上,還可以通過指定wxBITMAP_TYPE_PICT_RESOURCE來加載一個PICT資源.

如果你希望在不同的平台上從不同的位置加載圖片,你可以使用wxBITMAP宏,如下所示:

 

 
#if !defined(__WXMSW__) && !defined(__WXPM__)
#include "picture.xpm"
#endif
wxBitmap bitmap(wxBITMAP(picture));

這將使得程序在windows和OS/2平台上從資源文件中加載圖片,而在別的平台上,則從一個picture_xpm變量中加載xpm格式的圖片,因為XPM在所有的平台上都支持,所以這種使用方法並不常見.

 

設置一個wxMask

每個wxBitmap對象都可以指定一個wxMask,所謂wxMask指的是一個單色圖片用來指示原圖中的透明區域.如果你要加載的圖片中包含透明區域信息(比如XPM,PNG或者GIF格式),那么wxMask將被自動創建,另外你也可以通過代碼創建一個wxMask然后調用SetMask函數將其和對應的wxBitmap對象相關連.你還可以從wxBitmap對象創建一個wxMask,或者通過給一個wxBitmap對象指定一種透明顏色來創建一個wxMask對象.

下面的代碼創建了一個擁有透明色的灰階位圖mainBitmap,它的大小是32x32象素,原始圖形從imageBits數據創建,遮罩圖形從maskBits創建,遮罩中1代表不透明,0代表透明顏色.

 

 
static char imageBits[] = { 255, 255, 255, 255, 31,
  255, 255, 255, 31, 255, 255, 255, 31, 255, 255, 255,
  31, 255, 255, 255, 31, 255, 255, 255, 31, 255, 255,
  255, 31, 255, 255, 255, 31, 255, 255, 255, 25, 243,
  255, 255, 19, 249, 255, 255, 7, 252, 255, 255, 15, 254,
  255, 255, 31, 255, 255, 255, 191, 255, 255, 255, 255,
  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
  255 };
static char maskBits[] = { 240, 1, 0, 0, 240, 1,
  0, 0, 240, 1, 0, 0, 240, 1, 0, 0, 240, 1, 0, 0, 240, 1,
  0, 0, 240, 1, 0, 0, 240, 1, 0, 0, 255, 31, 0, 0, 255,
  31, 0, 0, 254, 15, 0, 0, 252, 7, 0, 0, 248, 3, 0, 0,
  240, 1, 0, 0, 224, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0 };
wxBitmap mainBitmap(imageBits, 32, 32);
wxBitmap maskBitmap(maskBits, 32, 32);
mainBitmap.SetMask(new wxMask(maskBitmap)); 

 

XPM圖形格式

在使用小的需要支持透明的圖片(比如工具欄上的小圖片或者notebook以及樹狀控件上的小圖片)的時候,wxWidgets的程序員通常偏愛使用XPM格式,它的最大的特點是采用C/C++語言的語法,既可以被程序動態加載,也可以直接編譯到可執行代碼中去,下面是一個例子:

 

 
// 你也可以用 #include "open.xpm"
static char *open_xpm[] = {
/* 列數 行數 顏色個數 每個象素的字符個數 */
"16 15 5 1",
"  c None",
". c Black",
"X c Yellow",
"o c Gray100",
"O c #bfbf00",
/* 象素 */
"                ",
"          ...   ",
"         .   . .",
"              ..",
"  ...        ...",
" .XoX.......    ",
" .oXoXoXoXo.    ",
" .XoXoXoXoX.    ",
" .oXoX..........",
" .XoX.OOOOOOOOO.",
" .oo.OOOOOOOOO. ",
" .X.OOOOOOOOO.  ",
" ..OOOOOOOOO.   ",
" ...........    ",
"                "
};
wxBitmap bitmap(open_xpm);

正如你看到的那樣,XPM是使用字符編碼的.在圖片數據前面,有一個調色板區域,使用字符和顏色對應的方法,顏色既可以用標准標識符表示,也可以用16進制的RGB值表示,使用關鍵字None來表示透明區域.盡管在windows系統上,XPM並不被大多數的圖形處理工具支持,不過你還是可以通過一些工具把PNG格式轉換成XPM格式,比如DialogBlocks自帶的ImageBlocks工具,或者你可以直接使用wxWidgets編寫一個你自己的轉換工具.

 

使用位圖繪畫

使用位圖繪畫的方式有兩種,你可以使用內存設備上下文綁定一個位圖,然后使用wxDC::Blit函數,也可以直接使用wxDC::DrawBitmap函數,前者允許你使用位圖的一部分進行繪制.在兩種方式下,如果這個圖片支持透明或者alpha通道,你都可以通過將最后一個參數指定為True或者False來打開或者關閉透明支持.

這兩種方法的用法如下:

 

 
// Draw a bitmap using a wxMemoryDC
wxMemoryDC memDC;
memDC.SelectObject(bitmap);
// Draw the bitmap at 100, 100 on the destination DC
destDC.Blit(100, 100,                         // Draw at (100, 100)
    bitmap.GetWidth(), bitmap.GetHeight(),    // Draw full bitmap
    & memDC,                                  // Draw from memDC
    0, 0,                                     // Draw from bitmap origin
    wxCOPY,                                   // Logical operation
    true);                                    // Take mask into account
memDC.SelectObject(wxNullBitmap);
// Alternative method: use DrawBitmap
destDC.DrawBitmap(bitmap, 100, 100, true);

第五章,"繪畫和打印"中對使用bitmap繪畫有更詳細的描述.

 

打包位圖資源

如果你曾是一個windows平台的程序員,你可能習慣從可執行文件的資源部分加載一幅圖片,當然在wxWidgets中也可以這樣作,你只需要指定一個資源名稱一個資源類型wxBITMAP_TYPE_BMP_RESOURCE,不過這種作法是平台相關的.你可能更傾向於使用另外一種平台無關的解決方案.

一個可移植的方法是,你可以將你用到的所有數據文件,包括HTML網頁,圖片或者別的任何類型的文件壓縮在一個zip文件里,然后你可以用wxWidgets提供的虛擬文件系統對加載這個zip文件的其中任何一個或幾個文件,如下面的代碼所示:

 

 
// 創建一個文件系統
wxFileSystem*fileSystem = new wxFileSystem;
wxString archiveURL(wxT("myapp.bin"));
wxString filename(wxT("myimage.png"));
wxBitmapType bitmapType = wxBITMAP_TYPE_PNG;
// 創建一個URL
wxString combinedURL(archiveURL + wxString(wxT("#zip:")) + filename);
wxImage image;
wxBitmap bitmap;
// 打開壓縮包中的對應文件
wxFSFile* file = fileSystem->OpenFile(combinedURL);
if (file)
{
    wxInputStream* stream = file->GetStream();

    // Load and convert to a bitmap
    if (image.LoadFile(* stream, bitmapType))
        bitmap = wxBitmap(image);

    delete file;
}
delete fileSystem;
if (bitmap.Ok())
{
    ...
}

更多關於虛擬文件系統的信息請參考第14章:文件和流操作.

 

使用wxIcon編程

一個wxIcon代表一個小的位圖,它總有一個透明遮罩,它的用途包括:

 

 

  • 設置frame窗口或者對話框的圖標
  • 通過wxImageList類給wxTreeCtrl, wxListCtrl或者wxNotebook提供圖標 (更多信息請參考最后一章)
  • 使用wxDC::DrawIcon函數在設備上下文中繪制一個圖標

 

下表列出了圖標類的主要成員函數

 

ht!|>m3.8cm|>m10.72cm<|wxIcon相關函數 wxIcon 圖標類可以通過指定另外一個圖標類的方式,指定XPM數據(char**)的方式, 原始數據(char[])的方式,或者文件名及文件類型的方式創建.
CopyFromBitmap 從wxBitmap類創建一個圖標.
GetWidth, GetHeight 返回圖標的大小.
Getdepth 返回圖標的顏色深度.
LoadFile 從文件加載圖標.
Ok 在圖標數據已經具備的時候返回True.

 

創建一個wxIcon

wxIcon可以使用XPM數據創建,或者從一個wxBitmap對象中創建,或者從文件(比如一個Xpm文件)中讀取.wxWidgets也提供了類似於前一小節提到的wxBITMAP類似的宏,用來從一個平台相關的資源中獲取圖標.

在windows平台上,LoadFile以及同等性質的操作可以使用的文件類型包括BMP圖片和ICO文件,如果你要從其它圖片格式中創建圖標,可以先將其讀入一個wxBitmap對象中,然后再將其轉換為一個圖標.

而在Mac OSX和 Unix/Linux的GTK+版本中,wxIcon可以識別的圖片類型和wxBitmap可以識別的圖片類型是一樣的.

下面代碼演示了創建一個wxIcon對象的幾種方法:

 

 
// 方法1: 從XPM數據創建
#include "icon1.xpm"
wxIcon icon1(icon1_xpm);
// 方法2: 從一個ICO資源中創建(Window and OS/2 only)
wxIcon icon2(wxT("icon2"));
// 方法3: 從一個圖片文件中 (Windows and OS/2 only)
// 如果你的圖片包含多個圖標你可以指定單個圖標的寬度
wxIcon icon3(wxT("icon3.ico"), wxBITMAP_TYPE_ICO, 16, 16);
// 方法4: 從位圖創建
wxIcon icon4;
wxBitmap bitmap(wxT("icon4.png"), wxBITMAP_TYPE_PNG);
icon4.CopyFromBitmap(bitmap);

 

使用wxIcon

下面的代碼演示了wxIcon的三種使用方法:設置窗口圖標,增加到一個圖片列表或者繪制在某個設備上下文上

 

 
#include "myicon.xpm"
wxIcon icon(myicon_xpm);
// 1: 設置窗口圖標
frame->SetIcon(icon);
// 2: 增加到wxImageList
wxImageList* imageList = new wxImageList(16, 16);
imageList->Add(icon);
// 3: 在(10, 10)的位置繪制
wxClientDC dc(window);
dc.DrawIcon(icon, 10, 10);

將某個圖標綁定到應用程序

將某個圖標綁定到應用程序,以便系統可以顯示這個圖標在合適的位置使得用戶可以通過點擊圖標的方式打開應用程序,這個工作wxWidgets是做不到的.這是極少的你需要在不同的平台使用不同的技術的領域中的一個.

在windows平台上,你需要在makefile中增加一個資源文件(擴展名是.rc),並且在這個資源文件中指定一個圖標區域,如下所示:

 

 
aardvarkpro ICON aardvarkpro.ico
#include "wx/msw/wx.rc"

在這里, aardvarkpro.ico就是這個和應用程序綁定的圖標的名稱,它可以有多種分辨率和顏色深度(典型的大小包括48x48,32x32和16x16).當windows的資源管理器需要顯示某個圖標的時候,它將使用子母順序排在第一個的那個圖標,因此你最好給確定要作為應用程序圖標的那個圖標的名稱前面加幾個a子母以便按照子母順序它排在前面,否則你的應用程序可能綁定的是你不期望的圖標.

在Mac系統上,你需要准備一個應用程序包,其中包含一些ICNS文件.參考第20章"讓你的程序更完美",來獲得關於程序包更多的信息,其中的主要文件Info.plist文件看上去應該象下面的額樣子:

 

 
  <key>CFBundleDocumentTypes</key>
  <array>
         <dict>
               <key>CFBundleTypeExtensions</key>
               <array>
                        <string>pjd</string>
                 </array>
                 <key>CFBundleTypeIconFile</key>
                 <string>dialogblocks-doc.icns</string>
                 <key>CFBundleTypeName</key>
                 <string>pjdfile</string>
                 <key>CFBundleTypeRole</key>
                 <string>Editor</string>
           </dict>
    </array>
    <key>CFBundleIconFile</key>
    <string>dialogblocks-app.icns</string>
...

應用程序圖標和應用程序相關的文檔類型圖標是由CFBundleIconFile和CFBundleTypeIconFile屬性指定的.你可以直接用Apple提供圖標編輯器編輯ICNS文件,不過如果你希望所有的平台使用同樣的圖標,你最好現用PNG圖片創建各種大小的圖標,然后再將它粘貼到各個平台上的圖標編輯器中,要確保PNG使用的透明遮罩顏色和各個工具使用的透明顏色相一致.

而在linux平台上,Gnome桌面系統和KDE桌面系統則各自擁有自己的圖標提供體系,我們將在第20章進行簡要的描述.

 

使用wxCursor編程

光標用來指示鼠標指針當前的位置.你可以給某個窗口指定不同的光標以便提示用戶這個窗口期待某種類型的鼠標操作.和圖標一樣,光標也是一種始終帶有透明遮罩的小圖片,可以使用一般的構造函數或者是平台相關的構造函數來創建.其中的一些構造函數還需要相對於整個圖片的左上角指定一個熱點位置,當鼠標點擊的時候,熱點所在的位置將作為鼠標點擊的位置.

下表列舉了光標相關的函數

|>m3.8cm|>m10.72cm<|wxCursor相關函數 wxCursor 光標可以從wxImage對象,二進制數據,系統定義的光標標識符以及光標文件來創建.
Ok 如果光標數據已經具備,則返回True.

 

創建一個光標

創建光標最簡單的方法是通過系統提供的光標標識符,如下面的例子所示:

 

 
wxCursor cursor(wxCURSOR_WAIT);

下表列出了目前支持的光標標識符和它們的光標的樣子(依照平台的不同會有些變化)

 

 

|l|l|p3in| 預定義的光標標識符
[]續上頁
未完待續 wxCURSOR_ARROW 標准光標.
wxCURSOR_RIGHT_ARROW 標准反向光標.
wxCURSOR_BLANK 透明光標.
wxCURSOR_BULLSEYE 近視眼.
wxCURSOR_CROSS 十字.
wxCURSOR_HAND 手.
wxCURSOR_IBEAM I字光標.
wxCURSOR_LEFT_BUTTON 按左鍵(GTK+ only).
wxCURSOR_MAGNIFIER 放大鏡.
wxCURSOR_MIDDLE_BUTTON 按中鍵(譯者注:原書圖片有誤) (GTK+ only).
wxCURSOR_NO_ENTRY 禁止通行.
wxCURSOR_PAINT_BRUSH 畫刷.
wxCURSOR_PENCIL 鉛筆.
wxCURSOR_POINT_LEFT 向左.
wxCURSOR_POINT_RIGHT 向右.
wxCURSOR_QUESTION_ARROW 帶問號的箭頭.
wxCURSOR_RIGHT_BUTTON 按右鍵(譯者注:圖片有誤) (GTK+ only).
wxCURSOR_SIZENESW 東北到西南伸縮.
wxCURSOR_SIZENS 南北伸縮.
wxCURSOR_SIZENWSE 西北到東南伸縮.
wxCURSOR_SIZEWE 東西伸縮.
wxCURSOR_SIZING 一般伸縮.
wxCURSOR_SPRAYCAN 畫刷.
wxCURSOR_WAIT 等待.
wxCURSOR_WATCH 查看.
wxCURSOR_ARROWWAIT 后台忙.

 

你還可以使用預定義光標指針wxSTANDARD_CURSOR, wxHOURGLASS_CURSOR和wxCROSS_CURSOR.

另外在windows和Mac平台上還可以從對應的資源文件中加載光標:

 

 
//windows平台
wxCursor cursor(wxT("cursor_resource"), wxBITMAP_TYPE_CUR_RESOURCE,
                 hotSpotX, hotSpotY);
// Mac平台
wxCursor cursor(wxT("cursor_resource"), wxBITMAP_TYPE_MACCUR_RESOURCE);

你還可以通過wxImage對象創建光標,而"熱點"則要通過wxImage::SetOptionInt函數設置.之所以要設置熱點,是因為很多光標不太適合使用默認的左上角作為熱點,比如對於十字光標來說,你可能希望將其十字交叉的地方作為熱點.下面的代碼演示了怎樣從一個PNG文件中產生設置了熱點的光標:

 

 
// 用wxImage創建光標
wxImage image(wxT("cursor.png"), wxBITMAP_TYPE_PNG);
image.SetOptionInt(wxIMAGE_OPTION_CUR_HOTSPOT_X, 5);
image.SetOptionInt(wxIMAGE_OPTION_CUR_HOTSPOT_Y, 5);
wxCursor cursor(image);

 

使用wxCursor

每個窗口都可以設置一個對應的光標,這個光標在鼠標進入這個窗口的時候顯示,如果一個窗口沒有設置光標,其父窗口的光標將被顯示,如果所有的父窗口都沒有設置光標,則系統默認光標被顯示:

使用下面的代碼給窗口設置一個光標:

 

 
window->SetCursor(wxCursor(wxCURSOR_WAIT));

使用wxSetCursorEvent

在windows系統或者是Mac OS X系統上,有一些小地方我們需要注意一下.舉個例子,如果你自己實現了一個容器類,比方說是一個分割窗口,並且給它設置了一個特殊的光標(比如說wxCURSOR_WE用來表明某個分割條是可以被拉動的),然后你在這個分割窗口中放置了兩個子窗口,如果你沒有給這兩個子窗口設置光標的話,當光標在子窗口上移動時,它們可能會不恰當的顯示其父窗口,那個wxCURSOR_WE光標.而本來你是希望只有在鼠標移動到分割條上的時候才顯示的.

要告訴wxWidgets某個光標只應該在某種情況下被顯示,你可以增加一個wxSetCursorEvent事件的處理函數,這個事件在Windows和Mac平台上,當需要設置光標的時候(通常是鼠標在窗口間移動的時候)被產生.在這個事件處理函數中可以調用wxSetCursorEvent::SetCursor來設置一個特殊的光標.如下所示:

 

 
BEGIN_EVENT_TABLE(wxSplitterWindow, wxWindow)
    EVT_SET_CURSOR(wxSplitterWindow::OnSetCursor)
END_EVENT_TABLE()
// 指示光標只應該被設置給分割條
void wxSplitterWindow::OnSetCursor(wxSetCursorEvent& event)
{
    if ( SashHitTest(event.GetX(), event.GetY(), 0) )
    {
        // 使用默認的處理
        event.Skip();
    }
    //else:什么也不作,換句話說,不調用Skip.則事件表不會被繼續搜索
}

在這個例子中,當鼠標指針移過分割條的時候,SashHitTest函數返回True,因此Skip函數被調用,事件表調用失敗,這和沒有定義這個事件表的效果是一樣的,導致wxWidgets象往常一樣顯示指定給窗口的光標(wxCURSOR_WE).而如果SashHitTest函數返回False,則表明光標是在子窗口上移動,這時候應該不顯示我們指定的光標,因此我們不調用Skip函數,讓事件表匹配成功,則事件表將不會在繼續匹配,這將使得wxWidgets認為這個窗口沒有被指定光標,因此.在這種情況下,即使子窗口自己沒有光標(象wxTextCtrl這種控件,一般系統會指定一個它自己的光標,不過wxWidgets對這個是不感知的),也將不會使用我們指定給父窗口的光標.

 

使用wxImage編程

你可以使用wxImage對圖形進行一些平台無關的調整,或者將其作為圖片加載和保存的中間步驟.圖片在wxImage中是按照每一個象素使用一個分別代表紅色,綠色和藍色的字節的格式保存的,如果圖片包含alpha通道,則還會占用額外的一個字節.

wxImage主要的函數如下:

 

ht!|>m3.8cm|>m10.72cm<|wxImage相關函數 wxImage wxImage的創建方法包括:指定寬度和高度, 從另外一幅圖片創建, 使用XPM數據, 圖片元數據(char[]) 和可選的alpha通道數據,文件名及其類型,以及通過輸入流等多種方式創建.
ConvertAlphaToMask 將alpla通道(如果有的話)轉換成一個透明遮罩.
ConvertToMono 轉換成一個黑白圖片.
Copy 返回一個不使用引用記數器的完全一樣的拷貝.
Create 創建一個指定大小的圖片,可選的參數指明是否初始化圖片數據.
Destroy 如果沒有人再使用的話,釋放內部數據.
GeTData, SetData 獲取和設置內部數據指針(unsigned char*).
GetImageCount 返回一個文件或者流中的圖片個數.
GetOption, GetOptionInt, SetOption, HasOption 獲取, 設置和測試某個選項是否設置.
GetSubImage 將圖片的一部分返回為一個新的圖像.
GetWidth, GetHeight 返回圖片大小.
Getred, GetGreen, GetBlue, SetRGB, GetAlpha, SetAlpha 獲得和指定某個象素的RGB以及Alpha通道的值.
HasMask, GetMaskRed, GetMaskGreen, GetMaskBlue, SetMaskColour 用來測試圖像是否有一個遮罩,以及遮罩顏色的RGB值或者整個顏色的值.
LoadFile, SaveFile 各種圖片格式文件的讀取和保存操作.
Mirror 在各種方向上產生鏡像,返回一個新圖片.
Ok 判斷圖片是否已初始化.
Paste 將某個圖片粘貼在這個圖片的指定位置.
Rotate, Rotate90 旋轉圖片,返回一個新圖片.
SetMaskFromImage 通過指定的圖片和透明顏色產生一個遮罩並且設置這個遮罩.
Scale, Rescale 縮放產生一個新圖片或者縮放本圖片.

 

加載和保存圖像

wxImage可以讀取和保存各種各樣的圖片格式,並且使用圖像處理過程來增加擴展的能力.其它的圖像類(比如wxBitmap)在某個平台不具備處理某種圖形格式的能力的時候,也通常使用的都是wxImage的圖象處理過程來加載特定格式的圖形.

本章第二小節中展示了wxWidgets支持的各種圖形處理過程.其中wxBMPHandler是默認支持的,而要支持其它的圖形格式處理,就需要使用wxImage::AddHandler函數增加對應的圖形處理過程或者使用wxInitAllImageHandlers增加所有支持的圖形處理過程.

如果你只需要特定的圖形格式支持,可以在OnInit函數中使用類似下面的代碼:

 

 
#include "wx/image.h"
wxImage::AddHandler( new wxPNGHandler );
wxImage::AddHandler( new wxJPEGHandler );
wxImage::AddHandler( new wxGIFHandler );
wxImage::AddHandler( new wxXPMHandler );

或者,你可以簡單的調用:

 

 
wxInitAllImageHandlers();

下面演示了幾種從文件或者流讀取圖片的方式,注意在實際使用過程中,最好使用絕對路徑以避免依賴於當前路徑的設置:

 

 
// 使用構造函數指定類型來讀取圖像
wxImage image(wxT("image.png"), wxBITMAP_TYPE_PNG);
if (image.Ok())
{
    ...
}
// 不指定圖像類型一般也能正常工作
wxImage image(wxT("image.png"));
// 使用兩步法創建圖像
wxImage image;
if (image.LoadFile(wxT("image.png")))
{
    ...
}
/* 如果一個文件包含兩副圖片Two-step loading with an 
index into a multi-image file:*/
// 下面演示選擇第2副加載
wxImage image;
int imageCount = wxImage::GetImageCount(wxT("image.tif"));
if (imageCount > 2)
    image.LoadFile(wxT("image.tif"), wxBITMAP_TYPE_TIFF, 2);
// 從文件流加載圖片
wxFileInputStream stream(wxT("image.tif"));
wxImage image;
image.LoadFile(stream, wxBITMAP_TYPE_TIF);
// 保存到一個文件
image.SaveFile(wxT("image.png")), wxBITMAP_TYPE_PNG);
// 保存到一個流
wxFileOutputStream stream(wxT("image.tif"));
image.SaveFile(stream, wxBITMAP_TYPE_TIF);

除了XPM和PCX格式以外,其它的圖片格式都將以24位顏色深度保存(譯者注:GIF格式因為版權方面的原因不支持保存到文件),這兩種格式的圖形處理過程將會計算實際的顏色個數從而選擇相應的顏色深度.JPEG格式還擁有一個質量選項可供設置.它的值的范圍為從0到100,0代表最低的圖片質量和最高的壓縮比,100則代表最高的圖片質量和最低的壓縮比.如下所示:

 

 
// 設置一個合理的質量壓縮比
image.SetOption(wxIMAGE_OPTION_QUALITY, 80);
image.SaveFile(wxT("picture.jpg"), wxBITMAP_TYPE_JPEG);

另外如果以XPM格式保存到流輸出中的時候,需要使用wxImage::SetOption函數設置一個名稱否則,處理函數不知道該用什么名稱命名對應的C變量.

 

 
// 保存XPM到流格式
image.SetOption(wxIMAGE_OPTION_FILENAME, wxT("myimage"));
image.SaveFile(stream, wxBITMAP_TYPE_XPM);

注意處理函數會自動在你設置的名稱后增加"_xpm".

 

透明

有兩種方式設置一個wxImage為透明的圖像:使用顏色遮罩或者alpha通道.一種顏色可以被指定為透明顏色,通過這種方法在將wxImage轉換成wxBitmap的時候可以很容易的制作一個透明遮罩.

wxImage也支持alpha通道數據,在每一個象素的RGB顏色之外來由另外一個字節用來指示alpha通道的值,0代表完全透明,255則代表完全不透明.中間的值代表半透明.

不是所有的圖片都用有alpha通道數據的,因此在使用GetAlpha函數之前,應該使用HasAlpha函數來判斷圖像是否擁有alpha通道數據.到目前為止,只有PNG文件或者調用SetAlpha設置了alpha通道的圖像才擁有alpha通道數據.保存一個帶有alpha通道的圖像目前還不被支持.繪制一個擁有alpha通道的方法是先將其轉換成wxBitmap然后使用wxDC::DrawBitmap或者wxDC::Blit函數.

下面的代碼演示了怎樣使用顏色掩碼創建一個透明的wxImage,它是藍色的,擁有一個透明的矩形區域:

 

 
// 創建一個有顏色掩碼的wxBitmap
// 首先,在這個wxBitmap上繪畫
wxBitmap bitmap(400, 400);
wxMemoryDC dc;
dc.SelectObject(bitmap);
dc.SetBackground(*wxBLUE_BRUSH);
dc.Clear();
dc.SetPen(*wxRED_PEN);
dc.SetBrush(*wxRED_BRUSH);
dc.DrawRectangle(50, 50, 200, 200);
dc.SelectObject(wxNullBitmap);
// 將其轉換成wxImage
wxImage image = bitmap.ConvertToImage();
// 設置掩碼顏色
image.SetMaskColour(255, 0, 0);

在下面的例子中,使用從一個圖片創建顏色遮罩的方式,其中image.bmp是原始圖像,而mask.bmp則是一個掩碼圖像,在后者中所有透明的部分都是黑色顯示的.

 

 
// 加載一副圖片和它的掩碼遮罩
wxImage image(wxT("image.bmp"), wxBITMAP_TYPE_BMP);
wxImage maskImage(wxT("mask.bmp"), wxBITMAP_TYPE_BMP);
// 從后者創建一個遮罩並且設置給前者.
image.SetMaskFromImage(maskImage, 0, 0, 0);

如果你加載的圖片本身含有透明顏色,你可以檢測並且直接創建遮罩:

 

 
// 加載透明圖片
wxImage image(wxT("image.png"), wxBITMAP_TYPE_PNG);
// 獲取掩碼
if (image.HasMask())
{
    wxColour maskColour(image.GetMaskRed(),
    image.GetMaskGreen(),
    image.GetMaskBlue());
}

 

變形

wxImage支持縮放,旋轉以及鏡像等多種變形方式,下面各舉一些例子:

 

 
// 把原始圖片縮放到200x200,並保存在新的圖片里
// 原圖保持不變.
wxImage image2 = image1.Scale(200, 200);
// 將原圖縮放到200x200
image1.Rescale(200, 200);
// 旋轉固定角度產生新圖片.
// 原圖片保持不變.
wxImage image2 = image1.Rotate(0.5);
// 順時針旋轉90度產生新圖片.
// 原圖保持不變.
wxImage image2 = image1.Rotate90(true);
// 水平鏡像產生新圖片.
// 原圖保持不變.
wxImage image2 = image1.Mirror(true);

 

顏色消減

如果你想對某個圖像的顏色進行消減,你可以使用wxQuantize類的一些靜態函數,其中最有趣的函數Quantize的參數為一個輸入圖片,一個輸出圖片,一個可選的wxPalette**指針用來存放經過消減的顏色,以及一個你希望保留的顏色個數,你也可以傳遞一個unsigned char**變量來獲取一個8-bit顏色深度的輸出圖像.最后的一個參數style(類型)用來對返回的圖像進行一些更深入的控制,詳情請參考wxWidgets的手冊.

下面的代碼演示了怎樣將一幅圖片的顏色消減到最多256色:

 

 
#include "wx/image.h"
#include "wx/quantize.h"
wxImage image(wxT("image.png"));
int maxColorCount = 256;
int colors = image.CountColours();
wxPalette* palette = NULL;
if (colors > maxColorCount )
{
    wxImage reducedImage;
    if (wxQuantize::Quantize(image, reducedImage,
                               & palette, maxColorCount))
    {
        colors = reducedImage.CountColours();
        image = reducedImage;
    }
}

一個wxImage可以設置一個wxPalette,例如加載GIF文件的時候. 然后,圖片內部仍然是以RGB的方式存儲數據的,調色板僅代表圖片加載時候的顏色隱射關系.調色板的另外一個用途是某些圖片處理函數用它來將圖片保存為低顏色深度的圖片,例如windows的BMP圖片處理過程將檢測是否設置了wxBMP_8BPP_PALETTE標記,如果設置了,則將使用調色板.而如果設置了wxBMP_8BPP標記(而不是wxBMP_8BPP_PALETTE),它將使用自己的算法進行顏色消減.另外某些圖片處理過程自己也進行顏色消減,比如PCX的處理過程,除非它認為剩余的顏色個數已經足夠低了,否則它將對圖片的顏色進行消減.

關於調色板更多的信息請參考第5章的"調色板"小節.

 

直接操作wxImage 的元數據

你可以直接通過GetData函數訪問wxImage的元數據以便以比GeTRed, GetBlue, GetGreen和SetRGB更快的方式對其進行操作,下面舉了一個使用這種方法將一個圖片轉換成灰度圖片的方法:

 

 
void wxImage::ConvertToGrayScale(wxImage& image)
{
    double red2Gray   = 0.297;
    double green2Gray = 0.589;
    double blue2Gray  = 0.114;
    int w = image.GetWidth(), h = image.GetHeight();
    unsigned char *data = image.GetData();
    int x,y;
    for (y = 0; y < h; y++)
        for (x = 0; x < w; x++)
        {
            long pos = (y * w + x) * 3;
            char g = (char) (data[pos]*red2Gray +
                              data[pos+1]*green2Gray +
                              data[pos+2]*blue2Gray);
            data[pos] = data[pos+1] = data[pos+2] = g;
        }
}

 

圖片列表和圖標集

有時候,使用一組圖片是非常方便的.這時候,你可以直接在你的代碼中使用wxImageList,也可以和wxWidgets提供的一些控件一起使用wxImageList,wxNotebook,wxtreeCtrl和wxListCtrl都需要wxImageList來管理它們所需要使用的圖標.你也可使用wxImageList中的某個單獨的圖片在設備上下文上繪畫.

創建一個wxImageList需要的參數包括單個圖片的寬度和高度,一個bool值來指定是否需要指定圖片遮罩,以及這個圖片列表的初始大小(主要是為了內部優化代碼),然后一個一個的增加wxBitmap對象或者wxIcon對象.wxImageList不能直接使用wxImage對象,你需要先將其轉換為wxBitmap對象.wxImageList::Add函數返回一個整數的索引用來代表這個剛增加的圖片,在Add函數成功返回以后,你就可以釋放原始圖片了,wxImageList已經在內部創建了一個這個圖片的拷貝.

下面是創建wxImageList以及在其中增加圖片的一些例子:

 

 
// 創建一個wxImageList
wxImageList *imageList = new wxImageList(16, 16, true, 1);
// 增加一個透明的PNG文件
wxBitmap bitmap1(wxT("image.png"), wxBITMAP_TYPE_PNG);
imageList->Add(bitmap1);
// 增加一個透明的來自別的bitmap的圖片
wxBitmap bitmap2(wxT("image.bmp"), wxBITMAP_TYPE_BMP);
wxBitmap maskBitmap(wxT("mask.bmp"), wxBITMAP_TYPE_BMP);
imageList->Add(bitmap2, maskBitmap);
// 增加一個指定透明顏色的透明圖片
wxBitmap bitmap3(wxT("image.bmp"), wxBITMAP_TYPE_BMP);
imageList->Add(bitmap3, *wxRED);
// 增加一個圖標
#include "folder.xpm"
wxIcon icon(folder_xpm);
imageList->Add(icon);

你可以直接把wxImageList中的圖片繪制在設備上下文上,通過指定wxIMAGELIST_DRAW_TRANSPARENT類型來指示繪制透明圖片,你還可以指定的類型包括wxIMAGELIST_DRAW_NORMAL, wxIMAGELIST_DRAW_SELECTED或者wxIMAGELIST_DRAW_FOCUSED,用來表征圖片的狀態,如下所示:

 

 
// 繪制列表中所有的圖片
wxClientDC dc(window);
size_t i;
for (i = 0; i < imageList->GetImageCount(); i++)
{
    imageList->Draw(i, dc, i*16, 0, wxIMAGELIST_DRAW_NORMAL|
                                    wxIMAGELIST_DRAW_TRANSPARENT);
}

要把圖片列表和notebook的TAB頁面綁定在一起,你需要創建一個包含大小為16x16的圖片的列表,然后調用wxNotebook::SetImageList或者wxNotebook::AssignImageList將其和某個wxNotebook綁定,這兩個函數的區別在於,前者在wxNotebook釋放的時候不釋放列表,而后者在自己被釋放的時候,會同時釋放圖片列表.指定完圖片列表以后,你就可以給某個頁面指定圖標索引以便在頁面標簽上顯示圖標了,下面的代碼演示了這個過程:

 

 
//創建一個wxImageList
wxImageList *imageList = new wxImageList(16, 16, true, 1);
// 增加一些圖標
wxBitmap bitmap1(wxT("folder.png"), wxBITMAP_TYPE_PNG);
wxBitmap bitmap2(wxT("file.png"), wxBITMAP_TYPE_PNG);
int folderIndex = imageList->Add(bitmap1);
int fileIndex = imageList->Add(bitmap2);
// 創建一個擁有兩個頁面的notebook
wxNotebook* notebook = new wxNotebook(parent, wxID_ANY);
wxPanel* page1 = new wxPanel(notebook, wxID_ANY);
wxPanel* page2 = new wxPanel(notebook, wxID_ANY);
// 綁定圖片列表
notebook->AssignImageList(imageList);
// Add the pages, with icons
notebook->AddPage(page1, wxT("Folder options"), true, folderIndex);
notebook->AddPage(page2, wxT("File options"), false, fileIndex);

wxtreeCtrl和wxListCtrl的使用方法和上面介紹的非常相似,也包含類似的兩種綁定方法.

如果你擁有很多圖標,有時候很難通過索引來對應到具體的圖標,你可能想編寫一個類以便通過字符串來找到某個圖片索引.下面演示了基於這個目的的一個簡單的實現:

 

 
#include "wx/hashmap.h"
WX_DECLARE_STRING_HASH_MAP(int, IconNameToIndexHashMap);
// 通過名字引用圖片的類
class IconNameToIndex
{
public:
    IconNameToIndex() {}
    // 在圖片列表中增加一個已經命名的圖片
    void Add(wxImageList* list, const wxBitmap& bitmap,
        const wxString& name) {
        m_hashMap[name] = list->Add(bitmap);
    }
    // 在圖片列表中增加一個已命名的圖標
    void Add(wxImageList* list, const wxIcon& icon,
        const wxString& name) {
        m_hashMap[name] = list->Add(icon);
    }
    // 通過名稱找到索引
    int Find(const wxString& name) { return m_hashMap[name]; }
private:
    IconNameToIndexHashMap m_hashMap;
};

wxIconBundle類同樣也是一個圖片列表,不過這個類的目的是為了將多個不同分辨率的圖標保存在一個類中而不是多個類中,以便系統在合適的時候根據不同的使用目的選擇一個特定的圖標.比如,在資源管理器中的圖標通常比在主窗口標題欄上顯示的圖標要大的多.下面的例子演示了其用法:

 

 
// 創建一個只有單個16x16圖標的圖片集
#include "file16x16.xpm"
wxIconBundle iconBundle(wxIcon(file16x16_xpm));
// 在圖片集中增加一個32x32的圖片
iconBundle.Add(wxIcon(wxT("file32x32.png"), wxBITMAP_TYPE_PNG));
// 從一個包含多個圖片的文件中創建一個圖片集
wxIconBundle iconBundle2(wxT("multi-icons.tif"), wxBITMAP_TYPE_TIF);
// 從圖片集中獲取指定大小的圖片,如果找不到則繼續尋找
// wxSYS_ICON_X, wxSYS_ICON_Y大小的圖片
wxIcon icon = iconBundle.GetIcon(wxSize(16,16));
// 將圖片集指定給某個主窗口
wxFrame* frame = new wxFrame(parent, wxID_ANY);
frame->SetIcons(iconBundle);

在windows系統上,SetIcons函數期待一個包含16x16和32x32大小的圖標的圖標集.

 

自定義wxWidgets提供的小圖片

wxArtProvider這個類允許你更改wxWidgets默認提供的那些小圖片,比如wxWidgets HTML幫助閱讀器中或者默認的Log對話框中使用的圖片.

wxWidgets提供了一個標准的wxArtProvider對象,並且體系內的一些需要使用圖標和小圖片的地方都調用了這個類的wxArtProvider::GetBitmap和wxArtProvider::GetIcon函數.

小圖片是由兩個標識符決定的:主標識符(wxArtID)和客戶區標識符(wxArtClient).其中客戶區標識符只在同一個主標識符在不同的窗口中需要不同的圖片的時候才使用,比如,wxHTML幫助窗口使用的圖標使用下面的代碼取得的:

 

 
wxBitmap bmp = wxArtProvider::GetBitmap(wxART_GO_BACK,wxART_TOOLBAR);

如果你想瀏覽所有wxWidgets提供的小圖片以及它們的標識符,你可以編譯和運行wxWidgets自帶的samples/artprov中的例子,它的外觀如下圖所示:

 

wxWidgets提供的artprov例子

要替換wxWidgets提供的這些小圖片,你需要實現一個wxArtProvider的派生類,重載其中的CreateBitmap函數,然后在OnInit函數中調用wxArtProvider::PushProvider以便讓wxWidgets知道.下面的這個例子替換了wxHTML幫助窗口中的大部分默認的圖標:

 

 
// 新的圖標
#include "bitmaps/helpbook.xpm"
#include "bitmaps/helppage.xpm"
#include "bitmaps/helpback.xpm"
#include "bitmaps/helpdown.xpm"
#include "bitmaps/helpforward.xpm"
#include "bitmaps/helpoptions.xpm"
#include "bitmaps/helpsidepanel.xpm"
#include "bitmaps/helpup.xpm"
#include "bitmaps/helpuplevel.xpm"
#include "bitmaps/helpicon.xpm"
#include "wx/artprov.h"
class MyArtProvider : public wxArtProvider
{
protected:
    virtual wxBitmap CreateBitmap(const wxArtID& id,
                                  const wxArtClient& client,
                                  const wxSize& size);
};
// 新的CreateBitmap函數
wxBitmap MyArtProvider::CreateBitmap(const wxArtID& id,
                                     const wxArtClient& client,
                                     const wxSize& size)
{
    if (id == wxART_HELP_SIDE_PANEL)
        return wxBitmap(helpsidepanel_xpm);
    if (id == wxART_HELP_SETTINGS)
        return wxBitmap(helpoptions_xpm);
    if (id == wxART_HELP_BOOK)
        return wxBitmap(helpbook_xpm);
    if (id == wxART_HELP_FOLDER)
        return wxBitmap(helpbook_xpm);
    if (id == wxART_HELP_PAGE)
        return wxBitmap(helppage_xpm);
    if (id == wxART_GO_BACK)
        return wxBitmap(helpback_xpm);
    if (id == wxART_GO_FORWARD)
        return wxBitmap(helpforward_xpm);
    if (id == wxART_GO_UP)
        return wxBitmap(helpup_xpm);
    if (id == wxART_GO_DOWN)
        return wxBitmap(helpdown_xpm);
    if (id == wxART_GO_TO_PARENT)
        return wxBitmap(helpuplevel_xpm);
    if (id == wxART_FRAME_ICON)
        return wxBitmap(helpicon_xpm);
    if (id == wxART_HELP)
        return wxBitmap(helpicon_xpm);

    // Any wxWidgets icons not implemented here
    // will be provided by the default art provider.
    return wxNullBitmap; 
}
// 你的初始化函數
bool MyApp::OnInit()
{
    ...
    wxArtProvider::PushProvider(new MyArtProvider);
    ...
    return true;
}

 

本章小結

在這一章里,我們學習了怎樣使用wxWidgets中的圖片相關的類wxBitmap, wxIcon, wxCursor和wxImage,還學習了怎樣使用wxImageList和wxIconBundle,以及怎樣定義wxWidgets默認使用的小圖片.更多相關的例子請參考wxWidgets自帶的samples/image, samples/listctrl和samples/dragimag目錄中的例子.

在下一章里,我們將介紹一下怎樣使用剪貼板來傳輸數據以及怎樣實現拖放編程.


LaTeX Original
\chapter{使用圖像編程}
這一章來了解一下我們可以使用圖片來作些什么事情.一幅圖勝過千言萬語,在wxWidgets,工具條,樹形控件,notebooks,按鈕,Html窗口和特定的繪畫代碼中,都會用到圖片.有時候它們還會在不可見的地方發揮作用,比如我們可以用它來創建雙緩沖區以避免閃爍.這一章里,我們會接觸到各種各樣的圖片類,還會談到怎樣覆蓋wxWidgets提供的默認圖片和圖標。
\section{wxWidgets中圖片相關的類}
wxWidgets支持四種和位圖相關的類:wxBitmap, wxIcon, wxCursor和wxImage.

wxBitmap是一個平台有關的類,它擁有一個可選的wxMask屬性以支持透明繪畫.在windows系統上,wxBitmap是通過設備無關位圖(DIBs)實現的,而在GTK+和X11平台上,每個wxBitmap則包含一個GDK的pixmap對象或者X11的pixmap對象.而在Mac平台上,則使用的是PICT.wxBitmap可以和wxImage進行互相轉換.

wxIcon用來實現各個平台上的圖標,一個圖標指的是一個小的透明圖片,可以用來代表不同的frame或者對話框窗口.在GTK+,X11和Mac平台上,icon就是一個小的總含有wxMask的wxBitmp,而在windows平台上,wxIcon則是封裝了HICON對象.

wxCursor則是一個用來展示鼠標指針的圖像,在GTK+平台上是用的GdkCursor,X11和Mac平台上用的是各自的Cursor,而在windows平台上則使用的是HCURSOR.wxCursor有一個熱點的概念(所謂熱點指的是圖片中用來精確代表指針單擊位置的那個點),也總是包含一個遮罩(mask).

wxImage則是四個類中唯一的一個平台無關的實現,它支持24bit位圖以及可選的alpha通道.wxImage可以從wxBitmap類使用wxBitmap::ConvertToImage函數轉換而來,也可以從各種各樣的圖片文件中加載,它所支持的圖片格式也是可以通過圖片格式處理器來擴展的.它擁有操作其圖片上某些bit的能力,因此也可以用來對圖片進行一個基本的操作.和wxBitmap不同,wxImage不可以直接被設備上下文wxDC使用,如果要在wxDC上繪圖,需要現將wxImage轉換成wxBitmap,然后就可以使用wxDC的DrawBitmap函數進行繪圖了.wxImage支持設置一個掩碼顏色來實現透明的效果,也支持通過alpha通道實現非常復雜的透明效果.

你可以在這些圖片類型之間進行相互轉換,盡管某些轉換操作是平台相關的.

注意圖片類中大量使用引用記數器,因此對圖片類進行賦值和拷貝的操作的系統開銷是非常小的,不過這也意味着對一個圖片的更改可能會影響到別的圖片.

所有的圖片類都使用下表列出的標准的wxBitmapType標識符來讀取或者保存圖片數據:

\begin{mytblex}{ht!}{|>{\setlength{\baselineskip}{15pt}}m{3.8cm}|>{\vspace*{5pt}\setlength{\baselineskip}{15pt}}m{10.72cm}<{\vspace*{3pt}}|}{位圖類型}\hline 
wxBITMAP\_TYPE\_BMP & Windows位圖文件 (BMP).\\
\hline
wxBITMAP\_TYPE\_BMP\_RESOURCE & 從windows可執行文件資源部分加載的Windows位圖.\\
\hline
wxBITMAP\_TYPE\_ICO & Windows圖標文件(ICO).\\
\hline
wxBITMAP\_TYPE\_ICO\_RESOURCE & 從windows可執行文件資源部分加載的Windows圖標.\\
\hline
wxBITMAP\_TYPE\_CUR & Windows光標文件(CUR).\\
\hline
wxBITMAP\_TYPE\_CUR\_RESOURCE & 從windows可執行文件資源部分加載的Windows光標.\\
\hline
wxBITMAP\_TYPE\_XBM & Unix平台上使用的XBM單色圖片.\\
\hline
wxBITMAP\_TYPE\_XBM\_DATA & 從C++數據中構造的XBM單色位圖.\\
\hline
wxBITMAP\_TYPE\_XPM & XPM格式圖片,最好的支持跨平台並且支持編譯到應用程序中去的格式.\\
\hline
wxBITMAP\_TYPE\_XPM\_DATA & 從C++數據中構造的XPM圖片.\\
\hline
wxBITMAP\_TYPE\_TIF & TIFF格式位圖,在大圖片中使用比較普遍.\\
\hline
wxBITMAP\_TYPE\_GIF & GIF格式圖片,最多支持256中顏色,支持透明.\\
\hline
wxBITMAP\_TYPE\_PNG & PNG位圖格式, 一個使用廣泛的圖片格式,支持透明和alpha通道,沒有版權問題.\\
\hline
wxBITMAP\_TYPE\_JPEG & JPEG格式位圖, 一個廣泛使用的壓縮圖片格式,支持大圖片,不過它的壓縮算法是有損耗壓縮,因此不適合對圖片進行反復加載和壓縮.\\
\hline
wxBITMAP\_TYPE\_PCX & PCX圖片格式.\\
\hline
wxBITMAP\_TYPE\_PICT & Mac PICT位圖.\\
\hline
wxBITMAP\_TYPE\_PICT\_RESOURCE & 從可執行文件資源部分加載的Mac PICT位圖.\\
\hline
wxBITMAP\_TYPE\_ICON\_RESOURCE & 僅在Mac OS X平台上有效, 用來加載一個標准的圖標(比如wxICON\_INFORMATION)或者一個圖標資源.\\
\hline
wxBITMAP\_TYPE\_ANI & Windows動畫圖標(ANI).\\
\hline
wxBITMAP\_TYPE\_IFF & IFF位圖文件.\\
\hline
wxBITMAP\_TYPE\_MACCURSOR & Mac光標文件.\\
\hline
wxBITMAP\_TYPE\_MACCURSOR\_RESOURCE & 從可執行文件資源部分加載的Mac光標.\\
\hline
wxBITMAP\_TYPE\_ANY & 讓加載圖片的代碼自己確定圖片的格式.\\
\hline
\end{mytblex} 

\section{使用wxBitmap編程}

你可以使用wxBitmap來作下面的事情:

\begin{enumerate}
\itemsep=0pt
\item 通過設備上下文將其畫在一個窗口上.
\item 在某些類(比如wxBitmapButton, wxStaticBitmap, and wxToolBar)中將其作為一個圖片標簽.
\item 使用其作為雙緩沖區(在將某個圖形繪制到屏幕上之前先繪制在一塊緩沖區上).
\end{enumerate}

某些平台(特別是windows平台)上限制了系統中bitmap資源的數目,因此如果你需要使用很多的bitmap,你最好使用wxImage類來保存它們,而只在使用的時候將其轉化成bitmap.

在討論怎樣創建wxBitmap之前,讓我們先來討論一下幾個主要的函數(如下表所示)

\begin{mytblex}{ht!}{|>{\setlength{\baselineskip}{15pt}}m{3.8cm}|>{\vspace*{5pt}\setlength{\baselineskip}{15pt}}m{10.72cm}<{\vspace*{3pt}}|}{wxBitmap相關函數}\hline 
wxBitmap & 代表一個bitmap,可以通過指定寬度和高度,或者指定另外一個bitmap,或者指定一個wxImage,XPM數據(char**), 原始數據(char[]), 或者一個指定類型的文件名的方式來創建.\\
\hline
ConvertToImage & 轉換成一個wxImage,保留透明部分.\\
\hline
CopyFromIcon & 從一個wxIcon創建一個wxBitmap.\\
\hline
Create & 從圖片數據或者一個給定的大小創建一個bitmap.\\
\hline
GetWidth, GetHeight & 返回圖片大小.\\
\hline
Getdepth & 返回圖片顏色深度.\\
\hline
GetMask, SetMask & 返回綁定的wxMask對象或者NULL.\\
\hline
GetSubBitmap & 將位圖其中的某一部分創建為一個新的位圖.\\
\hline
LoadFile, SaveFile & 從某種支持格式的文件加載或者保存到文件里.\\
\hline
Ok & 如果bitmap的數據已經具備則返回True.\\
\hline
\end{mytblex} 
   
\subsection{創建一個wxBitmap}

可以通過下面的幾個途徑來創建一個wxBitmap對象.

你可以直接通過默認的構造函數創建一個不包含數據的bitmap,不過你幾乎不能用這個bitmap作任何事情,除非你調用Create或者LoadFile或者用另外一個bitmap賦值以便使其擁有具體的bitmap數據.

你還可以通過指定寬度和高度的方法創建一個位圖,這種情況下創建的位圖將被填充以隨機的顏色,你需要在其上繪畫以便更好的使用它,下面的代碼演示了怎樣創建一個200x100的圖片並且將其的背景刷新為白色.


\begin{lstlisting} 
// 使用當前的顏色深度創建一個200x100的位圖
wxBitmap bitmap(200, 100,  -1);
// 創建一個內存設備上下文
wxMemoryDC dc;
// 將創建的圖片和這個內存設備上下文關聯
dc.SelectObject(bitmap);
// 設置背景顏色
dc.SetBackground(*wxWHITE_BRUSH);
// 繪制位圖背景
dc.Clear();
// 解除設備上下文和位圖的關聯
dc.SelectObject(wxNullBitmap);
\end{lstlisting}  

你也可以從一個wxImage對象創建一個位圖,並且保留這個image對象的顏色遮罩或者alpha通道:


\begin{lstlisting} 
// 加載一幅圖像
wxImage image(wxT("image.png"), wxBITMAP_TYPE_PNG);
// 將其轉換成bitmap
wxBitmap bitmap(image);
\end{lstlisting} 

通過CopyFromIcon函數可以通過圖標文件創建一個bitmap:


\begin{lstlisting} 
// 加載一個圖標
wxIcon icon(wxT("image.xpm"), wxBITMAP_TYPE_XPM);
// 將其轉換成位圖
wxBitmap bitmap;
bitmap.CopyFromIcon(icon);
\end{lstlisting} 

或者你可以通過指定類型的方式從一個圖片文件直接加載一個位圖:


\begin{lstlisting} 
// 從文件加載
wxBitmap bitmap(wxT("picture.png", wxBITMAP_TYPE_PNG);
if (!bitmap.Ok())
{
    wxMessageBox(wxT("Sorry, could not load file."));
}
\end{lstlisting} 

wxBitmap可以加載所有的可以被wxImage加載的圖片類型,使用的則是各個平台定義的針對特定類型的加載方法.最常用的圖片格式比如"PNG,JPG,BMP和XPM"等在各個平台上都支持讀取和保存操作,不過你需要確認你的wxWidgets在編譯的時候已經打開了對應的支持.

目前支持的圖形類型處理函數如下表所示:

\begin{mytbl}{|>{\setlength{\baselineskip}{15pt}}m{3.8cm}|>{\vspace*{5pt}\setlength{\baselineskip}{15pt}}m{10.72cm}<{\vspace*{3pt}}|}{已支持的圖像類型}\hline 
wxBMPHandler & 用來加載windows位圖文件.\\
\hline
wxPNGHandler & 用來加載PNG類型的文件.這種文件支持透明背景以及alpha通道.\\
\hline
wxJPEGHandler & 用來支持JPEG文件\\
\hline
wxGIFHandler & 因為版權方面的原因,僅支持GIF格式的加載.\\
\hline
wxPCXHandler & 用來支持PCX. wxPCXHandler會自己計算圖片中顏色的數目,如果沒有超過256色,則采用8bits顏色深度,否則就采用24
bits顏色深度.\\
\hline
wxPNMHandler & 用來支持PNM文件格式. 對於加載來說PNM格式可以支持ASCII和raw RGB兩種格式.但是保存的時候僅支持raw RGB格式.\\
\hline
wxTIFFHandler & 用來支持TIFF.\\
\hline
wxIFFHandler & 用來支持IFF格式.\\
\hline
wxXPMHandler & 用來支持XPM格式.\\
\hline
wxICOHandler & 用來支持windows平台圖標文件.\\
\hline
wxCURHandler & 用來支持windows平台光標文件.\\
\hline
wxANIHandler & 用來支持windows平台動畫光標文件.\\
\hline
\end{mytbl} 

在Mac OS X平台上,還可以通過指定wxBITMAP\_TYPE\_PICT\_RESOURCE來加載一個PICT資源.

如果你希望在不同的平台上從不同的位置加載圖片,你可以使用wxBITMAP宏,如下所示:


\begin{lstlisting} 
#if !defined(__WXMSW__) && !defined(__WXPM__)
#include "picture.xpm"
#endif
wxBitmap bitmap(wxBITMAP(picture));
\end{lstlisting} 

這將使得程序在windows和OS/2平台上從資源文件中加載圖片,而在別的平台上,則從一個picture\_xpm變量中加載xpm格式的圖片,因為XPM在所有的平台上都支持,所以這種使用方法並不常見.

\subsection{設置一個wxMask}

每個wxBitmap對象都可以指定一個wxMask,所謂wxMask指的是一個單色圖片用來指示原圖中的透明區域.如果你要加載的圖片中包含透明區域信息(比如XPM,PNG或者GIF格式),那么wxMask將被自動創建,另外你也可以通過代碼創建一個wxMask然后調用SetMask函數將其和對應的wxBitmap對象相關連.你還可以從wxBitmap對象創建一個wxMask,或者通過給一個wxBitmap對象指定一種透明顏色來創建一個wxMask對象.

下面的代碼創建了一個擁有透明色的灰階位圖mainBitmap,它的大小是32x32象素,原始圖形從imageBits數據創建,遮罩圖形從maskBits創建,遮罩中1代表不透明,0代表透明顏色.


\begin{lstlisting} 
static char imageBits[] = { 255, 255, 255, 255, 31,
  255, 255, 255, 31, 255, 255, 255, 31, 255, 255, 255,
  31, 255, 255, 255, 31, 255, 255, 255, 31, 255, 255,
  255, 31, 255, 255, 255, 31, 255, 255, 255, 25, 243,
  255, 255, 19, 249, 255, 255, 7, 252, 255, 255, 15, 254,
  255, 255, 31, 255, 255, 255, 191, 255, 255, 255, 255,
  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
  255 };
static char maskBits[] = { 240, 1, 0, 0, 240, 1,
  0, 0, 240, 1, 0, 0, 240, 1, 0, 0, 240, 1, 0, 0, 240, 1,
  0, 0, 240, 1, 0, 0, 240, 1, 0, 0, 255, 31, 0, 0, 255,
  31, 0, 0, 254, 15, 0, 0, 252, 7, 0, 0, 248, 3, 0, 0,
  240, 1, 0, 0, 224, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0 };
wxBitmap mainBitmap(imageBits, 32, 32);
wxBitmap maskBitmap(maskBits, 32, 32);
mainBitmap.SetMask(new wxMask(maskBitmap)); 
\end{lstlisting} 

\subsection{XPM圖形格式}

在使用小的需要支持透明的圖片(比如工具欄上的小圖片或者notebook以及樹狀控件上的小圖片)的時候,wxWidgets的程序員通常偏愛使用XPM格式,它的最大的特點是采用C/C++語言的語法,既可以被程序動態加載,也可以直接編譯到可執行代碼中去,下面是一個例子:


\begin{lstlisting} 
// 你也可以用 #include "open.xpm"
static char *open_xpm[] = {
/* 列數 行數 顏色個數 每個象素的字符個數 */
"16 15 5 1",
"  c None",
". c Black",
"X c Yellow",
"o c Gray100",
"O c #bfbf00",
/* 象素 */
"                ",
"          ...   ",
"         .   . .",
"              ..",
"  ...        ...",
" .XoX.......    ",
" .oXoXoXoXo.    ",
" .XoXoXoXoX.    ",
" .oXoX..........",
" .XoX.OOOOOOOOO.",
" .oo.OOOOOOOOO. ",
" .X.OOOOOOOOO.  ",
" ..OOOOOOOOO.   ",
" ...........    ",
"                "
};
wxBitmap bitmap(open_xpm);
\end{lstlisting} 

正如你看到的那樣,XPM是使用字符編碼的.在圖片數據前面,有一個調色板區域,使用字符和顏色對應的方法,顏色既可以用標准標識符表示,也可以用16進制的RGB值表示,使用關鍵字None來表示透明區域.盡管在windows系統上,XPM並不被大多數的圖形處理工具支持,不過你還是可以通過一些工具把PNG格式轉換成XPM格式,比如DialogBlocks自帶的ImageBlocks工具,或者你可以直接使用wxWidgets編寫一個你自己的轉換工具.

\subsection{使用位圖繪畫}

使用位圖繪畫的方式有兩種,你可以使用內存設備上下文綁定一個位圖,然后使用wxDC::Blit函數,也可以直接使用wxDC::DrawBitmap函數,前者允許你使用位圖的一部分進行繪制.在兩種方式下,如果這個圖片支持透明或者alpha通道,你都可以通過將最后一個參數指定為True或者False來打開或者關閉透明支持.

這兩種方法的用法如下:

\begin{lstlisting} 
// Draw a bitmap using a wxMemoryDC
wxMemoryDC memDC;
memDC.SelectObject(bitmap);
// Draw the bitmap at 100, 100 on the destination DC
destDC.Blit(100, 100,                         // Draw at (100, 100)
    bitmap.GetWidth(), bitmap.GetHeight(),    // Draw full bitmap
    & memDC,                                  // Draw from memDC
    0, 0,                                     // Draw from bitmap origin
    wxCOPY,                                   // Logical operation
    true);                                    // Take mask into account
memDC.SelectObject(wxNullBitmap);
// Alternative method: use DrawBitmap
destDC.DrawBitmap(bitmap, 100, 100, true);
\end{lstlisting} 

第五章,"繪畫和打印"中對使用bitmap繪畫有更詳細的描述.

\subsection{打包位圖資源}

如果你曾是一個windows平台的程序員,你可能習慣從可執行文件的資源部分加載一幅圖片,當然在wxWidgets中也可以這樣作,你只需要指定一個資源名稱一個資源類型wxBITMAP\_TYPE\_BMP\_RESOUR\-CE,不過這種作法是平台相關的.你可能更傾向於使用另外一種平台無關的解決方案.

一個可移植的方法是,你可以將你用到的所有數據文件,包括HTML網頁,圖片或者別的任何類型的文件壓縮在一個zip文件里,然后你可以用wxWidgets提供的虛擬文件系統對加載這個zip文件的其中任何一個或幾個文件,如下面的代碼所示:


\begin{lstlisting} 
// 創建一個文件系統
wxFileSystem*fileSystem = new wxFileSystem;
wxString archiveURL(wxT("myapp.bin"));
wxString filename(wxT("myimage.png"));
wxBitmapType bitmapType = wxBITMAP_TYPE_PNG;
// 創建一個URL
wxString combinedURL(archiveURL + wxString(wxT("#zip:")) + filename);
wxImage image;
wxBitmap bitmap;
// 打開壓縮包中的對應文件
wxFSFile* file = fileSystem->OpenFile(combinedURL);
if (file)
{
    wxInputStream* stream = file->GetStream();

    // Load and convert to a bitmap
    if (image.LoadFile(* stream, bitmapType))
        bitmap = wxBitmap(image);

    delete file;
}
delete fileSystem;
if (bitmap.Ok())
{
    ...
}
\end{lstlisting} 

更多關於虛擬文件系統的信息請參考第14章:文件和流操作.

\section{使用wxIcon編程}

一個wxIcon代表一個小的位圖,它總有一個透明遮罩,它的用途包括:

\begin{itemize}
\itemsep=0pt
\item 設置frame窗口或者對話框的圖標
\item 通過wxImageList類給wxTreeCtrl, wxListCtrl或者wxNotebook提供圖標 (更多信息請參考最后一章)
\item 使用wxDC::DrawIcon函數在設備上下文中繪制一個圖標
\end{itemize}


下表列出了圖標類的主要成員函數

\begin{mytblex}{ht!}{|>{\setlength{\baselineskip}{15pt}}m{3.8cm}|>{\vspace*{5pt}\setlength{\baselineskip}{15pt}}m{10.72cm}<{\vspace*{3pt}}|}{wxIcon相關函數}\hline 
wxIcon & 圖標類可以通過指定另外一個圖標類的方式,指定XPM數據(char**)的方式, 原始數據(char[])的方式,或者文件名及文件類型的方式創建.\\
\hline
CopyFromBitmap & 從wxBitmap類創建一個圖標.\\
\hline
GetWidth, GetHeight & 返回圖標的大小.\\
\hline
Getdepth & 返回圖標的顏色深度.\\
\hline
LoadFile & 從文件加載圖標.\\
\hline
Ok & 在圖標數據已經具備的時候返回True.\\
\hline
\end{mytblex} 
   
\subsection{創建一個wxIcon}

wxIcon可以使用XPM數據創建,或者從一個wxBitmap對象中創建,或者從文件(比如一個Xpm文件)中讀取.wxWidgets也提供了類似於前一小節提到的wxBITMAP類似的宏,用來從一個平台相關的資源中獲取圖標.

在windows平台上,LoadFile以及同等性質的操作可以使用的文件類型包括BMP圖片和ICO文件,如果你要從其它圖片格式中創建圖標,可以先將其讀入一個wxBitmap對象中,然后再將其轉換為一個圖標.

而在Mac OSX和 Unix/Linux的GTK+版本中,wxIcon可以識別的圖片類型和wxBitmap可以識別的圖片類型是一樣的.

下面代碼演示了創建一個wxIcon對象的幾種方法:


\begin{lstlisting} 
// 方法1: 從XPM數據創建
#include "icon1.xpm"
wxIcon icon1(icon1_xpm);
// 方法2: 從一個ICO資源中創建(Window and OS/2 only)
wxIcon icon2(wxT("icon2"));
// 方法3: 從一個圖片文件中 (Windows and OS/2 only)
// 如果你的圖片包含多個圖標你可以指定單個圖標的寬度
wxIcon icon3(wxT("icon3.ico"), wxBITMAP_TYPE_ICO, 16, 16);
// 方法4: 從位圖創建
wxIcon icon4;
wxBitmap bitmap(wxT("icon4.png"), wxBITMAP_TYPE_PNG);
icon4.CopyFromBitmap(bitmap);
\end{lstlisting} 
 
\subsection{使用wxIcon}

下面的代碼演示了wxIcon的三種使用方法:設置窗口圖標,增加到一個圖片列表或者繪制在某個設備上下文上


\begin{lstlisting} 
#include "myicon.xpm"
wxIcon icon(myicon_xpm);
// 1: 設置窗口圖標
frame->SetIcon(icon);
// 2: 增加到wxImageList
wxImageList* imageList = new wxImageList(16, 16);
imageList->Add(icon);
// 3: 在(10, 10)的位置繪制
wxClientDC dc(window);
dc.DrawIcon(icon, 10, 10);
\end{lstlisting} 

將某個圖標綁定到應用程序

將某個圖標綁定到應用程序,以便系統可以顯示這個圖標在合適的位置使得用戶可以通過點擊圖標的方式打開應用程序,這個工作wxWidgets是做不到的.這是極少的你需要在不同的平台使用不同的技術的領域中的一個.

在windows平台上,你需要在makefile中增加一個資源文件(擴展名是.rc),並且在這個資源文件中指定一個圖標區域,如下所示:


\begin{lstlisting} 
aardvarkpro ICON aardvarkpro.ico
#include "wx/msw/wx.rc"
\end{lstlisting} 

在這里, aardvarkpro.ico就是這個和應用程序綁定的圖標的名稱,它可以有多種分辨率和顏色深度(典型的大小包括48x48,32x32和16x16).當windows的資源管理器需要顯示某個圖標的時候,它將使用子母順序排在第一個的那個圖標,因此你最好給確定要作為應用程序圖標的那個圖標的名稱前面加幾個a子母以便按照子母順序它排在前面,否則你的應用程序可能綁定的是你不期望的圖標.

在Mac系統上,你需要准備一個應用程序包,其中包含一些ICNS文件.參考第20章"讓你的程序更完美",來獲得關於程序包更多的信息,其中的主要文件Info.plist文件看上去應該象下面的額樣子:

\begin{lstlisting} 
  <key>CFBundleDocumentTypes</key>
  <array>
         <dict>
               <key>CFBundleTypeExtensions</key>
               <array>
                        <string>pjd</string>
                 </array>
                 <key>CFBundleTypeIconFile</key>
                 <string>dialogblocks-doc.icns</string>
                 <key>CFBundleTypeName</key>
                 <string>pjdfile</string>
                 <key>CFBundleTypeRole</key>
                 <string>Editor</string>
           </dict>
    </array>
    <key>CFBundleIconFile</key>
    <string>dialogblocks-app.icns</string>
...
\end{lstlisting} 

應用程序圖標和應用程序相關的文檔類型圖標是由CFBundleIconFile和CFBundleTypeIconFile屬性指定的.你可以直接用Apple提供圖標編輯器編輯ICNS文件,不過如果你希望所有的平台使用同樣的圖標,你最好現用PNG圖片創建各種大小的圖標,然后再將它粘貼到各個平台上的圖標編輯器中,要確保PNG使用的透明遮罩顏色和各個工具使用的透明顏色相一致.

而在linux平台上,Gnome桌面系統和KDE桌面系統則各自擁有自己的圖標提供體系,我們將在第20章進行簡要的描述.

\section{使用wxCursor編程}

光標用來指示鼠標指針當前的位置.你可以給某個窗口指定不同的光標以便提示用戶這個窗口期待某種類型的鼠標操作.和圖標一樣,光標也是一種始終帶有透明遮罩的小圖片,可以使用一般的構造函數或者是平台相關的構造函數來創建.其中的一些構造函數還需要相對於整個圖片的左上角指定一個熱點位置,當鼠標點擊的時候,熱點所在的位置將作為鼠標點擊的位置.

下表列舉了光標相關的函數
\begin{mytbl}{|>{\setlength{\baselineskip}{15pt}}m{3.8cm}|>{\vspace*{5pt}\setlength{\baselineskip}{15pt}}m{10.72cm}<{\vspace*{3pt}}|}{wxCursor相關函數}\hline 
wxCursor & 光標可以從wxImage對象,二進制數據,系統定義的光標標識符以及光標文件來創建.\\
\hline
Ok & 如果光標數據已經具備,則返回True.\\
\hline
\end{mytbl} 
   
\subsection{創建一個光標}

創建光標最簡單的方法是通過系統提供的光標標識符,如下面的例子所示:

\begin{lstlisting} 
wxCursor cursor(wxCURSOR_WAIT);
\end{lstlisting} 

下表列出了目前支持的光標標識符和它們的光標的樣子(依照平台的不同會有些變化)

\begin{small}\begin{longtable}{|l|l|p{3in}|}
\caption{預定義的光標標識符}\\
\hline\endfirsthead
\caption[]{\emph{續上頁}}\\
\hline\endhead
\hline
\multicolumn{2}{r}{\emph{未完待續}}
\endfoot
\hline\endlastfoot
wxCURSOR\_ARROW & \includegraphics[scale=.6]{i10-01} &  標准光標.\\
\hline
wxCURSOR\_RIGHT\_ARROW & \includegraphics[scale=.6]{i10-06} &  標准反向光標.\\
\hline
wxCURSOR\_BLANK   &  & 透明光標.\\
\hline
wxCURSOR\_BULLSEYE & \includegraphics[scale=.6]{i10-07} &  近視眼.\\
\hline
wxCURSOR\_CROSS & \includegraphics[scale=.6]{i10-08} &  十字.\\
\hline
wxCURSOR\_HAND & \includegraphics[scale=.6]{i10-09} &  手.\\
\hline
wxCURSOR\_IBEAM & \includegraphics[scale=.6]{i10-10} &  I字光標.\\
\hline
wxCURSOR\_LEFT\_BUTTON & \includegraphics[scale=.6]{i10-11} &  按左鍵(GTK+ only).\\
\hline
wxCURSOR\_MAGNIFIER & \includegraphics[scale=.6]{i10-12} &  放大鏡.\\
\hline
wxCURSOR\_MIDDLE\_BUTTON & \includegraphics[scale=.6]{i10-10} &  按中鍵(譯者注:原書圖片有誤) (GTK+ only).\\
\hline
wxCURSOR\_NO\_ENTRY & \includegraphics[scale=.6]{i10-02} &  禁止通行.\\
\hline
wxCURSOR\_PAINT\_BRUSH & \includegraphics[scale=.6]{i10-03} &  畫刷.\\
\hline
wxCURSOR\_PENCIL & \includegraphics[scale=.6]{i10-04} &  鉛筆.\\
\hline
wxCURSOR\_POINT\_LEFT & \includegraphics[scale=.6]{i10-05} &  向左.\\
\hline
wxCURSOR\_POINT\_RIGHT & \includegraphics[scale=.6]{i10-14} &  向右.\\
\hline
wxCURSOR\_QUESTION\_ARROW & \includegraphics[scale=.6]{i10-18} &  帶問號的箭頭.\\
\hline
wxCURSOR\_RIGHT\_BUTTON & \includegraphics[scale=.6]{i10-10} &  按右鍵(譯者注:圖片有誤) (GTK+ only).\\
\hline
wxCURSOR\_SIZENESW & \includegraphics[scale=.6]{i10-20} &  東北到西南伸縮.\\
\hline
wxCURSOR\_SIZENS & \includegraphics[scale=.6]{i10-21} &  南北伸縮.\\
\hline
wxCURSOR\_SIZENWSE & \includegraphics[scale=.6]{i10-22} &  西北到東南伸縮.\\
\hline
wxCURSOR\_SIZEWE & \includegraphics[scale=.6]{i10-23} &  東西伸縮.\\
\hline
wxCURSOR\_SIZING & \includegraphics[scale=.6]{i10-24} &  一般伸縮.\\
\hline
wxCURSOR\_SPRAYCAN & \includegraphics[scale=.6]{i10-03} &  畫刷.\\
\hline
wxCURSOR\_WAIT & \includegraphics[scale=.6]{i10-15} &  等待.\\
\hline
wxCURSOR\_WATCH & \includegraphics[scale=.6]{i10-16} &  查看.\\
\hline
wxCURSOR\_ARROWWAIT & \includegraphics[scale=.6]{i10-17} &  后台忙.\\
\hline
\end{longtable}\end{small}

你還可以使用預定義光標指針wxSTANDARD\_CURSOR, wxHOURGLASS\_CURSOR和wxCROSS\_CURSOR.

另外在windows和Mac平台上還可以從對應的資源文件中加載光標:

\begin{lstlisting} 
//windows平台
wxCursor cursor(wxT("cursor_resource"), wxBITMAP_TYPE_CUR_RESOURCE,
                 hotSpotX, hotSpotY);
// Mac平台
wxCursor cursor(wxT("cursor_resource"), wxBITMAP_TYPE_MACCUR_RESOURCE);
\end{lstlisting} 

你還可以通過wxImage對象創建光標,而"熱點"則要通過wxImage::SetOptionInt函數設置.之所以要設置熱點,是因為很多光標不太適合使用默認的左上角作為熱點,比如對於十字光標來說,你可能希望將其十字交叉的地方作為熱點.下面的代碼演示了怎樣從一個PNG文件中產生設置了熱點的光標:


\begin{lstlisting} 
// 用wxImage創建光標
wxImage image(wxT("cursor.png"), wxBITMAP_TYPE_PNG);
image.SetOptionInt(wxIMAGE_OPTION_CUR_HOTSPOT_X, 5);
image.SetOptionInt(wxIMAGE_OPTION_CUR_HOTSPOT_Y, 5);
wxCursor cursor(image);
\end{lstlisting} 

\subsection{使用wxCursor}

每個窗口都可以設置一個對應的光標,這個光標在鼠標進入這個窗口的時候顯示,如果一個窗口沒有設置光標,其父窗口的光標將被顯示,如果所有的父窗口都沒有設置光標,則系統默認光標被顯示:

使用下面的代碼給窗口設置一個光標:

\begin{lstlisting} 
window->SetCursor(wxCursor(wxCURSOR_WAIT));
\end{lstlisting} 

使用wxSetCursorEvent

在windows系統或者是Mac OS X系統上,有一些小地方我們需要注意一下.舉個例子,如果你自己實現了一個容器類,比方說是一個分割窗口,並且給它設置了一個特殊的光標(比如說wxCURSOR\_WE用來表明某個分割條是可以被拉動的),然后你在這個分割窗口中放置了兩個子窗口,如果你沒有給這兩個子窗口設置光標的話,當光標在子窗口上移動時,它們可能會不恰當的顯示其父窗口,那個wxCURSOR\_WE光標.而本來你是希望只有在鼠標移動到分割條上的時候才顯示的.

要告訴wxWidgets某個光標只應該在某種情況下被顯示,你可以增加一個wxSetCursorEvent事件的處理函數,這個事件在Windows和Mac平台上,當需要設置光標的時候(通常是鼠標在窗口間移動的時候)被產生.在這個事件處理函數中可以調用wxSetCursorEvent::SetCursor來設置一個特殊的光標.如下所示:


\begin{lstlisting} 
BEGIN_EVENT_TABLE(wxSplitterWindow, wxWindow)
    EVT_SET_CURSOR(wxSplitterWindow::OnSetCursor)
END_EVENT_TABLE()
// 指示光標只應該被設置給分割條
void wxSplitterWindow::OnSetCursor(wxSetCursorEvent& event)
{
    if ( SashHitTest(event.GetX(), event.GetY(), 0) )
    {
        // 使用默認的處理
        event.Skip();
    }
    //else:什么也不作,換句話說,不調用Skip.則事件表不會被繼續搜索
}
\end{lstlisting} 

在這個例子中,當鼠標指針移過分割條的時候,SashHitTest函數返回True,因此Skip函數被調用,事件表調用失敗,這和沒有定義這個事件表的效果是一樣的,導致wxWidgets象往常一樣顯示指定給窗口的光標(wxCURSOR\_WE).而如果SashHitTest函數返回False,則表明光標是在子窗口上移動,這時候應該不顯示我們指定的光標,因此我們不調用Skip函數,讓事件表匹配成功,則事件表將不會在繼續匹配,這將使得wxWidgets認為這個窗口沒有被指定光標,因此.在這種情況下,即使子窗口自己沒有光標(象wxTextCtrl這種控件,一般系統會指定一個它自己的光標,不過wxWidgets對這個是不感知的),也將不會使用我們指定給父窗口的光標.

\section{使用wxImage編程}

你可以使用wxImage對圖形進行一些平台無關的調整,或者將其作為圖片加載和保存的中間步驟.圖片在wxImage中是按照每一個象素使用一個分別代表紅色,綠色和藍色的字節的格式保存的,如果圖片包含alpha通道,則還會占用額外的一個字節.

wxImage主要的函數如下:

\begin{mytblex}{ht!}{|>{\setlength{\baselineskip}{15pt}}m{3.8cm}|>{\vspace*{5pt}\setlength{\baselineskip}{15pt}}m{10.72cm}<{\vspace*{3pt}}|}{wxImage相關函數}\hline 
wxImage & wxImage的創建方法包括:指定寬度和高度, 從另外一幅圖片創建, 使用XPM數據, 圖片元數據(char[]) 和可選的alpha通道數據,文件名及其類型,以及通過輸入流等多種方式創建.\\
\hline
ConvertAlphaToMask & 將alpla通道(如果有的話)轉換成一個透明遮罩.\\
\hline
ConvertToMono & 轉換成一個黑白圖片.\\
\hline
Copy & 返回一個不使用引用記數器的完全一樣的拷貝.\\
\hline
Create & 創建一個指定大小的圖片,可選的參數指明是否初始化圖片數據.\\
\hline
Destroy & 如果沒有人再使用的話,釋放內部數據.\\
\hline
GeTData, SetData & 獲取和設置內部數據指針(unsigned char*).\\
\hline
GetImageCount & 返回一個文件或者流中的圖片個數.\\
\hline
GetOption, GetOptionInt, SetOption, HasOption & 獲取, 設置和測試某個選項是否設置.\\
\hline
GetSubImage & 將圖片的一部分返回為一個新的圖像.\\
\hline
GetWidth, GetHeight & 返回圖片大小.\\
\hline
Getred, GetGreen, GetBlue, SetRGB, GetAlpha, SetAlpha & 獲得和指定某個象素的RGB以及Alpha通道的值.\\
\hline
HasMask, GetMaskRed, GetMaskGreen, GetMaskBlue, SetMaskColour & 用來測試圖像是否有一個遮罩,以及遮罩顏色的RGB值或者整個顏色的值.\\
\hline
LoadFile, SaveFile & 各種圖片格式文件的讀取和保存操作.\\
\hline
Mirror & 在各種方向上產生鏡像,返回一個新圖片.\\
\hline
Ok & 判斷圖片是否已初始化.\\
\hline
Paste & 將某個圖片粘貼在這個圖片的指定位置.\\
\hline
Rotate, Rotate90 & 旋轉圖片,返回一個新圖片.\\
\hline
SetMaskFromImage & 通過指定的圖片和透明顏色產生一個遮罩並且設置這個遮罩.\\
\hline
Scale, Rescale & 縮放產生一個新圖片或者縮放本圖片.\\
\hline
\end{mytblex} 
   
\subsection{加載和保存圖像}

wxImage可以讀取和保存各種各樣的圖片格式,並且使用圖像處理過程來增加擴展的能力.其它的圖像類(比如wxBitmap)在某個平台不具備處理某種圖形格式的能力的時候,也通常使用的都是wxImage的圖象處理過程來加載特定格式的圖形.

本章第二小節中展示了wxWidgets支持的各種圖形處理過程.其中wxBMPHandler是默認支持的,而要支持其它的圖形格式處理,就需要使用wxImage::AddHandler函數增加對應的圖形處理過程或者使用wxInitAllImageHandlers增加所有支持的圖形處理過程.

如果你只需要特定的圖形格式支持,可以在OnInit函數中使用類似下面的代碼:


\begin{lstlisting} 
#include "wx/image.h"
wxImage::AddHandler( new wxPNGHandler );
wxImage::AddHandler( new wxJPEGHandler );
wxImage::AddHandler( new wxGIFHandler );
wxImage::AddHandler( new wxXPMHandler );
\end{lstlisting} 

或者,你可以簡單的調用:


\begin{lstlisting} 
wxInitAllImageHandlers();
\end{lstlisting} 

下面演示了幾種從文件或者流讀取圖片的方式,注意在實際使用過程中,最好使用絕對路徑以避免依賴於當前路徑的設置:


\begin{lstlisting} 
// 使用構造函數指定類型來讀取圖像
wxImage image(wxT("image.png"), wxBITMAP_TYPE_PNG);
if (image.Ok())
{
    ...
}
// 不指定圖像類型一般也能正常工作
wxImage image(wxT("image.png"));
// 使用兩步法創建圖像
wxImage image;
if (image.LoadFile(wxT("image.png")))
{
    ...
}
/* 如果一個文件包含兩副圖片Two-step loading with an 
index into a multi-image file:*/
// 下面演示選擇第2副加載
wxImage image;
int imageCount = wxImage::GetImageCount(wxT("image.tif"));
if (imageCount > 2)
    image.LoadFile(wxT("image.tif"), wxBITMAP_TYPE_TIFF, 2);
// 從文件流加載圖片
wxFileInputStream stream(wxT("image.tif"));
wxImage image;
image.LoadFile(stream, wxBITMAP_TYPE_TIF);
// 保存到一個文件
image.SaveFile(wxT("image.png")), wxBITMAP_TYPE_PNG);
// 保存到一個流
wxFileOutputStream stream(wxT("image.tif"));
image.SaveFile(stream, wxBITMAP_TYPE_TIF);
\end{lstlisting}  

除了XPM和PCX格式以外,其它的圖片格式都將以24位顏色深度保存(譯者注:GIF格式因為版權方面的原因不支持保存到文件),這兩種格式的圖形處理過程將會計算實際的顏色個數從而選擇相應的顏色深度.JPEG格式還擁有一個質量選項可供設置.它的值的范圍為從0到100,0代表最低的圖片質量和最高的壓縮比,100則代表最高的圖片質量和最低的壓縮比.如下所示:


\begin{lstlisting} 
// 設置一個合理的質量壓縮比
image.SetOption(wxIMAGE_OPTION_QUALITY, 80);
image.SaveFile(wxT("picture.jpg"), wxBITMAP_TYPE_JPEG);
\end{lstlisting}  

另外如果以XPM格式保存到流輸出中的時候,需要使用wxImage::SetOption函數設置一個名稱否則,處理函數不知道該用什么名稱命名對應的C變量.


\begin{lstlisting} 
// 保存XPM到流格式
image.SetOption(wxIMAGE_OPTION_FILENAME, wxT("myimage"));
image.SaveFile(stream, wxBITMAP_TYPE_XPM);
\end{lstlisting}  

注意處理函數會自動在你設置的名稱后增加"\_xpm".

\subsection{透明}

有兩種方式設置一個wxImage為透明的圖像:使用顏色遮罩或者alpha通道.一種顏色可以被指定為透明顏色,通過這種方法在將wxImage轉換成wxBitmap的時候可以很容易的制作一個透明遮罩.

wxImage也支持alpha通道數據,在每一個象素的RGB顏色之外來由另外一個字節用來指示alpha通道的值,0代表完全透明,255則代表完全不透明.中間的值代表半透明.

不是所有的圖片都用有alpha通道數據的,因此在使用GetAlpha函數之前,應該使用HasAlpha函數來判斷圖像是否擁有alpha通道數據.到目前為止,只有PNG文件或者調用SetAlpha設置了alpha通道的圖像才擁有alpha通道數據.保存一個帶有alpha通道的圖像目前還不被支持.繪制一個擁有alpha通道的方法是先將其轉換成wxBitmap然后使用wxDC::DrawBitmap或者wxDC::Blit函數.

下面的代碼演示了怎樣使用顏色掩碼創建一個透明的wxImage,它是藍色的,擁有一個透明的矩形區域:


\begin{lstlisting} 
// 創建一個有顏色掩碼的wxBitmap
// 首先,在這個wxBitmap上繪畫
wxBitmap bitmap(400, 400);
wxMemoryDC dc;
dc.SelectObject(bitmap);
dc.SetBackground(*wxBLUE_BRUSH);
dc.Clear();
dc.SetPen(*wxRED_PEN);
dc.SetBrush(*wxRED_BRUSH);
dc.DrawRectangle(50, 50, 200, 200);
dc.SelectObject(wxNullBitmap);
// 將其轉換成wxImage
wxImage image = bitmap.ConvertToImage();
// 設置掩碼顏色
image.SetMaskColour(255, 0, 0);
\end{lstlisting}  

在下面的例子中,使用從一個圖片創建顏色遮罩的方式,其中image.bmp是原始圖像,而mask.bmp則是一個掩碼圖像,在后者中所有透明的部分都是黑色顯示的.


\begin{lstlisting} 
// 加載一副圖片和它的掩碼遮罩
wxImage image(wxT("image.bmp"), wxBITMAP_TYPE_BMP);
wxImage maskImage(wxT("mask.bmp"), wxBITMAP_TYPE_BMP);
// 從后者創建一個遮罩並且設置給前者.
image.SetMaskFromImage(maskImage, 0, 0, 0);
\end{lstlisting}  

如果你加載的圖片本身含有透明顏色,你可以檢測並且直接創建遮罩:


\begin{lstlisting} 
// 加載透明圖片
wxImage image(wxT("image.png"), wxBITMAP_TYPE_PNG);
// 獲取掩碼
if (image.HasMask())
{
    wxColour maskColour(image.GetMaskRed(),
    image.GetMaskGreen(),
    image.GetMaskBlue());
}
\end{lstlisting} 

\subsection{變形}

wxImage支持縮放,旋轉以及鏡像等多種變形方式,下面各舉一些例子:


\begin{lstlisting} 
// 把原始圖片縮放到200x200,並保存在新的圖片里
// 原圖保持不變.
wxImage image2 = image1.Scale(200, 200);
// 將原圖縮放到200x200
image1.Rescale(200, 200);
// 旋轉固定角度產生新圖片.
// 原圖片保持不變.
wxImage image2 = image1.Rotate(0.5);
// 順時針旋轉90度產生新圖片.
// 原圖保持不變.
wxImage image2 = image1.Rotate90(true);
// 水平鏡像產生新圖片.
// 原圖保持不變.
wxImage image2 = image1.Mirror(true);
\end{lstlisting} 

\subsection{顏色消減}

如果你想對某個圖像的顏色進行消減,你可以使用wxQuantize類的一些靜態函數,其中最有趣的函數Quantize的參數為一個輸入圖片,一個輸出圖片,一個可選的wxPalette**指針用來存放經過消減的顏色,以及一個你希望保留的顏色個數,你也可以傳遞一個unsigned char**變量來獲取一個8-bit顏色深度的輸出圖像.最后的一個參數style(類型)用來對返回的圖像進行一些更深入的控制,詳情請參考wxWidgets的手冊.

下面的代碼演示了怎樣將一幅圖片的顏色消減到最多256色:


\begin{lstlisting} 
#include "wx/image.h"
#include "wx/quantize.h"
wxImage image(wxT("image.png"));
int maxColorCount = 256;
int colors = image.CountColours();
wxPalette* palette = NULL;
if (colors > maxColorCount )
{
    wxImage reducedImage;
    if (wxQuantize::Quantize(image, reducedImage,
                               & palette, maxColorCount))
    {
        colors = reducedImage.CountColours();
        image = reducedImage;
    }
}
\end{lstlisting}  

一個wxImage可以設置一個wxPalette,例如加載GIF文件的時候. 然后,圖片內部仍然是以RGB的方式存儲數據的,調色板僅代表圖片加載時候的顏色隱射關系.調色板的另外一個用途是某些圖片處理函數用它來將圖片保存為低顏色深度的圖片,例如windows的BMP圖片處理過程將檢測是否設置了wxBMP\_8BPP\_PALETTE標記,如果設置了,則將使用調色板.而如果設置了wxBMP\_8BPP標記(而不是wxBMP\_8BPP\_PALETTE),它將使用自己的算法進行顏色消減.另外某些圖片處理過程自己也進行顏色消減,比如PCX的處理過程,除非它認為剩余的顏色個數已經足夠低了,否則它將對圖片的顏色進行消減.

關於調色板更多的信息請參考第5章的"調色板"小節.

\subsection{直接操作wxImage 的元數據}

你可以直接通過GetData函數訪問wxImage的元數據以便以比GeTRed, GetBlue, GetGreen和SetRGB更快的方式對其進行操作,下面舉了一個使用這種方法將一個圖片轉換成灰度圖片的方法:


\begin{lstlisting} 
void wxImage::ConvertToGrayScale(wxImage& image)
{
    double red2Gray   = 0.297;
    double green2Gray = 0.589;
    double blue2Gray  = 0.114;
    int w = image.GetWidth(), h = image.GetHeight();
    unsigned char *data = image.GetData();
    int x,y;
    for (y = 0; y < h; y++)
        for (x = 0; x < w; x++)
        {
            long pos = (y * w + x) * 3;
            char g = (char) (data[pos]*red2Gray +
                              data[pos+1]*green2Gray +
                              data[pos+2]*blue2Gray);
            data[pos] = data[pos+1] = data[pos+2] = g;
        }
}
\end{lstlisting} 

\section{圖片列表和圖標集}

有時候,使用一組圖片是非常方便的.這時候,你可以直接在你的代碼中使用wxImageList,也可以和wxWidgets提供的一些控件一起使用wxImageList,wxNotebook,wxtreeCtrl和wxListCtrl都需要wxImageList來管理它們所需要使用的圖標.你也可使用wxImageList中的某個單獨的圖片在設備上下文上繪畫.

創建一個wxImageList需要的參數包括單個圖片的寬度和高度,一個bool值來指定是否需要指定圖片遮罩,以及這個圖片列表的初始大小(主要是為了內部優化代碼),然后一個一個的增加wxBitmap對象或者wxIcon對象.wxImageList不能直接使用wxImage對象,你需要先將其轉換為wxBitmap對象.wxImageList::Add函數返回一個整數的索引用來代表這個剛增加的圖片,在Add函數成功返回以后,你就可以釋放原始圖片了,wxImageList已經在內部創建了一個這個圖片的拷貝.

下面是創建wxImageList以及在其中增加圖片的一些例子:


\begin{lstlisting} 
// 創建一個wxImageList
wxImageList *imageList = new wxImageList(16, 16, true, 1);
// 增加一個透明的PNG文件
wxBitmap bitmap1(wxT("image.png"), wxBITMAP_TYPE_PNG);
imageList->Add(bitmap1);
// 增加一個透明的來自別的bitmap的圖片
wxBitmap bitmap2(wxT("image.bmp"), wxBITMAP_TYPE_BMP);
wxBitmap maskBitmap(wxT("mask.bmp"), wxBITMAP_TYPE_BMP);
imageList->Add(bitmap2, maskBitmap);
// 增加一個指定透明顏色的透明圖片
wxBitmap bitmap3(wxT("image.bmp"), wxBITMAP_TYPE_BMP);
imageList->Add(bitmap3, *wxRED);
// 增加一個圖標
#include "folder.xpm"
wxIcon icon(folder_xpm);
imageList->Add(icon);
\end{lstlisting} 

你可以直接把wxImageList中的圖片繪制在設備上下文上,通過指定wxIMAGELIST\_DRAW\_TRANS\-PARENT類型來指示繪制透明圖片,你還可以指定的類型包括wxIMAGELIST\_DRAW\_NORMAL, wxIMAGELIST\_DRAW\-\_SELECTED或者wxIMAGELIST\_DRAW\_FOCUSED,用來表征圖片的狀態,如下所示:


\begin{lstlisting} 
// 繪制列表中所有的圖片
wxClientDC dc(window);
size_t i;
for (i = 0; i < imageList->GetImageCount(); i++)
{
    imageList->Draw(i, dc, i*16, 0, wxIMAGELIST_DRAW_NORMAL|
                                    wxIMAGELIST_DRAW_TRANSPARENT);
}
\end{lstlisting} 

要把圖片列表和notebook的TAB頁面綁定在一起,你需要創建一個包含大小為16x16的圖片的列表,然后調用wxNotebook::SetImageList或者wxNotebook::AssignImageList將其和某個wxNotebook綁定,這兩個函數的區別在於,前者在wxNotebook釋放的時候不釋放列表,而后者在自己被釋放的時候,會同時釋放圖片列表.指定完圖片列表以后,你就可以給某個頁面指定圖標索引以便在頁面標簽上顯示圖標了,下面的代碼演示了這個過程:


\begin{lstlisting} 
//創建一個wxImageList
wxImageList *imageList = new wxImageList(16, 16, true, 1);
// 增加一些圖標
wxBitmap bitmap1(wxT("folder.png"), wxBITMAP_TYPE_PNG);
wxBitmap bitmap2(wxT("file.png"), wxBITMAP_TYPE_PNG);
int folderIndex = imageList->Add(bitmap1);
int fileIndex = imageList->Add(bitmap2);
// 創建一個擁有兩個頁面的notebook
wxNotebook* notebook = new wxNotebook(parent, wxID_ANY);
wxPanel* page1 = new wxPanel(notebook, wxID_ANY);
wxPanel* page2 = new wxPanel(notebook, wxID_ANY);
// 綁定圖片列表
notebook->AssignImageList(imageList);
// Add the pages, with icons
notebook->AddPage(page1, wxT("Folder options"), true, folderIndex);
notebook->AddPage(page2, wxT("File options"), false, fileIndex);
\end{lstlisting} 

wxtreeCtrl和wxListCtrl的使用方法和上面介紹的非常相似,也包含類似的兩種綁定方法.

如果你擁有很多圖標,有時候很難通過索引來對應到具體的圖標,你可能想編寫一個類以便通過字符串來找到某個圖片索引.下面演示了基於這個目的的一個簡單的實現:


\begin{lstlisting} 
#include "wx/hashmap.h"
WX_DECLARE_STRING_HASH_MAP(int, IconNameToIndexHashMap);
// 通過名字引用圖片的類
class IconNameToIndex
{
public:
    IconNameToIndex() {}
    // 在圖片列表中增加一個已經命名的圖片
    void Add(wxImageList* list, const wxBitmap& bitmap,
        const wxString& name) {
        m_hashMap[name] = list->Add(bitmap);
    }
    // 在圖片列表中增加一個已命名的圖標
    void Add(wxImageList* list, const wxIcon& icon,
        const wxString& name) {
        m_hashMap[name] = list->Add(icon);
    }
    // 通過名稱找到索引
    int Find(const wxString& name) { return m_hashMap[name]; }
private:
    IconNameToIndexHashMap m_hashMap;
};
\end{lstlisting} 

wxIconBundle類同樣也是一個圖片列表,不過這個類的目的是為了將多個不同分辨率的圖標保存在一個類中而不是多個類中,以便系統在合適的時候根據不同的使用目的選擇一個特定的圖標.比如,在資源管理器中的圖標通常比在主窗口標題欄上顯示的圖標要大的多.下面的例子演示了其用法:


\begin{lstlisting} 
// 創建一個只有單個16x16圖標的圖片集
#include "file16x16.xpm"
wxIconBundle iconBundle(wxIcon(file16x16_xpm));
// 在圖片集中增加一個32x32的圖片
iconBundle.Add(wxIcon(wxT("file32x32.png"), wxBITMAP_TYPE_PNG));
// 從一個包含多個圖片的文件中創建一個圖片集
wxIconBundle iconBundle2(wxT("multi-icons.tif"), wxBITMAP_TYPE_TIF);
// 從圖片集中獲取指定大小的圖片,如果找不到則繼續尋找
// wxSYS_ICON_X, wxSYS_ICON_Y大小的圖片
wxIcon icon = iconBundle.GetIcon(wxSize(16,16));
// 將圖片集指定給某個主窗口
wxFrame* frame = new wxFrame(parent, wxID_ANY);
frame->SetIcons(iconBundle);
\end{lstlisting} 

在windows系統上,SetIcons函數期待一個包含16x16和32x32大小的圖標的圖標集.

\section{自定義wxWidgets提供的小圖片}

wxArtProvider這個類允許你更改wxWidgets默認提供的那些小圖片,比如wxWidgets HTML幫助閱讀器中或者默認的Log對話框中使用的圖片.

wxWidgets提供了一個標准的wxArtProvider對象,並且體系內的一些需要使用圖標和小圖片的地方都調用了這個類的wxArtProvider::GetBitmap和wxArtProvider::GetIcon函數.

小圖片是由兩個標識符決定的:主標識符(wxArtID)和客戶區標識符(wxArtClient).其中客戶區標識符只在同一個主標識符在不同的窗口中需要不同的圖片的時候才使用,比如,wxHTML幫助窗口使用的圖標使用下面的代碼取得的:


\begin{lstlisting} 
wxBitmap bmp = wxArtProvider::GetBitmap(wxART_GO_BACK,wxART_TOOLBAR);
\end{lstlisting} 

如果你想瀏覽所有wxWidgets提供的小圖片以及它們的標識符,你可以編譯和運行wxWidgets自帶的samples/artprov中的例子,它的外觀如下圖所示:


\begin{figure}[ht!]
\centering
\includegraphics[scale=.6]{f10-01}
\caption{wxWidgets提供的artprov例子}
\end{figure} 

要替換wxWidgets提供的這些小圖片,你需要實現一個wxArtProvider的派生類,重載其中的CreateBitmap函數,然后在OnInit函數中調用wxArtProvider::PushProvider以便讓wxWidgets知道.下面的這個例子替換了wxHTML幫助窗口中的大部分默認的圖標:


\begin{lstlisting} 
// 新的圖標
#include "bitmaps/helpbook.xpm"
#include "bitmaps/helppage.xpm"
#include "bitmaps/helpback.xpm"
#include "bitmaps/helpdown.xpm"
#include "bitmaps/helpforward.xpm"
#include "bitmaps/helpoptions.xpm"
#include "bitmaps/helpsidepanel.xpm"
#include "bitmaps/helpup.xpm"
#include "bitmaps/helpuplevel.xpm"
#include "bitmaps/helpicon.xpm"
#include "wx/artprov.h"
class MyArtProvider : public wxArtProvider
{
protected:
    virtual wxBitmap CreateBitmap(const wxArtID& id,
                                  const wxArtClient& client,
                                  const wxSize& size);
};
// 新的CreateBitmap函數
wxBitmap MyArtProvider::CreateBitmap(const wxArtID& id,
                                     const wxArtClient& client,
                                     const wxSize& size)
{
    if (id == wxART_HELP_SIDE_PANEL)
        return wxBitmap(helpsidepanel_xpm);
    if (id == wxART_HELP_SETTINGS)
        return wxBitmap(helpoptions_xpm);
    if (id == wxART_HELP_BOOK)
        return wxBitmap(helpbook_xpm);
    if (id == wxART_HELP_FOLDER)
        return wxBitmap(helpbook_xpm);
    if (id == wxART_HELP_PAGE)
        return wxBitmap(helppage_xpm);
    if (id == wxART_GO_BACK)
        return wxBitmap(helpback_xpm);
    if (id == wxART_GO_FORWARD)
        return wxBitmap(helpforward_xpm);
    if (id == wxART_GO_UP)
        return wxBitmap(helpup_xpm);
    if (id == wxART_GO_DOWN)
        return wxBitmap(helpdown_xpm);
    if (id == wxART_GO_TO_PARENT)
        return wxBitmap(helpuplevel_xpm);
    if (id == wxART_FRAME_ICON)
        return wxBitmap(helpicon_xpm);
    if (id == wxART_HELP)
        return wxBitmap(helpicon_xpm);

    // Any wxWidgets icons not implemented here
    // will be provided by the default art provider.
    return wxNullBitmap; 
}
// 你的初始化函數
bool MyApp::OnInit()
{
    ...
    wxArtProvider::PushProvider(new MyArtProvider);
    ...
    return true;
}
\end{lstlisting} 

\section{本章小結}

在這一章里,我們學習了怎樣使用wxWidgets中的圖片相關的類wxBitmap, wxIcon, wxCursor和wxImage,還學習了怎樣使用wxImageList和wxIconBundle,以及怎樣定義wxWidgets默認使用的小圖片.更多相關的例子請參考wxWidgets自帶的samples/image, samples/listctrl和samples/dragimag目錄中的例子.

在下一章里,我們將介紹一下怎樣使用剪貼板來傳輸數據以及怎樣實現拖放編程.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM