Python 海龟绘图:turtle库的使用

turtle 简介

turtle库是 turtle 绘图体系 Python 的实现,turtle 库是 python 的标准库之一,属于入门级的图形绘制函数库。

turtle 库绘制原理:有一只海龟在窗体正中心,在画布上游走,走过的轨迹形成了绘制的图形,海龟由程序控制,可以自由改变颜色、方向宽度等。

官方文档地址:https://docs.python.org/3/library/turtle.html


turtle 基础知识

画布

画布(canvas)就是 turtle 为我们展开用于绘图区域,我们可以设置它的大小初始位置

我们可以通过screensize函数来设置画布的大小和背景颜色:

1
turtle.screensize(canvwidth=None, canvheight=None, bg=None)

其中screensize函数的三个参数分别表示画布的宽、高和背景颜色。

或者通过setup函数来设置:

1
turtle.setup(width=0.5, height=0.75, startx=None, starty=None)

其中参数widthheight:输入宽和高为整数时, 表示像素;为小数时,表示占据电脑屏幕的比例。

(startxstarty): 这一坐标表示矩形窗口左上角顶点的位置,如果为空,则窗口位于屏幕中心。


画笔

画笔的状态

在画布上,默认有一个坐标原点为画布中心的坐标轴,坐标原点上有一只面朝 x 轴正方向小乌龟。

这里我们描述小乌龟时使用了两个词语:坐标原点(位置),面朝 x 轴正方向(方向), turtle 绘图中,就是使用位置方向描述小乌龟(画笔)的状态。


画笔的属性

画笔有宽度颜色移动速度三个属性,分别可以通过以下函数来设置:

函数描述
turtle.pensize()设置画笔的宽度。
turtle.pencolor()没有参数时,返回当前画笔颜色;
传入参数时,设置画笔颜色。
turtle.speed(speed)设置画笔移动速度,数字越大越快,当速度为 0 时为最快速。

绘图命令

操纵海龟绘图有着许多的命令,这些命令主要可以划分为 3 种:一种为画笔运动命令,一种为画笔控制命令,还有一种是全局控制命令

画笔运动命令

画笔运动命令如下:

命令描述
turtle.forward(distance)向当前画笔方向移动 distance 像素长度
turtle.backward(distance)向当前画笔相反方向移动 distance 像素长度
turtle.right(degree)顺时针移动 degree°
turtle.left(degree)逆时针移动 degree°
turtle.pendown()
或 turtle.pd()
移动时绘制图形,缺省时也为绘制
turtle.goto(x,y)将画笔移动到坐标为 x,y 的位置
turtle.penup()
或 turtle.pu()
提起笔移动,不绘制图形,用于另起一个地方绘制
turtle.circle()画圆,半径为正(负),表示圆心在画笔的左边(右边)画圆
setx( )将当前 x 轴移动到指定位置
sety( )将当前 y 轴移动到指定位置
setheading(angle)设置当前朝向为 angle 角度
home()设置当前画笔位置为原点,朝向水平向右
dot(r)绘制一个指定直径和颜色的圆点

值得一提的是circle()函数:

1
turtle.circle(radius, extent=None, steps=None)

它包含三个参数:

  1. radius表示半径,半径为正(负),表示圆心在画笔的左边(右边)画圆;
  2. extent表示角度,360 为一个整圆;
  3. step为 3 时,表示绘制三角形。
画笔控制命令

画笔控制命令如下:

命令描述
turtle.fillcolor(colorstring)绘制图形的填充颜色
turtle.color(color1, color2)同时设置 pencolor=color1, fillcolor=color2
turtle.filling()返回当前是否在填充状态
turtle.begin_fill()准备开始填充图形
turtle.end_fill()填充完成
turtle.hideturtle()隐藏画笔的 turtle 形状
turtle.showturtle()显示画笔的 turtle 形状
全局控制命令

全局控制命令如下:

命令描述
turtle.clear()清空 turtle 窗口,但是 turtle 的位置和状态不会改变
turtle.reset()清空窗口,重置 turtle 状态为起始状态
turtle.undo()撤销上一个 turtle 动作
turtle.isvisible()返回当前 turtle 是否可见
stamp()复制当前图形
turtle.write(s [,font=(“font-name”,font_size,“font_type”)])写文本,s 为文本内容,font 是字体的参数,分别为字体名称,大小和类型;font 为可选项,font 参数也是可选项
其他命令

除了上述的三种命令外,turtle 库中还要一些不常用的命令:

命令描述
turtle.mainloop()或 turtle.done()启动事件循环,调用 Tkinter 的 mainloop 函数。
必须是海龟图形程序中的最后一个语句。
turtle.mode(mode=None)设置海龟模式(“standard”,“logo”或“world”)并执行重置。如果没有给出模式,则返回当前模式。
turtle.delay(delay=None)设置或返回以毫秒为单位的绘图延迟。
turtle.begin_poly()开始记录多边形的顶点。当前的海龟位置是多边形的第一个顶点。
turtle.end_poly()停止记录多边形的顶点。当前的海龟位置是多边形的最后一个顶点。将与第一个顶点相连。
turtle.get_poly()返回最后记录的多边形。

