turtle繪圖入門(mén)

黎明老師
https://segmentfault.com/a/1190000017854607
turtle圖形庫(kù)源于1966年誕生的Logo語(yǔ)言,是入門(mén)Python的有趣工具。因其簡(jiǎn)單便捷的圖形化方法、和立即反饋式的繪畫(huà)效果,成為眾多編程入門(mén)者的首選。相對(duì)于大多數(shù)入門(mén)教材中枯燥的語(yǔ)法學(xué)習(xí),和函數(shù)、方法的演練來(lái)說(shuō),turtle的趣味性顯得別具一格、極富效果。
一直希望能用一篇小短文來(lái)講清楚turtle,但是嘗試組織了幾次之后,感覺(jué)一篇文章門(mén)檻還是有點(diǎn)高,最終決定還是分成兩篇,即大家現(xiàn)在看到的“初級(jí)篇”和“高級(jí)篇”,以利更好的分層次闡述。
一個(gè)類(lèi)庫(kù)是否利于入門(mén),首先了要解它的背景,它能做什么,不能做什么,沒(méi)有十全十美的事物,不要期望過(guò)高。turtle原意是水生的龜類(lèi),可以譯成海龜或?yàn)觚?。是?966年Wally Feurzig和Seymour Papert開(kāi)發(fā)出的一種簡(jiǎn)單易用的編程語(yǔ)言。我們這里的turtle庫(kù),指的是turtle語(yǔ)言用Python的實(shí)現(xiàn),turtle庫(kù)現(xiàn)在已進(jìn)入Python的內(nèi)置模塊,位于Lib/turtle.py,用于繪圖。咱們這里選擇turtle庫(kù),主要是用來(lái)了解和學(xué)習(xí)Python的貼身入門(mén)、語(yǔ)法入門(mén)、面向?qū)ο袢腴T(mén)、認(rèn)識(shí)方向、角度與顏色、簡(jiǎn)單的數(shù)學(xué)運(yùn)算、方法定義、循環(huán)與遞歸等。
在使用turtle庫(kù)之前最重要的事,是需要認(rèn)識(shí)turtle的兩個(gè)坐標(biāo)、角度系統(tǒng):standard和logo。如下圖所示:

這里standard模式,也就是標(biāo)準(zhǔn)坐標(biāo)系,在turtle庫(kù)的使用過(guò)程中,默認(rèn)使用這套坐標(biāo)、角度系統(tǒng),即以繪圖界面的中心點(diǎn)為坐標(biāo)圓點(diǎn)(0,0),以x坐標(biāo)正方向?yàn)?度角,逆時(shí)針旋轉(zhuǎn)。不要小看這簡(jiǎn)單的三句話(huà),這是最基礎(chǔ)的約定前提,請(qǐng)熟記于胸,否則編程的時(shí)候,容易懵的。
除了standard標(biāo)準(zhǔn)坐標(biāo)系外,在turtle庫(kù)的編程中還經(jīng)常使用另外一套坐標(biāo)、角度系統(tǒng),叫做logo!對(duì),就是那個(gè)Logo,那個(gè)原來(lái)的獨(dú)立編程語(yǔ)言,這套logo坐標(biāo)、角度系統(tǒng)就是用來(lái)跟原logo兼容的,對(duì)于了解過(guò)Logo語(yǔ)言的用戶(hù)來(lái)說(shuō),會(huì)更加熟悉,上圖:

看出差別來(lái)了嗎,logo坐標(biāo)系是以繪圖界面的中心點(diǎn)為坐標(biāo)圓點(diǎn)(0,0),以y坐標(biāo)正方向?yàn)?度角,順時(shí)針旋轉(zhuǎn)。跟standard坐標(biāo)不同的地方在于0度方向的選擇,和旋轉(zhuǎn)方式。在編程中使用 mode() 方法進(jìn)行切換。
# 切換坐標(biāo)、角度系統(tǒng)
# mode('standard')
mode('logo')所以,如果見(jiàn)到這個(gè)方法,您首先得切換自己大腦中的坐標(biāo)、角度系統(tǒng)。:)這是編程的底層邏輯,必需先弄清楚。
接下來(lái),再認(rèn)識(shí)turtle的畫(huà)圖方式:落筆、抬筆、清空、設(shè)置顏色、粗細(xì)等等。
如何讓這只烏龜在界面上畫(huà)一條線(xiàn)呢,就如同咱們用筆寫(xiě)字一樣,需要用經(jīng)過(guò)“落筆”、“劃過(guò)”、“抬筆”、“結(jié)束”的過(guò)程,用turtle庫(kù)來(lái)表達(dá),同樣非常直觀,翻譯成簡(jiǎn)單的英文就可以:
import turtle as t # 導(dǎo)入turtle庫(kù)
t.pendown() # 落筆
t.forward(300) # 劃過(guò)300個(gè)單位的長(zhǎng)度
t.penup() # 抬筆
t.done() # 結(jié)束在IDE中鍵入這段代碼,執(zhí)行,就能看到在窗口上畫(huà)出了這樣一條直線(xiàn)?!?”號(hào)后面的是注釋。(IDE的選擇及環(huán)境安裝見(jiàn)《第一節(jié)》)
用turtle畫(huà)出了Python的第一條直線(xiàn)之后,接著來(lái)看看怎么“轉(zhuǎn)彎”。下面以繪制一個(gè)正方形為例子,演示turtle怎么前進(jìn)、怎么左轉(zhuǎn)、右轉(zhuǎn)。
import turtle as t # 導(dǎo)入turtle庫(kù)
t.pendown() # 落筆
t.forward(100) # 劃過(guò)100個(gè)單位的長(zhǎng)度
t.left(90) # 左轉(zhuǎn)90度
t.forward(100)
t.left(90)
t.forward(100)
t.left(90)
t.forward(100)
t.left(90)
t.penup() # 抬筆
t.done() # 結(jié)束在IDE中鍵入這段代碼,執(zhí)行,就能看到在窗口上畫(huà)出了一個(gè)標(biāo)準(zhǔn)的正方形。
有一點(diǎn)編程基礎(chǔ)的小伙伴,立即就可以看出來(lái),上面代碼中有四組相同的語(yǔ)句,嗯,這不是可以用循環(huán)來(lái)實(shí)現(xiàn)嗎?沒(méi)錯(cuò),改進(jìn)一下:
import turtle as t
t.pendown()
for j in range(4): # 重復(fù)執(zhí)行4次
t.forward(100)
t.left(90)
t.penup()
t.done()放到IDE中執(zhí)行,可以看到效果跟順序執(zhí)行的完全一致。range()是Python的內(nèi)置函數(shù),這里表示循環(huán)執(zhí)行的次數(shù)。
注意縮進(jìn),Python中不使用括號(hào)來(lái)表示語(yǔ)句塊,而靠冒號(hào):和后面的縮進(jìn)(空格縮進(jìn))來(lái)表示語(yǔ)句塊。因此縮進(jìn)成為了語(yǔ)法的一部分,要嚴(yán)格遵循,從其它語(yǔ)言轉(zhuǎn)過(guò)來(lái)的小伙伴需要習(xí)慣一下。
學(xué)習(xí)任何一項(xiàng)新知識(shí),就像爬坡一樣,有個(gè)量變積累到質(zhì)變的過(guò)程。接下來(lái)就來(lái)寫(xiě)一個(gè)烏龜爬坡的小程序吧,上代碼:
import turtle as t
t.pendown()
t.forward(50)
t.left(90) # 左轉(zhuǎn)90度
t.forward(50)
t.right(90) # 右轉(zhuǎn)90度
t.forward(50)
t.left(90)
t.forward(50)
t.right(90) # 右轉(zhuǎn)90度
t.forward(50)
t.left(90)
t.forward(50)
t.right(90) # 右轉(zhuǎn)90度
t.penup()
t.done()放到IDE中執(zhí)行,可以看到一段3次爬坡的動(dòng)畫(huà)。left(90)是向左轉(zhuǎn)動(dòng)90度;right(90)是向右轉(zhuǎn)動(dòng)90度。當(dāng)然這3次爬坡,也是可以用順序來(lái)做的,代碼如下:
import turtle as t
t.pendown()
for j in range(3): # 重復(fù)執(zhí)行3次
t.forward(50)
t.left(90) # 左轉(zhuǎn)90度
t.forward(50)
t.right(90) # 右轉(zhuǎn)90度
t.penup()
t.done()for循環(huán)的執(zhí)行效果,跟順序執(zhí)行的完全一樣,更改range(3)的參數(shù),可以執(zhí)行不同的次數(shù)。
彩蛋來(lái)了:turtle既然是烏龜,我怎么畫(huà)到現(xiàn)在沒(méi)有看到一只烏龜呢?的確有點(diǎn)奇怪,明明是一個(gè)小箭頭一直在畫(huà)畫(huà),為什么要說(shuō)成是turtle烏龜呢?
不要著急,加一行小代碼,烏龜立即出現(xiàn):
t.shape('turtle')把這行代碼,加到“落筆”的前面,就會(huì)發(fā)現(xiàn)小箭頭變成小烏龜了,還是挺萌吧!上面畫(huà)直線(xiàn)、畫(huà)正方形、爬坡三個(gè)例子你都可以加上試試試試!
上面的left(90)和right(90)分別表示左轉(zhuǎn)90度和右轉(zhuǎn)90度,那怎么設(shè)置烏龜?shù)慕嵌?,讓它向我們指定的任意方向前進(jìn)呢?嗯,調(diào)整90這個(gè)參數(shù),在界面上畫(huà)一個(gè)自己的名字吧!哦,中文就算了,畫(huà)出來(lái)太復(fù)雜,畫(huà)個(gè)拼音首字母還是可以的,上代碼:
import turtle as t
t.mode('logo') # 切換成Logo坐標(biāo)、角度系統(tǒng)
t.shape('turtle') # 顯示出小烏龜
t.penup() # 抬筆,先不要畫(huà)
t.goto(-200, 100) # 移動(dòng)到起始點(diǎn)
t.setheading(180) # 設(shè)置要畫(huà)的方面,Logo坐標(biāo)的180度方向,即向下
t.pendown() # 落筆,準(zhǔn)備開(kāi)始畫(huà)第一個(gè)字母
t.forward(200)
t.left(90)
t.forward(100)
t.penup() # 第一個(gè)字母"L"畫(huà)完,抬筆
t.forward(100) # 到達(dá)第二個(gè)字母的起始位置
t.pendown() # 落筆,準(zhǔn)備開(kāi)始畫(huà)第二個(gè)字母
t.left(90)
t.forward(200)
t.right(135)
t.forward(100)
t.left(90)
t.forward(100)
t.right(135)
t.forward(200)
t.penup() # 第二個(gè)字母"M"畫(huà)完,抬筆
t.done()在IDE中運(yùn)行這段代碼,可以看到小烏龜在窗口左側(cè)畫(huà)了兩個(gè)大寫(xiě)的字母“L”和“M”。需要注意的地方是,程序中使用mode('logo')切換到了Logo的坐標(biāo)、角度系統(tǒng),setheading(180)將小烏龜?shù)那斑M(jìn)方向設(shè)置為L(zhǎng)ogo坐標(biāo)的180度方向,即向下的方向,如果有疑問(wèn),可以參考本文開(kāi)篇時(shí)對(duì)坐標(biāo)系的描述。
goto(x, y)是一個(gè)新方法,可以讓小烏龜去到界面上的任意一個(gè)坐標(biāo)點(diǎn)。(當(dāng)然,畫(huà)名字的這個(gè)小例子,如果全部使用goto(x,y)會(huì)比使用forward()要簡(jiǎn)單很多,具體代碼你可以自己嘗試)
配合for循環(huán)、forward()和角度度化,可以讓turtle在變與不變中創(chuàng)造出很多有意思的圖形。比如把上面畫(huà)正方形的例子稍做變化:
import turtle as t
t.pendown()
for j in range(10): # 重復(fù)執(zhí)行10次
# 畫(huà)正方形-開(kāi)始
t.forward(100)
t.left(90)
t.forward(100)
t.left(90)
t.forward(100)
t.left(90)
t.forward(100)
t.left(90) # 畫(huà)正方形-結(jié)束
# 右轉(zhuǎn)36度
t.right(36)
t.penup()
t.done()執(zhí)行后,可以看到,畫(huà)出一個(gè)非常規(guī)整、漂亮的組合圖案。
填充,可以讓turtle的圖形更炫麗。主要有以下幾個(gè)方法:
t.pencolor('red') #設(shè)置畫(huà)筆的顏色
t.fillcolor('green') #設(shè)置填充的顏色
t.begin_fill() #開(kāi)始填充
t.end_fill() #結(jié)束填充下面來(lái)改造一下我們前面畫(huà)正方形的例子,
import turtle as t
t.pencolor('red') #設(shè)置畫(huà)筆的顏色
t.fillcolor('green') #設(shè)置填充的顏色
t.pendown()
t.begin_fill() #開(kāi)始填充
for j in range(4):
t.forward(100)
t.left(90)
t.end_fill() #結(jié)束填充
t.penup()
t.done()執(zhí)行一下,可以看到turtle畫(huà)出一個(gè)紅色邊框的實(shí)心綠正方形。
在Python中使用def進(jìn)行方法定義或者叫函數(shù)定義,可以讓指揮turtle畫(huà)畫(huà)這件事,更輕松一點(diǎn)。再次改造一下上面畫(huà)正方形的例子:
import turtle as t
def drawRect(): # 定義一個(gè)函數(shù),名為 drawRect()
t.pendown()
for j in range(4):
t.forward(100)
t.left(90)
t.penup()
drawRect() # 調(diào)用這個(gè)函數(shù)
t.done()在這個(gè)例子中,我們使用def定義了一個(gè)函數(shù),名為drawRect(),內(nèi)容就是帶一個(gè)正方形,跟前面的描述一樣。
自定義好函數(shù)以后,只要調(diào)用這個(gè)函數(shù)名,就可以執(zhí)行函數(shù)體內(nèi)的代碼內(nèi)容。我們?cè)賮?lái)豐富一下這個(gè)例子,對(duì)上面畫(huà)多個(gè)正方形的例子進(jìn)行優(yōu)化:
import turtle as t
def drawRect(): # 定義函數(shù)
t.pendown()
for j in range(4):
t.forward(100)
t.left(90)
t.penup()
for j in range(10): # 重復(fù)執(zhí)行10次
drawRect() # 調(diào)用這個(gè)自定義的函數(shù)
t.right(36)
t.done()執(zhí)行一下,可見(jiàn)效果一樣,但是代碼更為簡(jiǎn)潔,可讀性更好。
下面給這段代碼加上填充效果:
import turtle as t
def drawRect():
t.pendown()
for j in range(4):
t.forward(100)
t.left(90)
t.penup()
clrs = ['red', 'green', 'blue', 'gray', 'brown'] # 顏色列表
for j in range(10):
t.fillcolor(clrs[j % 5]) # 從列表中選擇顏色
t.begin_fill() # 開(kāi)始填充
drawRect()
t.end_fill() # 結(jié)束填充
t.right(36)
t.done()運(yùn)行一下這個(gè)小例子,可以看到,turtle畫(huà)出了用五種不同顏色填充的正方形組合圖案。例子中為了使得每個(gè)正方形填充的顏色不一樣,我們先定義了一個(gè)顏色列表clrs,列表clrs中預(yù)先存入了5個(gè)顏色。在for循環(huán)中,每次執(zhí)行時(shí)根據(jù)j值的變化,用取余數(shù)%的方式,每次取到不重復(fù)的顏色。
當(dāng)然,turtle的功能不只于此,對(duì)于Python的入門(mén)者來(lái)說(shuō),通過(guò)turtle生動(dòng)的例子,可以畫(huà)漂亮的圖形,也可以同時(shí)熟悉Python的語(yǔ)法和規(guī)則,何樂(lè)而不為呢?
封面圖代碼Github地址:https://github.com/ddxygq/PyCode/blob/master/%E5%9F%BA%E7%A1%80%E5%8C%85/turtle/tree.py
