讀古今文學網 > 父與子的編程之旅:與小卡特一起學Python > 16.3 在窗口中畫圖 >

16.3 在窗口中畫圖

現在我們有了一個 Pygame 窗口,在我們優雅地將它關閉之前它會一直打開。代碼清單 16-2 的第 3 行中的 [640, 480] 是窗口的大小,表示 640 像素寬、480 像素高。下面就在這裡面畫一些圖形。按照代碼清單 16-4 修改你的程序。

代碼清單 16-3 畫一個圓

什麼是翻轉

對於 Pygame 窗口中顯示的所有內容,Pygame 中的顯示對像(我們的顯示對像名為 screen,這在代碼清單 16-3 的第 3 行創建)都會有這些內容的兩個副本。這樣做的原因是,開始動畫時,我們希望讓動畫盡可能流暢,速度盡可能快。所以不必在每次對圖形做一個小小的修改時都更新顯示,可以做很多修改後再「翻轉」(flip)到圖形的新版本。這樣就會一次顯示所有這些修改,而不是一個接一個地出現。這樣一來,我們就不會顯示出只畫了一半的圓(或外星人,或者其他任何東西)。

可以把這兩個副本當作一個「當前屏」和一個「下一屏」。當前屏就是我們現在看到的,下一屏是完成「翻轉」之後看到的。做完「下一屏」上的所有修改後,再翻轉到下一屏,就能看到這些改變。

如何建立一個圓

運行代碼清單 16-3 中的程序時,應該能看到如下圖這樣,靠近窗口左上角有一個紅色的圓。

毫不奇怪,pygame.draw.circle 函數會畫一個圓。必須告訴它以下 5 件事。

  • 在哪個表面(surface)畫這個圓。(在這裡,要在第 3 行定義的表面上畫圓,名為 screen,這就是顯示表面。)

  • 用什麼顏色來畫。(在這裡要用紅色,對應的值為 [255, 0, 0]。)

  • 在什麼位置畫。(在這裡要位於 [100, 100],這表示從左上角向下 100 像素再向右 100 像素的位置)。

  • 圓的大小。(這裡是 30,這是圓的半徑,也就是圓心到外圍邊界的距離,單位是像素。)

  • 線寬。(如果 width = 0,圓是完全填充的,這裡就採用了完全填充。)

下面再來更詳細地學習這 5 個方面。

術語箱

像素(pixel)這個詞是「圖像元素」(picture element)的簡寫。這表示屏幕上或圖像中的一點。如果在一個圖像瀏覽器中查看圖片,充分放大(讓圖像非常大),就可以看到單個的像素。下面是一張照片的正常視圖和放大視圖,在放大視圖中可以看到像素。

哇,你眼力真好!這些小線條實際上就是像素行。一般的計算機屏幕可能有 768 行像素,每行有 1024 個像素。我們就會說這個屏幕「分辨率是 1024×768」。有些屏幕的像素更多,有些可能比這要少。

Pygame 表面

在實際生活中如果我讓你畫一幅畫,你可能會先問「我在哪兒畫?」在 Pygame 中,我們要在一個表面上畫圖。顯示表面就是我們在屏幕上看到的表面,也就是代碼清單 16-3 中的 screen。不過 Pygame 程序可以有多個表面,可以把圖像從一個表面複製到另一個表面。還可以對表面做一些處理,比如旋轉表面或者調整它們的大小(讓它們更大或更小)。

前面提到過,顯示表面有兩個副本。按軟件術語來講,我們說顯示表面是雙緩衝的(double-buffered)。正是因為這個原因,我們不會在屏幕上看到只畫了一半的形狀和圖像。我們會在緩衝區裡畫圓、外星人或者任何東西,然後「翻轉」顯示表面,來顯示已經完全繪製的圖像。

Pygame 中的顏色

Pygame 中使用的顏色系統是很多計算機語言和程序中通用的系統,稱為 RGB。這裡的 R、G 和 B 分別代表紅、綠和藍。