代码实例

哆啦 A 梦

下面的程序可以绘制出哆啦 A 梦:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
import turtle

# 创建哆啦A梦
doraemon = turtle.Turtle()
doraemon.speed(10)


def draw_eye_white_circle(x):
doraemon.goto(x, 80)
doraemon.pendown()
doraemon.color('black')
doraemon.begin_fill()
doraemon.circle(15)
doraemon.color('white')
doraemon.end_fill()


def draw_eye_black_circle(x):
doraemon.goto(x, 90)
doraemon.color('black')
doraemon.begin_fill()
doraemon.begin_fill()
doraemon.circle(6)
doraemon.end_fill()
doraemon.penup()


# 画蓝色圆
doraemon.color('#0093dd')
doraemon.begin_fill()
doraemon.circle(60)
doraemon.end_fill()

# 画白色圆
doraemon.begin_fill()
doraemon.circle(50)
doraemon.color('white')
doraemon.end_fill()

# 画右眼白
draw_eye_white_circle(15)

# 画右眼黑
draw_eye_black_circle(6)

# 画左眼白
draw_eye_white_circle(-15)

# 画左眼黑
draw_eye_black_circle(-24)

# 画鼻子
doraemon.goto(0, 60)
doraemon.pendown()
doraemon.color('#c70000')
doraemon.begin_fill()
doraemon.circle(8)
doraemon.end_fill()

doraemon.color('black')
# 设置朝向(对应上北下南,左西右东)
# 0 为东,90 为北,180 为西,270 为南(同 -90)
doraemon.setheading(-90)
# 沿着刚刚设定的朝下方向绘制 40px
doraemon.forward(40)
doraemon.penup()

# 画嘴巴
doraemon.goto(-30, 40)
doraemon.pendown()
doraemon.circle(30, 180)
doraemon.penup()
doraemon.goto(0, 0)

turtle.Screen().exitonclick()

效果图:
哆啦A梦

动态时钟

下面的程序可以绘制出动态时钟:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
import turtle
from datetime import *

# 抬起画笔,向前运动一段距离放下


def Skip(step):
turtle.penup()
turtle.forward(step)
turtle.pendown()


def mkHand(name, length):
# 注册Turtle形状,建立表针Turtle
turtle.reset()
Skip(-length * 0.1)
# 开始记录多边形的顶点。当前的乌龟位置是多边形的第一个顶点。
turtle.begin_poly()
turtle.forward(length * 1.1)
# 停止记录多边形的顶点。当前的乌龟位置是多边形的最后一个顶点。将与第一个顶点相连。
turtle.end_poly()
# 返回最后记录的多边形。
handForm = turtle.get_poly()
turtle.register_shape(name, handForm)


def Init():
global secHand, minHand, hurHand, printer
# 重置Turtle指向北
turtle.mode("logo")
# 建立三个表针Turtle并初始化
mkHand("secHand", 135)
mkHand("minHand", 125)
mkHand("hurHand", 90)
secHand = turtle.Turtle()
secHand.shape("secHand")
minHand = turtle.Turtle()
minHand.shape("minHand")
hurHand = turtle.Turtle()
hurHand.shape("hurHand")

for hand in secHand, minHand, hurHand:
hand.shapesize(1, 1, 3)
hand.speed(0)

# 建立输出文字Turtle
printer = turtle.Turtle()
# 隐藏画笔的turtle形状
printer.hideturtle()
printer.penup()


def SetupClock(radius):
# 建立表的外框
turtle.reset()
turtle.pensize(7)
for i in range(60):
Skip(radius)
if i % 5 == 0:
turtle.forward(20)
Skip(-radius - 20)

Skip(radius + 20)
if i == 0:
turtle.write(int(12), align="center",
font=("Courier", 14, "bold"))
elif i == 30:
Skip(25)
turtle.write(int(i/5), align="center",
font=("Courier", 14, "bold"))
Skip(-25)
elif (i == 25 or i == 35):
Skip(20)
turtle.write(int(i/5), align="center",
font=("Courier", 14, "bold"))
Skip(-20)
else:
turtle.write(int(i/5), align="center",
font=("Courier", 14, "bold"))
Skip(-radius - 20)
else:
turtle.dot(5)
Skip(-radius)
turtle.right(6)


def Week(t):
week = ["星期一", "星期二", "星期三",
"星期四", "星期五", "星期六", "星期日"]
return week[t.weekday()]


def Date(t):
y = t.year
m = t.month
d = t.day
return "%s %d %d" % (y, m, d)


def Tick():
# 绘制表针的动态显示
t = datetime.today()
second = t.second + t.microsecond * 0.000001
minute = t.minute + second / 60.0
hour = t.hour + minute / 60.0
secHand.setheading(6 * second)
minHand.setheading(6 * minute)
hurHand.setheading(30 * hour)

turtle.tracer(False)
printer.forward(65)
printer.write(Week(t), align="center",
font=("Courier", 14, "bold"))
printer.back(130)
printer.write(Date(t), align="center",
font=("Courier", 14, "bold"))
printer.home()
turtle.tracer(True)

