# 第十二章 操作基本圖像
1. [處理基本的圖像](#A.2BWQR0Blf6Zyx2hFb.2BUM8-)
1. [使用圖像工作](#A.2BT391KFb.2BUM9d5U9c-)
1. [如何載入圖像?](#A.2BWYJPVY99UWVW.2FlDP.2Fx8-)
2. [我們能夠對圖像作些什么?](#A.2BYhFO7ID9WR9b.2BVb.2BUM9PXE6bTsBOSP8f-)
**本章內容:**
* 裝載圖像和創建圖像對象
* 創建設備上下文
* 繪制到設備上下文
* 繪制文本到設備上下文
* 管理畫刷、畫筆和設備坐標
任何用戶界面工具的最基本的行為就是在屏幕上繪制。在最基本的層面上來說,定義在`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`的方法都是簡單得得到諸如寬度、高度和顏色深度這類的屬性。
- 活學活用wxPython
- 前言
- 致謝
- 關于本書
- 第一部分
- 第一章 歡迎使用wxPython
- 第二章 給wxPython程序一個堅實的基礎
- 第三章 在事件驅動環境中開發
- 第四章 用PyCrust使得wxPython更易處理
- 第五章 繪制藍圖
- 第六章 使用wxPython基本構件
- 第二部分 基礎wxPython
- 第七章 使用基礎控件
- 第八章 將構件放入窗體中
- 第九章 通過對話框讓用戶選擇
- 第十章 創建和使用wxPython菜單
- 第十一章 使用sizer放置構件
- 第十二章 操作基本圖像
- 第三部分 高級wxPython
- 第十三章 建造列表控件并管理列表項
- 第十四章 網格控件
- 第十五章 樹形控件
- 第十六章 在應用程序中加入HTML
- 第十七章 wxPython的打印構架
- 第十八章 使用wxPython的其他功能