你可能在自然課上已經學過,通過結合或混合光的三原色(紅、綠和藍)可以得到任何顏色。計算機上也採用了同樣的做法。每個顏色(紅、綠和藍)對應一個從 0 到 255 的數。由一個包含 3 個整數的列表來給出顏色,每個數的範圍在 0 到 255 之間。如果所有數都是 0,就沒有任何顏色,這就是全黑,所以會得到黑色。如果三個數都是 255,會將 3 種顏色以最大亮度混合在一起,這就是白色。如果顏色是 [255, 0, 0],這就是純紅色,沒有綠色和藍色。純綠就是 [0, 255, 0],純藍是 [0, 0, 255]。如果所有 3 個數都一樣,比如 [150, 150,150],你會得到某種灰度。數字越小,灰度就越深,數字越大,灰度就越淺。

顏色名

Pygame 提供了一個命名顏色列表,如果你不想使用 [R, G, B] 記法,就可以使用這些命名顏色。定義好的顏色名有 600 多個。我不想在這裡全部列出,不過如果你想看看到底有哪些顏色,可以在你的硬盤上搜索一個名為 colordict.py 的文件,然後在文本編輯器中打開這個文件。

如果你想使用這些顏色名,必須在程序最前面增加下面這行代碼:

from pygame.color import THECOLORS  

然後,使用某個命名顏色時,可以這樣做(我們的畫圓例子中就是這樣做的):