# 100ms后继续调用tick
turtle.ontimer(Tick, 100)



# 打开/关闭龟动画,并为更新图纸设置延迟。
turtle.tracer(False)
Init()
SetupClock(160)
turtle.tracer(True)
Tick()
turtle.mainloop()

静态效果图:

动态时钟

小樱花树

下面的程序可以绘制出小樱花树:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
import turtle
import random

# 画樱花的躯干(60,t)


def Tree(branch, t):
if branch > 3:
if 8 <= branch <= 12:
if random.randint(0, 2) == 0:
t.color('snow') # 白
else:
t.color('lightcoral') # 淡珊瑚色
t.pensize(branch / 3)
elif branch < 8:
if random.randint(0, 1) == 0:
t.color('snow')
else:
t.color('lightcoral') # 淡珊瑚色
t.pensize(branch / 2)
else:
t.color('sienna') # 赭色
t.pensize(branch / 10)
t.forward(branch)
a = 1.5 * random.random()
t.right(20 * a)
b = 1.5 * random.random()
Tree(branch - 10 * b, t)
t.left(40 * a)
Tree(branch - 10 * b, t)
t.right(20 * a)
t.up()
t.backward(branch)
t.down()

# 掉落的花瓣


def Petal(m, t):
for i in range(m):
a = 200 - 400 * random.random()
b = 10 - 20 * random.random()
t.up()
t.forward(b)
t.left(90)
t.forward(a)
t.down()
t.color('lightcoral') # 淡珊瑚色
t.circle(1)
t.up()
t.backward(a)
t.right(90)
t.backward(b)


# 绘图区域
t = turtle.Turtle()
# 画布大小
w = turtle.Screen()
t.hideturtle() # 隐藏画笔
t.getscreen().tracer(5, 0)
w.screensize(bg='wheat') # wheat小麦
t.left(90)
t.up()
t.backward(150)
t.down()
t.color('sienna')

# 画樱花的躯干
Tree(60, t)
# 掉落的花瓣
Petal(200, t)
w.exitonclick()

效果图:
樱花树

飘落樱花树

下面的程序可以绘制出飘落樱花树(绘制时间较长,默认隐藏绘制过程):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
from turtle import *
from random import *
from math import *

def tree(n,l):
pd() # 下笔
# 阴影效果
t = cos(radians(heading()+45))/8+0.25
pencolor(t,t,t)
pensize(n/3)
forward(l) # 画树枝

if n>0:
b = random()*15+10 # 右分支偏转角度
c = random()*15+10 # 左分支偏转角度
d = l*(random()*0.25+0.7) # 下一个分支的长度
# 右转一定角度,画右分支
right(b)
tree(n-1,d)
# 左转一定角度,画左分支
left(b+c)
tree(n-1,d)
# 转回来
right(c)
else:
# 画叶子
right(90)
n=cos(radians(heading()-45))/4+0.5
pencolor(n,n*0.8,n*0.8)
circle(3)
left(90)
# 添加 0.3 倍的飘落叶子
if(random()>0.7):
pu()
# 飘落
t = heading()
an = -40 +random()*40
setheading(an)
dis = int(800*random()*0.5 + 400*random()*0.3 + 200*random()*0.2)
forward(dis)
setheading(t)
# 画叶子
pd()
right(90)
n = cos(radians(heading()-45))/4+0.5
pencolor(n*0.5+0.5,0.4+n*0.4,0.4+n*0.4)
circle(2)
left(90)
pu()
# 返回
t=heading()
setheading(an)
backward(dis)
setheading(t)
pu()
backward(l) # 退回

bgcolor(0.5,0.5,0.5) # 背景色
ht() # 隐藏turtle
speed(0) # 速度 1-10 渐进,0 最快
tracer(0,0) # 注释本行可以显示出绘制过程
pu() # 抬笔
backward(100)
left(90) # 左转 90 度
pu() # 抬笔
backward(300) # 后退 300
tree(12,100) # 递归 7 层
done()

效果图:
飘落樱花树

圣诞树

下面的程序可以绘制出圣诞树(绘制时间较长,默认隐藏绘制过程):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
from turtle import *
import random

n = 100.0

speed(0)
screensize(bg='seashell')
tracer(0, 0) # 注释本行显示绘制过程
left(90)
forward(3*n)
color("orange", "yellow")
begin_fill()
left(126)

for i in range(5):
forward(n/5)
right(144)
forward(n/5)
left(72)
end_fill()
right(126)

color("dark green")
backward(n*4.8)
def tree(d, s):
if d <= 0: return
forward(s)
tree(d-1, s*.8)
right(120)
tree(d-3, s*.5)
right(120)
tree(d-3, s*.5)
right(120)
backward(s)
tree(15, n)
backward(n/2)

for i in range(200):
a = 200 - 400 * random.random()
b = 10 - 20 * random.random()
up()
forward(b)
left(90)
forward(a)
down()
if random.randint(0, 1) == 0:
color('tomato')
else:
color('wheat')
circle(2)
up()
backward(a)
right(90)
backward(b)

done()

效果图:
圣诞树