第 5 章 小烏龜繪圖模組
∗ Turtle 繪圖模組
▸ 小烏龜繪圖 (Turtle graphics) 是 Logo 程式語言中的一個向量繪圖技術,開發於 1960 年代, 非常適合初學者學習,目前大多數的程式語言都有此套件
▸ 小烏龜繪圖的機制:想像有一隻小烏龜,可以依使用者的指令到處遊走,牠的尾巴就像畫筆一般,如果放下就會將行走軌跡畫下來, 尾巴如果提起,就不會畫軌跡
▸ Turtle 模組利用 python3-tk 套件來繪圖,安裝:
✶ Ubuntu:sudo apt install python3-tk
✶ Windows 與 Mac:預設已安裝
(1) Turtle 程式範例
∗ 繪圖座標系統:
∗ 小烏龜畫圖形
myTurtle.py (首先建立 python/ch5 目錄)
import turtle # Create screen and turtle objects screen = turtle.Screen() screen.setup(500, 400) myTurtle = turtle.Turtle() # Move the turtle myTurtle.forward(150) myTurtle.left(90) myTurtle.forward(75) # Exit screen.exitonclick()
▸ 第 1 行:匯入 turtle 模組,本範例使用 turtle 模組裡的 Screen() 及 Turtle() 兩個方法
▸ 第 4 行:利用 turtle 模組裡的 Screen() 方法產生一個螢幕物件, 並將其指派給變數 screen
✶ Python 模組方法的使用方式是利用 <module>.<method>() 的點號語法, 例如本範例中的 turtle.Screen()
▸ 第 5 行:利用 screen 物件的 setup() 方法來設定螢幕的寬度與高度 (本例中,寬高分別是 500 與 400 像素)
▸ 第 6 行:利用 turtle 模組裡的 Turtle() 方法產生小烏龜物件, 並指派給 myTurtle 變數
▸ 第 9 行:利用 myTurtle 物件的 forward() 方法讓小烏龜向前走 150 像素距離
✶ 註:小烏龜物件產生時,預設方向為面向東方
▸ 第 10 行:利用 myTurtle 物件的 left() 方法讓小烏龜左轉 90 度
▸ 第 11 行:向前走 75 像素距離
▸ 第 14 行:利用 screen 物件的 exitonclick() 方法在使用者點擊螢幕時結束程式
▸ 注意:不可將檔案命名為 turtle.py, 否則會和內建的 turtle 模組名稱衝突
∗ 其他 screen 與 turtle 物件方法
▸ 設定螢幕背景顏色
screen.bgcolor('lightBlue') # 淡藍色
screen.bgcolor('#add8e6')
▸ 設定畫筆顏色
myTurtle.color('blue') # 藍色
▸ 設定畫一個封閉幾何圖形的外框及底色,例如藍框紅底、半徑為 30 的圓:
myTurtle.color('blue', 'red') myTurtle.begin_fill() myTurtle.circle(30) myTurtle.end_fill()
▸ 設定畫筆尺寸
myTurtle.pensize(3) # 3 像素寬
▸ 提筆與落筆
myTurtle.penup() # 提筆
myTurtle.pendown() # 落筆
▸ 走到某個位置
myTurtle.goto([120, 150]) # 走到 (120, 150) 位置,位置資料為串列
myTurtle.goto(120, 150) # 位置參數為兩筆資料
∗ Tk 顏色名稱與數值
▸ http://www.tcl.tk/man/tcl8.4/TkCmd/colors.htm
▸ 練習 1
(2) For 迴圈
∗ 利用小烏龜繪製一個方框
▸ 規劃行走模式:向前走 → 左轉 → 向前走 → 左轉 → 向前走 → 左轉 → 向前走,程式如下:
drawSquare.py
import turtle # Create screen and turtle objects screen = turtle.Screen() screen.setup(500, 500) myTurtle = turtle.Turtle() # Move the turtle: draw a square myTurtle.forward(100) myTurtle.left(90) myTurtle.forward(100) myTurtle.left(90) myTurtle.forward(100) myTurtle.left(90) myTurtle.forward(100) # Exit screen.exitonclick()
▸ 以上程式其實頗為繁瑣,繪製四邊形需要 7 個指令,如果要繪製八邊形就要 15 個指令, 如此程式不僅變得更長,看起來更是雜亂,試想,如果要繪製 100 邊形,光複製近 200 行程式指令就手軟了
▸ 解決方案:重複的程式只要寫一次,然後自動重複執行許多次
✶ 此種功能稱為「迴圈」(Iteration, Loop),Python 語言有 for 與 while 兩種迴圈指令,都屬於複合指令 (Compound statement)
∗ 複合指令
▸ Python 有一些指令可以包含其他指令,稱為複合指令 (Compound statement),例如 for, while, if, else, elif, def, class 等
▸ 以 for 複合指令為例,其功能是重複執行一群指令,語法如下:
for <variable> in <sequence>: <body>
✶ <variable>:迴圈變數 (Loop variable),在每次迴圈執行時會循序取用一個值
✶ in:在 ... 之中
✶ <sequence>:一個序列,內含許多值,例如 Python 的串列 [1, 2, 3, 4],以中括號包住 4 個整數值
✶ <body>:複合指令的本體,是每次迴圈要執行的一群指令
▸ 複合指令的結尾要有冒號
▸ 複合指令的本體需縮排一層,每層縮排為 4 個空格 (不要使用定位鍵來縮排)
▸ Python 的縮排非常重要,縮排錯誤可能會造成程式執行錯誤,或執行結果錯誤。 縮排強迫程式設計師必須將程式結構化,以提升閱讀性 (Readability),此與其他語言非常不同
✶ 例如以下兩段程式結果是不同的:
for num in [1, 2, 3, 4]:
print(num)
print('End')
for num in [1, 2, 3, 4]:
print(num)
print('End')
# 第一段程式的 print('End') 指令不屬於 for 指令的本體,因此僅會執行一次; 第二段程式的 print('End') 指令屬於本體,因此會執行 4 次
▸ 不受縮排限制來對齊指令:某些指令較長,需要額外對齊較整潔,可以利用括號包住,就可以不受縮排的限制,例如
overtimeMorning = 10 overtimeAfternoon = 20 overtimeEvening = 30 overtimeNight = 40 overtime = (overtimeMorning + overtimeAfternoon + overtimeEvening + overtimeNight)
∗ 迴圈 (Iteration)
▸ 電腦程式的一項重要功能就是能夠重複執行某些指令,這樣的重複功能稱為迴圈
▸ 例如:我們想邀請一些朋友來參加我們的派對,打算寄給每一個人一封電子信件,每封信內容相同,但抬頭不同, 可利用以下迴圈程式印出個別訊息:
party.py
for name in ['張三', '李四', '王五', '趙六']: print(f'Hi {name},歡迎參加我們在星期六所舉辦的派對')
✶ 第 1 行 for name in ['張三', '李四', '王五', '趙六']:
# ['張三', '李四', '王五', '趙六'] 是Python 的串列, for 迴圈每次循序取出串列裡的一個項目,並且指派給迴圈變數 name:
# 第 1 個迴圈 name 的值是 張三
# 第 2 個迴圈 name 的值是 李四
# 第 3 個迴圈 name 的值是 王五
# 第 4 個迴圈 name 的值是 趙六
✶ 第 2 行印出訊息:此指令是 for 複合指令的本體,因此縮排 4 個空格
✶ 每次迴圈執行完畢,程式都會檢查是否還有資料要取用,如果有,就繼續迴圈,如果沒有 (這是迴圈結束條件),就停止迴圈
✶ 執行結果:
∗ For 迴圈的執行流程
▸ 電腦程式在執行時,會決定下一個要執行指令是那一個指令,這樣的控制稱為控制流程 (Control flow) 或者執行流程 (Execution flow)
▸ 程式由上往下循序執行,這種型態稱為循序控制流程 (Sequential control flow)
▸ For 指令是重複控制流程 (Repetitive control flow),如下圖所示:
✶ 首先判斷是否序列裡的所有項目都已輪過
✶ 若是,結束迴圈
✶ 若否,將序列裡下一個項目指派給迴圈變數
✶ 執行迴圈本體程式
∗ 利用 for 迴圈簡化小烏龜繪製方框程式
▸ 利用小烏龜畫一個方框,相同的事要重複 4 次:「向前走,轉 90 度」
▸ 先前畫方框的程式共有 8 行,現在改為 for 迴圈,程式僅有 3 行:
drawSquare2.py (Modify drawSquare.py)
...
# Move the turtle: draw a square
for i in [1, 2, 3, 4]:
myTurtle.forward(100)
myTurtle.left(90)
...
✶ for i in [1, 2, 3, 4]:建立一個循序使用串列裡的元素的迴圈, 迴圈每次會取用一個項目,因此會執行 4 次:
圈數 | 變數 i 的值 | 執行程式 |
---|---|---|
1 | 1 | myTurtle.forward(100) myTurtle.left(90) |
2 | 2 | myTurtle.forward(100) myTurtle.left(90) |
3 | 3 | myTurtle.forward(100) myTurtle.left(90) |
4 | 4 | myTurtle.forward(100) myTurtle.left(90) |
▸ 由於迴圈變數在本體中並未用到,只是用來當計數器,因此序列的內容也就不重要,只要串列裡成員的數量為 4 即可, 因此下列兩種迴圈都有相同效果:
for i in ['a', 'b', 'c', 'd']: # 循序取串列裡的元素 myTurtle.forward(100) myTurtle.left(90) for i in 'abcd': # 循序取字串裡的字元 myTurtle.forward(100) myTurtle.left(90)
∗ 加點顏色
▸ 其實少寫幾行程式並非迴圈的主要貢獻,迴圈讓我們學到程式的一個重要功能:重複執行相同的指令
▸ 迴圈在程式中是很常見的結構,可用此結構解決許多問題
▸ 試試看,每次迴圈給不同的畫筆顏色:
colorSquare.py
import turtle # Create screen and turtle objects screen = turtle.Screen() screen.setup(500, 500) myTurtle = turtle.Turtle() # Move the turtle: draw a square for color in ['red', 'blue', 'green', 'orange']: myTurtle.color(color) myTurtle.forward(100) myTurtle.left(90) # Exit screen.exitonclick()
▸ 練習 2
(3) Range 函式
∗ 產生數字序列
▸ 先前的 for 迴圈範例使用 [1, 2, 3, 4] 數字串列, 針對此,Python 提供了一個簡單的內建函式 range(),用來產生數字序列 (並非「串列」),語法如下:
✶ range(<stop>):產生 0 ~ <stop>-1 的數字串列 (從 0 開始,總共 <stop> 個數字)
# 例如:range(6) → 0, 1, 2, 3, 4, 5
✶ range(<start>, <stop>):產生 <start>, <start>+1, ..., <stop>-1 的數字串列 (從 <start> 開始, 總共 <stop>-<start> 個數字)
# 例如:range(4, 10) → 4, 5, 6, 7, 8, 9
✶ range(<start>, <stop>, <step>):產生 <start>, <start>+<step>, ... 的數字串列
# 例如:range(4, 10, 2) → 4, 6, 8
▸ 注意:range() 函式所產生的序列可直接提供 for 迴圈使用, 但如果要真的產生串列,則必須使用 list(range(...)) 方式,例如:
numList = range(2, 8) print(numList) # range(2, 8) numList = list(range(2, 8)) print(numList) # [2, 3, 4, 5, 6, 7]
✶ range() 函式回覆的是 range 類別,而非 list 類別,不用 list 類別主要理由就是效能:range 類別在記憶體裡只存三筆資料: start, stop, step, 而不是儲存整個串列,如此,不僅不浪費記憶體,速度也快多了
∗ 小烏龜畫方框迴圈程式改用 range() 函式:
drawSquare2.py
...
# Move the turtle: draw a square
for i in range(4):
myTurtle.forward(100)
myTurtle.left(90)
...
(4) 其他 Turtle 方法
∗ forward() 及 left() 方法可以有負值參數
▸ 例如:
myTurtle.forward(-100) # 後退 100 myTurtle.left(-30) # 右轉 30 度
▸ 也有 backward() 及 right() 方法可用
myTurtle.backward(-100) # 前進 100 myTurtle.right(-100) # 左轉 100 度
▸ 因為圓為 360 度,因此 myTurtle.right(100) 等於 myTurtle.left(260)
∗ 小烏龜可以有自己的形狀 (shape)
▸ Turtle 模組提供以下形狀: arrow, blank, circle, classic, square, triangle, turtle
▸ 例如設定為小烏龜形狀: myTurtle.shape('turtle')
∗ 小烏龜行走的速度 (Speed) 可以調整
▸ 速度範圍:1 ~ 10 (最慢 ~ 最快),0 則是最快 (沒有動畫)
▸ 設定速度:myTurtle.speed(8)
∗ 小烏龜可以蓋章:
myTurtle.stamp()
∗ 範例:畫螺旋形及星形
spiral.py
import turtle # 匯入小烏龜模組 screen = turtle.Screen() # 產生一個螢幕 screen.setup(500, 400) # 設定螢幕寬高 screen.bgcolor('lightgreen') # 設定螢幕背景 myTurtle = turtle.Turtle() # 產生一個小烏龜物件 myTurtle.color('blue') # 設定小烏龜顏色 myTurtle.shape('turtle') # 設定小烏龜形狀 myTurtle.penup() # 提筆 for step in range(5, 60, 2): # 迴圈:從 5 開始,每次跳 2 步,直到 60 myTurtle.stamp() # 蓋章 myTurtle.forward(step) # 向前 step 步 myTurtle.right(24) # 右轉 24 度 screen.exitonclick() # 使用者點擊螢幕時結束程式
star.py
import turtle screen = turtle.Screen() screen.setup(400, 300) myTurtle = turtle.Turtle() myTurtle.color('red', 'yellow') # 多邊形外框紅色,內部區域黃色 myTurtle.speed(10) # 設定速度 myTurtle.backward(100) # 後退 100 (讓圖形置中) myTurtle.begin_fill() # 開始填色 for i in range(36): myTurtle.forward(200) myTurtle.left(170) myTurtle.end_fill() # 結束填色 screen.exitonclick()
∗ 常用小烏龜物件方法彙整
方法 | 參數 | 說明 |
---|---|---|
forward(<x>) | distance | 向前走一段距離 <x> |
backward(<x>) | distance | 向後走一段距離 <x> |
right(<a>) | angle | 順時針旋轉角度 <a> |
left(<a>) | angle | 逆時針旋轉角度 <a> |
up(), penup() | 提筆 | |
down(), pendown() | 落筆 | |
color(<c>) | color name | 設定筆的顏色 <c> |
pensize(<s>) | size | 設定筆的尺寸 <s> |
fillcolor(<c>) | color name | 設定填滿多邊形的顏色 <c> |
heading() | 回覆目前的方向 | |
setheading(<a>) | angle | 設定目前方向為 <a> 角度 |
position() | 回覆目前的位置 | |
goto(<x>, <y>) goto([<x>, <y>]) | x, y | 前往 (<x>, <y>) 位置 |
speed(<s>) | speed | 設定速度 <s> (0 ~ 10) |
begin_fill() | 多邊形開始填色 | |
end_fill() | 多邊形結束填色 | |
circle(<r>) | radius | 畫一個圓,半徑為 <r> |
write(<s>) | string | 在目前位置寫文字 <s> |
dot() | 在目前位置留下一個點記號 | |
stamp() | 在目前位置留下一個小烏龜形狀記號 | |
shape(<s>) | shap name | 設定小烏龜形狀 <s> ('arrow', 'turtle', 'circle', 'square', 'triangle', 'classic') |