pygame.draw.circle(screen, THECOLORS[\"red\"],[100,100], 30, 0)  

 

到底怎麼回事?

為什麼是 255 呢?每個三原色(紅、綠、藍)都有 0 到 255 共 256 個不同的值。256 這個數字有什麼特別的呢?為什麼不是 200、300 或者 500 呢?

8 位總共正好能表示 256 個不同的值,也就是由 1 和 0 構成的 八位數的所有可能的組合。8 位也稱為一個字節,字節是有自己的地址的最小內存塊。計算機就是利用地址來查找某段內存的。

這就像在街道上一樣。你的房子或公寓有一個地址,不過你的房間沒有地址。房子就是街道上最小的「可尋址單位」。字節則是計算機內存中最小的「可尋址單位」。

也可以用 8 位以上來表示每種顏色,不過,8 位之後可用的位數可能就到 16 位(2 個字節)了,因為不完整的字節使用起來會不太方便。事實證明,根據人眼識別顏色的方式,用 8 位來表示實際可見的顏色完全足夠了。

由於有 3 個值(紅、綠、藍),每個值有 8 位,總共就是 24 位,所以這種表示顏色的方法也稱為「24 位顏色表示法」。對每個像素使用 24 位,每個三原色分別使用 8 位。

如果你想試驗一下,看看紅色、綠色和藍色如何結合來生成不同的顏色,可以試試 colormixer.py 程序,運行本書的安裝程序時會把這個程序放在 examples 文件夾下。利用這個程序,你可以嘗試紅、綠、藍的任意組合,看看能得到什麼顏色。

位置——屏幕坐標

如果想在屏幕上畫個東西或者放上一個東西,需要指定這個東西應當放在屏幕上的哪個位置。這裡有兩個數:一個對應 x 軸(水平方向),還有一個對應 y 軸(垂直方向)。在 Pygame 中,這兩個數從窗口左上角的 [0, 0] 坐標開始。

看到類似 [320, 240] 的一對數時,要知道第一個數表示水平方向,也就是相對於左邊界的距離。第二個數表示垂直方向,也就是相對於頂邊的距離。在數學和編程中,字母 x 通常用來表示水平距離,y 常用來表示垂直距離。

我們建立了一個 640 像素寬、480 像素高的窗口。如果希望在窗口正中間畫圓,需要在 [320, 240] 上繪製。這個位置離左邊界 320 像素,離上邊界 240 像素。

下面嘗試在窗口中間畫圓。運行代碼清單 16-4 中的程序。

代碼清單 16-4 把圓放在窗口中間

這裡使用坐標 [320, 240] 作為圓心。把運行代碼清單 16-3 的結果與運行代碼清單 16-4 時看到的結果做個比較,看看有什麼差別。

形狀大小

使用 Pygame 的 draw 函數畫形狀時,必須指定形狀的尺寸。對於圓來說,只有一個尺寸,也就是半徑。而像矩形之類的形狀,則必須指定長和寬。

Pygame 有一種特殊的對象,名為 rect(這是「rectangle」(矩形)的簡寫),用來定義矩形區域。rect 要使用左上角坐標、寬和高來定義:

Rect(left, top, width, height)  

這裡同時定義了位置和大小。下面是一個例子:

my_rect = Rect(250, 150, 300, 200)  

這會創建一個矩形,它的左上角距離窗口左邊界 250 像素,距離窗口上邊界 150 像素,寬為 300 像素,高為 200 像素。下面來試試看。

用下面這行代碼替換代碼清單 16-4 中的第 5 行,看看結果是什麼:

矩形的位置和大小可以是一個簡單的數字列表(或元組),也可以是一個 Pygame 的 Rect 對象。所以還可以把前面一行替換為下面這兩行代碼:

my_list = [250, 150, 300, 200]pygame.draw.rect(screen, [255,0,0], my_list, 0)  

或者

my_rect = pygame.Rect(250, 150, 300, 200)pygame.draw.rect(screen, [255,0,0], my_rect, 0) 

這就是最後得到的矩形。我增加了一些尺寸標注來說明每個數字分別表示什麼含義。

注意這裡只向 pygame.draw.rect 傳遞了 4 個參數,因為 rect 用一個參數就表示了位置和大小。在 pygame.draw.circle 中,位置和大小分別由兩個不同的參數表示,所以需要傳遞 5 個參數。

像(Pygame)程序員一樣思考

如果用 Rect(left, top, width, height) 創建一個矩形,還可以使用其他一些屬性來移動和對齊這個 Rect:

  • 4 條邊:topleftbottomright

  • 4 個角 : topleftbottomlefttoprightbottomright

  • 每條邊的中點:midtopmidleftmidbottommidright

  • 中心:centercenterxcentery

  • 尺寸:sizewidthheight

這些屬性只是為了提供方便。所以,如果你想移動一個矩形,讓它的中心位於某個點,不必得出上坐標和左坐標分別是什麼;可以直接訪問中心位置。

線寬

畫形狀時最後需要指定的一點是線的粗細。在之前的例子中,我們使用的線寬都是 0,這會填充整個形狀。如果使用不同的線寬,會看到形狀的輪廓。

試著把線寬改為 2:

試試看有什麼結果。再試試其他線寬。

現代藝術

想不想讓計算機生成某種現代藝術?玩玩唄,試試代碼清單 16-5。也可以在代碼清單 16-4 的基礎上做些修改,或者乾脆從頭開始鍵入。

代碼清單 16-5 使用 draw.rect 實現藝術創作

import pygame, sys, randompygame.initscreen = pygame.display.set_mode([640,480])screen.fill([255, 255, 255])for i in range (100):    width = random.randint(0, 250)    height = random.randint(0, 100)    top = random.randint(0, 400)    left = random.randint(0, 500)    pygame.draw.rect(screen, [0,0,0], [left, top, width, height], 1)pygame.display.fliprunning = Truewhile running:    for event in pygame.event.get:if event.type == pygame.QUIT:    running = Falsepygame.quit  

運行這個程序,看看會得到什麼。應該能得到如下圖所示的結果:

你明白這個程序是怎麼工作的嗎?它會隨機畫 100 個大小不等、位置不同的矩形。為了讓它更有「藝術性」,再增加一些顏色,另外將線寬也設為隨機,如代碼清單 16-7 所示。

代碼清單 16-6 帶顏色的現代藝術

運行這個程序時,每次你都會看到不同的東西。如果有看著不錯的,可以給它起個富有想像力的名字,比如「機器之聲」,看看能不能把它賣到你們當地的美術館 !