第十二章 操作基本图像
本章内容:
- 装载图像和创建图像对象
- 创建设备上下文
- 绘制到设备上下文
- 绘制文本到设备上下文
- 管理画刷、画笔和设备坐标
任何用户界面工具的最基本的行为就是在屏幕上绘制。在最基本的层面上来说,定义在wxPython
中的每个窗口部件都是由发送给屏幕的一系列命令构成的。那些绘制命令是否是在wxPython
代码库中,这依赖于相关窗口部件对于本地操作系统是否是本地化的或完全由wxPython
所定义的。在这一章,我们将给你展示如何在基本绘制命令层面上控制wxPython
。我们也将给你展示如何管理和显示其它的图形化元素如图像和字体。
被wxPython
用于绘制的主要的概念是设备上下文(device context
)。设备上下文使用一个标准的API
来管理对设备(如屏幕或打印机)的绘制。设备上下文类用于最基本的绘制功能,如绘制一条直线、曲线或文本。
使用图像工作
大多数的应用程序都需要载入至少一个图像,这些图像存储在外部的文件中。样例包括工具栏图片、光标、图标、启始画面或仅仅用于装饰以增加一些时髦感的图像。传统上,使用图像工作的复杂性是不得不处理用于储存图像的不同的图片文件格式。幸运的是,wxPython
内部为你做了所有的这些。你将使用相同的抽象概念来处理任何图像,而不用关心它的原始格式。
在随后的几节中,我们将讨论wxPython
用来管理图像的那么抽象概念,这包括大尺寸图像(large
-scale images)
,以及光标图像。你将看到如何装载图像到你的程序中,然后如何操作它们。
如何载入图像?
在wxPython
中,图像处理是一个双主管系统,与平台无关的图像处理由类wx.Image
管理,而与平台有关的图像处理由类wx.Bitmap
管理。实际上,意思就是外部文件格式由wx.Image
装载和保存,而wx.Bitmap
负责将图像显示到屏幕。图12.1显示了不同图像和位图的创建,按照不同的文件类型读入。
要从一个文件载入一个图像,使用wx.Image
的构造函数:
wx.Image(name, type=wx.BITMAP_TYPE_ANY, index=-1)
图12.1
参数name
是图像文件的名字,参数type
(类型)是处理器类型。type
的ID
可以是wx.BITMAP_TYPE_ANY
或表12.1中的一个。如果你使用wx.BITMAP_TYPE_ANY
,那么wxPython
将试图自动检测该文件的类型。如果你使用一个特定的文件类型,那么wxPython
将使用该类型转换这个文件。例12.1显示了如何使用wx.BITMAP_TYPE_ANY
来载入图像。
例12.1 载入并缩放简单图像
import wx
filenames = ["image.bmp", "image.gif", "image.jpg", "image.png" ]
class TestFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title="Loading Images")
p = wx.Panel(self)
fgs = wx.FlexGridSizer(cols=2, hgap=10, vgap=10)
for name in filenames:
#1 从文件载入图像
img1 = wx.Image(name, wx.BITMAP_TYPE_ANY)
# Scale the oiginal to another wx.Image
w = img1.GetWidth()
h = img1.GetHeight()
img2 = img1.Scale(w/2, h/2)#2 缩小图像
#3 转换它们为静态位图部件
sb1 = wx.StaticBitmap(p, -1, wx.BitmapFromImage(img1))
sb2 = wx.StaticBitmap(p, -1, wx.BitmapFromImage(img2))
# and put them into the sizer
fgs.Add(sb1)
fgs.Add(sb2)
p.SetSizerAndFit(fgs)
self.Fit()
app = wx.PySimpleApp()
frm = TestFrame()
frm.Show()
app.MainLoop()
上面的代码应该很简单。代码开始是我们想要载入的图像文件的名字,我们使用wx.BITMAP_TYPE_ANY
类型标记指示wxPython
去断定图像文件的格式,而用不着我们操心。然后我们使用图像的方法将图像缩小一半,并将图像转换为位图。
你也可以显示地指定图像文件的格式,在下一节,我们将显示wxPython
所支持的图像文件格式。
指定一个图像文件格式
图像由所用的图像处理器管理。一个图像处理器是wx.ImageHandler
的一个实例,它为管理图像格式提供了一个插入式的结构。在通常的情况下,你不需要考虑图像处理器是如何工作的。你所需要知道的是每个处理器都有它自己唯一的wxPython
标识符,用以载入相关格式的文件。表12.1列出了所支持的格式。
表12.1 wxPython
支持的图像文件格式
处理器类 | 类型标记 | 说明 |
---|---|---|
wx.ANIHandler |
wx.BITMAP_TYPE_ANI |
动画光标格式。这个处理器只载入图像而不保存它们。 |
wx.BMPHandler |
wx.BITMAP_TYPE_BMP |
Windows 和OS /2位图格式。 |
wx.CURHandle |
wx.BITMAP_TYPE_CUR |
Windows 光标 图标格式。 |
wx.GIFHandler |
wx.BITMAP_TYPE_GIF |
图形交换格式。由于版权限制,这个处理器不保存图像。 |
wx.ICOHandler |
wx.BITMAP_TYPE_ICO |
Windows 图标格式。 |
wx.IFFHandler |
wx.BITMAP_TYPE_IFF |
交换文件格式。这个处理器只载入图像,它不保存它们。 |
wx.JPEGHandler |
wx.BITMAP_TYPE_JPEG |
联合图形专家组格式。 |
wx.PCXHandler |
wx.BITMAP_TYPE_PCX |
PC 画刷格式。当以这种格式保存时,wxPython 计算在这个图像中的不同颜色的数量。如果可能的话,这个图像被保存为一个8位图像(也就是说,如果它有256种或更少的颜色)。否则它保存为24位。 |
wx.PNGHandler |
wx.BITMAP_TYPE_PNG |
便携式网络图形格式。 |
wx.PNMHandler |
wx.BITMAP_TYPE_PNM |
只能载入ASCII 或原始的RGB 图像。图像被该处理器保存为原始的RGB 。 |
wx.TIFFHandler |
wx.BITMAP_TYPE_TIF |
标签图像文件格式。 |
wx.XPMHandler |
wx.BITMAP_TYPE_XPM |
XPixMap 格式。 |
自动 | wx.BITMAP_TYPE_ANY |
自动检测使用的格式,然后调用相应的处理器。 |
要使用一个MIME
类型来标识文件,而不是一个处理器类型ID
的话,请使用函数wx.ImageFromMime(name, mimetype, index
=-1),其中的name
是文件名,mimetype
是有关文件类型的字符串。参数index
表示在图像文件包含多个图像的情况下要载入的图像的索引。这仅仅被GIF, ICO,
和TIFF
处理器使用。默认值-1表示选择默认的图像,被GIF
和TIFF
处理顺解释为每一个图像(index
=0),被ICO
处理器解释为最大且最多色彩的一个。
创建image
(图像)对象
wxPython
使用不同的全局函数来创建不同种类的wx.Image
对象。要创建一个有着特定尺寸的空图像,使用函数wx.EmptyImage(width,height)
——在这个被创建的图像中所有的像素都是黑色。要创建从一个打开的流或Python
文件类对象创建一个图像,使用wx.ImageFromStream(stream,type
=wx.BITMAP_TYPE_ANY, index
=-1)。有时,根据一个原始的RGB
数据来创建一个图像是有用的,这使用wx.ImageFromData(width,height,data)
,data
是一个字符串,每套连续的三个字符代表一个像素的红,绿,蓝的组分。这个字符串的大小应该是width
height
3。
创建bitmap
(位图)对象
有几个方法可以创建一个位图对象。其中最基本的wx.Bitmap
构造函数是 wx.Bitmap(name, type
=wx.BITMAP_TYPE_ANY)
。参数name
是一个文件名,type
可以是表12.1中的一个。如果bitmap
类能够本地化地处理这个文件格式,那么它就处理,否则这个图像将自动地经由wx.Image
载入并被转换为一个wx.Bitmap
实例。
你可以使用方法wx.EmptyBitmap(width,height,depth
=-1)来创建一个空的位图——参数width
和height
是位图的尺度,depth
是结果图像的颜色深度。有两个函数使你能够根据原始的数据来创建一个位图。函数wx.BitmapFromBits(bits, width, height, depth
=-1)创建一个位图,参数bits
是一个Python
字节列表。这个函数的行为依赖于平台。在大多数平台上,bits
要么是1要么是0,并且这个函数创建一个单色的位图。在Windows
平台上,数据被直接传递给Windows
的API
函数CreateBitmap()
。函数wxBitmapFromXPMData(listOfStrings)
一个Python
字符串列表作为一个参数,以XPM
格式读该字符串。
通过使用wx.Bitmap
的构造函数wx.BitmapFromImage(image, depth
=-1),你可以将一个图像转换为一个位图。参数image
是一个实际wx.Image
对象,depth
是结果位图的颜色深度。如果这个深度没有指定,那么使用当前显示器的颜色深度。你可以使用函数wx.ImageFromBitmap(bitmap)
将位图转回为一个图像,通过传递一个实际的wx.Bitmap
对象。在例12.1中,位图对象的创建使用了位图的构造函数,然后被用于构建wx.StaticBitmap
窗口部件,这使得它们能够像别的wxPython
项目一样被放入一个容器部件中。
我们能够对图像作些什么?
一旦你在wxPython
中使用了图像,你就可以使用许多有用的方法来处理它,并且可以写一些强大的图像处理脚本。
你可以使用GetWidth()
和GetHeight()
方法来查询图像的尺寸。你也可以使用方法GetRed(x, y), GetGreen(x, y),
和GetBlue(x, y)
方法得到任意象素点的颜色值。这些颜色方法的返回值是一个位于0~255之间的整数(用C的术语,它是一个无符号整数,但是这个区别在Python
中没有多大的意义)。同样,你能够使用SetRGB(x, y, red, green, blue)
来设置一个像素点的颜色,其中的x和y是这个像素点的坐标,颜色的取值位于0~255之间。
你可以使用GetData()
方法得到一大块区域中的所有数据。GetData()
方法的返回值是一个大的字符串,其中的每个字符代表一个RGB
元组,并且每个字符都可被认为是一个0~255之间整数值。这些值是有顺序的,第一个是位于像素点(0,0)的红色值,接下来的是位于像素点(0,0)的绿色值,然后是位于像素点(0,0)的蓝色值。再接下来的三个是像素点(0,1)的颜色值,如此等等。这个算法可以使用下面的Python
伪代码来定义:
def GetData(self):
result = ""
for y in range(self.GetHeight()):
for x in range(self.GetWidth()):
result.append(chr(self.GetRed(x,y)))
result.append(chr(self.GetGreen(x,y)))
result.append(chr(self.GetBlue(x,y)))
return result
当使用对应的SetData(data)
方法读取类似格式的RGB
字符串值时,有两件事需要知道。第一,SetData(data)
方法不执行范围或边界检查,以确定你读入的字符串的值是否在正确的范围内或它的长度是否是给定图像的尺寸。如果你的值不正确,那么该行为是未定义的。第二,由于底层是C++代码管理内在,所以将GetData()
的返回值传递给SetData()
是一个坏的方法——你应该构造一个新的字符串。
图像数据字符串可以很容易地与别的Python
类型作相互的转换,这使得很容易以整数的形式来访问和处理它,诸如数组或数字类型。例如,如果你太久的注视一个东西会损伤眼睛一样,试试这样:
import array
img = wx.EmptyImage(100,100)
a = array.array('B', img.GetData())
for i in range(len(a)):
a[i] = (25+i) % 256
img.SetData(a.tostring())
表12.2定义了一些wx.Image
的方法,这些方法执行简单的图像处理。
这些方法只是图像处理的开始部分。在接下来的部分,我们将给你展示两个方法,它们处理透明和半透明图像这一更复杂的主题。
表12.2 wx.Image
的图像处理方法
ConvertToMono(r, g, b)
:返回一个与原尺寸一致的wx.Image
,其中所有颜色值为(r, g, b)
的像素颜色改为白色,其余为黑色。原图像未改变。
Mirror(horizontally
=True)
:返回原图像的一个镜像图像。如果horizontally
参数是True
,那么镜像图像是水平翻转了的,否则是垂直翻转了的。原图像没有改变。
Replace(r1, g1, b1, r2, g2, b2)
:改变调用该方法的图像的所有颜色值为r1, g1, b1
的像素的颜色为r2, g2, b2
。
Rescale(width, height)
:改变图像的尺寸为新的宽度和高度。原图像也作了改变,并且颜色按比例地调整到新的尺寸。
Rotate(angle, rotationCentre, interpolating
=True, offestAfterRotation
=None)
:返回旋转原图像后的一个新的图像。参数angle
是一个浮点数,代表所转的弧度。rotationCentre
是一个wx.Point
,代表旋转的中心。如果interpolating
为True
,那么一个较慢而精确的算法被使用。offsetAfterRotation
是一个坐标点,表明在旋转后图像应该移位多少。任何未被覆盖的空白像素将被设置为黑色,或如果该图像有一个遮罩色,设置为遮罩色(mask color
)。
Rotate90(clockwise
=True)
:按照参数clockwise
的布尔值,控制图像按顺或逆时针方向作90度的旋转。
Scale(width, height)
:返回一个原图像的拷贝,并按比例改变为新的宽度和高度。
设置图像的遮罩以指定一个透明的图像
图像遮罩是图像中的一个特殊的颜色集,当图像显示在其它显示部分之上时,它扮演透明度的角色。你可以使用SetMaskColor(red, green, blue)
方法来设置一个图像遮罩,其中的red, green, blue
定义图像遮罩的颜色。如果你想关闭遮罩,可以使用SetMask(False)
,重置使用SetMask(True)
。方法HasMask()
返回与当前遮罩状态相关的一个布尔值。你也可以使用方法SetMaskFromImage(mask, mr, mg, mb)
根据同一尺寸的另一图像设置遮罩——在这种情况下,遮罩被定义为在遮罩wx.Image
中有着颜色mr, mg, mb
的所有像素,而不管在主图像中那些像素是什么颜色。这使得你在创建一个遮罩中有了很大的灵活性,因为你不必再担心在你原图像中的像素的颜色。你可以使用GetMaskRed()
,GetMaskGreen(),
和GetMaskBlue()
获取遮罩色。如果一个有遮罩的图像被转换为一个wx.Bitmap
,那么遮罩被自动转换为一个wx.Mask
对象并赋给该位图。
设置alpha
值来指定一个透明的图像
alpha
值是指定一个透明或部分透明图像的另一个方法。每个像素都有一个alpha
值,取值位于0(如果图像在该像素是完全透明的)到255(如果图像在该像素点是完全不透明的)之间。你可以使用SetAlphaData(data)
方法来设置alpha
值,它要求类似于SetData()
的字符串字节值,但是每个像素只有一个值。和SetData()
一样,SetAlphaData()
不进行范围检查。你可以使用HasAlpha()
来看是否设置了alpha
值,你也可以使用GetAlphaData()
来得到全部的数据集。你也可以使用SetAlpha(x, y, alpha)
来设定一个特定的像素的alpha
值,并使用GetAlpha(x, y)
来得到该值。
与wx.Image
的图像处理功能相对照,wx.Bitmap
的相对少些。几乎所有wx.Bitmap
的方法都是简单得得到诸如宽度、高度和颜色深度这类的属性。