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目录中的例子. 在下一章里,我们将介绍一下怎样使用剪贴板来传输数据以及怎样实现拖放编程.
上一篇:Windows中通过命令行新建文件夹、新建文件,和一些常用命令


下一篇:美团DB数据同步到数据仓库的架构与实践