文章目录(Table of Contents)
简介
Turtle
库是 Python 的基础绘图库。其主要是用于程序设计入门,是标准库之一,利用 turtle 可以制作很多复杂的绘图。总体来说,turtle 库可以帮助初学者对学习 Python 有兴趣。
Turtle 名称含义为「海龟」。我们想象一只海龟,位于显示器上窗体的正中心,在画布上游走,它游走的轨迹就形成了绘制的图形。海龟的运动是由程序控制的,它可以变换颜色,改变大小(宽度)等。
参考资料
- turtle — Turtle graphics,turtle 库的官方说明文档
- The Beginner's Guide to Python Turtle,一个非常好,非常全的 turtle 入门材料
- Python-Painting-Doraemon-Pikachu,利用 turtle 绘制哆啦A梦和皮卡丘
- turtle painting,利用 turtle 绘制一系列的内容(例如樱花树,爱心等)
Turtle 库简单使用
Turtle 是 Python 的一个内建的库,所以我们不需要进行安装。我们唯一需要做的就是将其导入到 Python 环境中。我们可以通过下面的命令来导入 turtle 库。
- import turtle
现在 turtle 已经在我们的环境中了。我们可以创建一个单独的窗口,在上面执行绘画的命令。首先我们初始化一个窗口。
- s = turtle.getscreen()
除了上面初始化画布的方式之外,我们还可以使用 turtle.Screen()
来进行定制化。中间可以输入画布的大小等,如下所示:
- s = turtle.Screen()
- win_width, win_height = 2000, 2000
- s.screensize(win_width, win_height, 'white')
接着我们初始化一个变量,这个就是来表示 turtle
的,我们可以控制他的移动。
- t = turtle.Turtle()
现在我们已经有了窗口(可以认为是一个画布)和我们的海龟(可以认为是一支画笔)。我们可以使用我们的程序来控制海龟在画布上进行移动。这个海龟会有一些属性是可以改变的,例如大小,颜色,移动速度等。下面我们就会详细说明如何控制海龟的移动。
控制海龟移动的方向
我们首先学习如何控制海龟朝不同的方向进行移动。一共是有四个方向海龟可以移动,分别是:
- 前(Forward)
- 后(Backward)
- 左(Left)
- 右(Right)
其中 .forward
和 .backward
分别表示表示朝着面向,或是背面的方向移动。里面可以放参数,来控制移动的距离。.left
和 .right
表示转向,可以控制转向的角度。我们可以尝试一下下面的例子:
- import turtle
- s = turtle.getscreen() # 初始化一个 screen
- t = turtle.Turtle() # 初始化一个变量, 表示 turtle
- t.speed(1) # 控制会话速度
- t.forward(100)
- t.right(90)
- t.forward(100)
- t.left(90)
- t.backward(100)
运行上面代码,我们的海龟会依次执行以下的动作(上面 speed 是控制海龟移动的速度的,这个稍后会有更加详细的介绍):
- 向前移动 100 个单位
- 右转 90 度
- 向前走 100 个单位
- 左转 90 度
- 前后走 100 个单位
最终会得到如下的效果:
上面的左转,右转,直行和后退都是可以化简的,如下所示:
- t.rt() instead of t.right()
- t.fd() instead of t.forward()
- t.lt() instead of t.left()
- t.bk() instead of t.backward()
我们可以在上面代码后面加入这一行,使得窗口不会马上关闭:
- turtle.exitonclick() # 不让窗口马上关闭
除了按照上面那样移动之外,我们还可以控制海龟直接到达指定的位置。我们可以把整个屏幕想象成一个二维坐标系,起始点就是原点,于是每一个点都有了坐标。
整个屏幕被分为了四个象限,海龟初始的地方是 (0,0),我们称之为原点,或是 Home。我们可以使用 .goto
将海龟移动到屏幕的任意位置。下面是一个简单的例子。
- import turtle
- s = turtle.getscreen() # 初始化一个 screen
- t = turtle.Turtle() # 初始化一个变量, 表示 turtle
- t.speed(1) # 控制会话速度
- # 直接移动到 (100, 100)
- t.goto(100, 100)
我们直接将海龟移动到 (100, 100)
的位置,输入如下所示:
我们还可以使用下面的命令直接将海龟移动到起始位置,也就是 Home
的位置。
- t.home()
这个代码是与 t.goto(0,0)
是等价的。
绘制基础图形
上面我们已经知道了如何移动海龟。接下来我们尝试一下如何绘制出长方形。
- import turtle
- s = turtle.getscreen() # 初始化一个 screen
- t = turtle.Turtle() # 初始化一个变量, 表示 turtle
- t.speed(1) # 控制会话速度
- t.forward(100)
- t.right(90)
- t.forward(50)
- t.right(90)
- t.forward(100)
- t.right(90)
- t.forward(50)
上面的代码输出结果如下所示:
可以看到在绘制长方形的时候,每次转向是一样的。于是我们考虑可以通过 for 循环来绘制一个正方形。代码如下所示:
- for i in range(4):
- t.forward(100)
- t.right(90)
最终绘制出的结果如下所示:
上面我们绘制了一个长方形和一个正方形。可以尝试使用 .goto
的方式来绘制长方形和正方形。同时也可以尝试绘制三角形,或是其他形状。
绘制预设图像-圆与点
上面我们介绍了如何绘制类似「矩形」或是「三角形」这样的几何形状。但是假设现在要和我们绘制一个圆,如果要使用上面的方式,是会比较吃力的。所以在 turtle 中预设了一些形状,例如我们可以使用下面的命令来绘制圆,其中参数是控制圆的半径。
- t.circle(100)
- t.circle(60)
我们绘制了两个半径不同的圆,最终的效果如下所示:
我们可以使用 while 循环,来绘制一个嵌套的圆,同时需要注意,当半径为负数时,圆的朝向就是向下的,可以看一下下面的代码:
- n = 10
- while n<=50:
- t.circle(n)
- t.circle(-n)
- n += 10
最终绘制的结果如下图所示:
除了绘制圆之外,我们还可以绘制点。这个是没有绘画过程的,是一瞬间完成的。
- t.dot(100)
同样,我们也可以控制里面的参数来控制点的大小。到目前位置,我们了解了如何使用 turtle 来绘制简单的图形。下面我们会介绍如何自定义 turtle,例如自定义背景颜色,自定义画笔的大小等。
使用 turtle 显示文字
我们可以使用 write 来让 turtle 显示指定的文字。下面是一个简单的例子,这里 False 控制写字的时候 turtle 是否会移动到最后一笔的位置。
- toy_turtle.write("Python Fan", False, align="center", font=("Arial", 16, 'normal', 'bold', 'italic', 'underline'))
一般在写字的时候,我们习惯于把 turtle 给隐藏掉:
- toy_turtle.hideturtle()
获得 turtle 的位置
当我们控制 turtle 移动之后,我们可以获取他们的位置。通过 turtle.pos()
来获取,如下所示:
使用 .pos
,可以获取到 x 和 y 的坐标。
进一步自定义 Turtle 图像
这里其实可以分为两个部分,第一个部分是对于画布的自定义;第二个部分是对于画笔的自定义。
自定义画布颜色或图像
上面我们默认情况下画布都是白色的,这个颜色我们是可以自定义的。下面是自定义的方式。
- turtle.bgcolor("blue")
- turtle.bgcolor("red")
- turtle.bgcolor("green")
- turtle.bgcolor("#00FFFF")
我们也可以是使用指定的图像来作为背景,下面是自定义的方式:
- s = turtle.getscreen() # 初始化一个 screen
- s.bgpic('turtle_2.gif')
修改画布标题
有些时候我们可以修改我们窗口的标题,更加定制化。例如下面我们将窗口标题修改为"My Title Program"。
- turtle.title("My Turtle Program")
最终修改的结果如下图所示:
修改 turtle 的大小
我们还可以增大和减少屏幕上 turtle 的大小。这个大小的改变是不会影响到最终画笔的粗细的,只是影响显示的大小而已。我们使用 .shapesize 来改变大小,主要会有以下的三个参数:
- Stretch length
- Stretch width
- Outline width
我们使用下面的参数来控制画笔的大小:
- t.shapesize(1,1,10)
下面展示了不同的参数的效果,可以看到这里是不影响最终绘制线条的粗细的。
修改 turtle 的形状
在默认情况下画笔的形状不是一个 turtle(海龟),而只是一个三角形。但是我们可以将他的形状修改为 turtle 的形状,或是一些其他的形状。Turtle 库一共支持以下的几种形状:
- Square,正方形
- Arrow,箭头
- Circle,圆形
- Turtle,海龟的形状
- Triangle,三角形
- Classic,也是类似于箭头,他默认的形状
我们可以使用以下的命令来修改形状,下面是修改为 turtle:
- t.shape('turtle')
最终实际的效果如下所示:
除了使用他默认的形状之外,我们还可以自定义形状。如下所示,我们自定义两个形状,并将其拼接在一起。其中第一个是一个箭头,第二个是一个三角形。
- s = turtle.Shape("compound")
- poly1 = ((0,0),(10,-5),(0,10),(-10,-5))
- s.addcomponent(poly1, "red", "blue")
- poly2 = ((0,0),(10,-5),(-10,-5))
- s.addcomponent(poly2, "blue", "red")
- turtle.register_shape("myshape", s)
- t.shape('myshape')
最终的效果如下所示:
我们甚至可以将一个图片,或是动图注册为 shape,下面是一个简单的例子:
修改 turtle 的速度
我们还可以自定义 turtle 移动的速度。我们可以加快或是减慢他移动的速度,使用 speed 来进行控制,下面是简单的说明:
- t = turtle.Turtle() # 初始化一个变量, 表示 turtle
- t.speed(1) # 控制移动速度
我们需要注意一下这里速度的对应关系,不是数字越小速度越慢,他有自己的对应关系:
- 'fastest' : 0
- 'fast' : 10
- 'normal' : 6
- 'slow' : 3
- 'slowest' : 1
速度快慢与参数的关系如上所示,就是会比较奇怪。
改变画笔的粗细
上面是改变了 turtle 的大小,不会改变最终线条的粗细。但是有的时候我们需要增加或是减少绘制的线条的粗细,我们可以使用 .pensize
来改变绘制的粗细。
- t.pensize(5) # 控制线条的粗细
- t.circle(100)
- t.pensize(1)
- t.circle(60)
.pensize
中的参数表示线条的粗细。数字越大,线条越粗。上面的代码表示第一个圆使用 5 倍粗细的线条进行绘制,第二个圆使用正常的线条进行绘制。最终的结果如下所示:
修改 turtle 和绘制线条的颜色
在默认情况下当我们打开画布的时候,turtle 是一个黑色的图像,并且绘制出的线条也是黑色的。但是根据我们的需求,我们可以进行下面两点定制化:
- 改变 turtle 的颜色,也就是改变他的填充颜色,也可以改变封闭图像里面的颜色 (change the fill color)。
- 改变线条的颜色,同时改变 turtle 的 outline,可以改变 turtle 走出的线条的颜色。
下面我们来看一下改变颜色的例子,我们进行如下的设置:
- t.shapesize(3,3,3)
- t.pensize(3) # 改变线的宽度
- t.fillcolor('red') # 修改 turtle 的颜色
- t.pencolor('green') # 修改 outline 的颜色
最终输出的效果如下所示:
我们也可以将上面修改颜色写在一起,如下所示:
- t.color('green', 'red')
其中,
- 第一个颜色是画线的颜色(the first color is for the pen)
- 第二个颜色是 turtle 的颜色(the second is for the fill)
在绘制的图形中进行填充
前面介绍的都是修改线条的颜色。除此之外,我们还可以修改里面填充的颜色。我们使用 .begin_fill
来告诉程序我们准备绘制一个封闭的图像,需要进行颜色的填充。使用 .end_fill
来告诉程序封闭的图像已经创建完毕,现在开始填充他。
在下面的代码中,我们创建了一个正方形,并在里面填充了蓝色,线条颜色为红色。我们还是首先使用 color 来设置颜色:
- t.color('red', 'blue')
- t.begin_fill()
- for i in range(4):
- t.forward(100)
- t.right(90)
- t.end_fill()
最终的效果如下所示:
通过一行代码设置样式
现在假设我们有如下的需求:
- 设置画笔颜色为紫色(Pen color: purple)
- 设置填充颜色为橘色(Fill color: orange)
- 设置画笔粗细为 10(Pen size: 10)
- 设置画笔速度为 9(Pen speed: 9)
如果按照我们上面讲的,我们的代码需要写成下面的样子:
- t.pencolor('purple')
- t.fillcolor('orange')
- t.pensize(10)
- t.speed(9)
- t.begin_fill()
- t.circle(100)
- t.end_fill()
最终的生成效果如下所示:
但是每次这么写会比较繁琐,我们可以将这些都写在一行里面。代码如下所示:
- t.pen(pencolor="purple", fillcolor="orange", pensize=10, speed=9)
- t.begin_fill()
- t.circle(100)
- t.end_fill()
这里最终生成的效果是和上面一样。
将画笔抬起与放下
有的时候,我们想要让我们的 turtle 从屏幕上的一个点移动到另外的一个点,但是又先不要绘制任何东西。为了实现这个,我们可以使用 .penup()
,这个非常类似与将画笔提起。如果我们想要让我们的 turtle 重新开始绘画,可以使用 .pendown()
。
下面我们尝试一个简单的例子,让我们的 turtle
移动到 (100, 100)
的位置在开始绘制圆。
- t.penup() # 提起画笔
- t.goto(100, 100) # 移动到 100,100
- t.pendown() # 将笔放下
- t.circle(60) # 开始绘制圆
这里最终的效果如下所示:
多个 turtle 与 turtle 的克隆
有的时候我们需要在画布上有多个类似的 turtle。例如一个 turtle 可以表示一种颜色的画笔,画笔定义好了,只需要修改颜色,就可以使用 clone。如果直接想要多个,只需要直接初始化多个 turtle 即可。例如下面所示:
- turtle_1 = turtle.Turtle()
- turtle_2 = turtle.Turtle()
在下面的代码中,我们初始化了一个 turtle,接着对其进行克隆,并修改他的属性。
- import turtle
- s = turtle.getscreen() # 初始化一个 screen
- t = turtle.Turtle() # 初始化一个变量, 表示 turtle
- t.pen(pencolor="purple", fillcolor="orange", pensize=10, speed=1)
- c = t.clone()
- c.pen(pencolor="red", fillcolor="blue", pensize=10, speed=1)
- c.circle(60)
- t.circle(-60)
最终的效果如下所示,上面的圆是使用克隆的画笔绘制的,下面的圆是使用原始的画笔绘制的。
删除某一个 turtle 绘制的内容
我们可以删除某一个 turtle
绘制的内容。如下所示:
- t = turtle.Turtle()
- t.clear()
注意此时只是清除了绘制的内容,而此时 turtle 的位置是不会移动的。如果我们在一个画布中有多个 turtles,那由别的 turtle 绘制的内容是不会消失的。
重置某个 turtle
上面的 clear
只可以删除某个 turtle
当先绘制的内容,但是他的位置是不变的。这里我们使用 reset
可以将 turtle
的位置也回到 HOME
的位置,同时所有设置的参数都会变为默认的参数。同样的,这个也是对于某个 turtle 来说的。只能将指定 turtle 进行重置。
下面我们看一个例子,有两个 turtle,我们只重置其中的一个 turtle。
- s = turtle.getscreen() # 初始化一个 screen
- t = turtle.Turtle() # 初始化一个变量, 表示 turtle
- t.pen(pencolor="purple", fillcolor="orange", pensize=10, speed=3)
- c = t.clone()
- c.pen(pencolor="red", fillcolor="blue", pensize=10, speed=3)
- c.circle(60)
- t.circle(-60)
- t.reset()
最终的效果如下所示,可以看到只有指定的 turtle 的进行了重置。
使用图片代替 Turtle 形状
前面我们讲过了如何自定义 Turtle,那部分更多的是在介绍自定义画笔属性(例如画笔的颜色,粗细等)。如果我们想要使用图片来作为画笔的形状呢。
- 我们使用
addshape
将图片加到画布中(一定要是gif
格式的图片,我们可以先保存为png
,在修改后缀为gif
也是可以的); - 使用
shape
来指定某一个画笔的形状;
下面的代码将两个画笔改变为指定「哆啦 A 梦」的形状:
- import turtle
- s = turtle.Screen()
- win_width, win_height = 2000, 2000
- s.screensize(win_width, win_height, 'white')
- s.addshape(name='D://cat1.gif')
- s.addshape(name='D://cat2.gif')
- # 初始化第一个画笔
- turtle_1 = turtle.Turtle()
- turtle_1.shape('D://cat1.gif')
- turtle_1.pen(
- pencolor="black",
- pensize=2,
- speed=1)
- # 初始化第二个画笔
- turtle_2 = turtle.Turtle()
- turtle_2.shape('D://cat2.gif')
- turtle_2.pen(
- pencolor="purple",
- pensize=10,
- speed=1)
- # 第一个画笔绘制正方形
- turtle_1.penup()
- turtle_1.goto(0, 200)
- turtle_1.write('123')
- # 第二个画笔绘制圆
- turtle_2.circle(300)
- turtle.exitonclick()
最终的效果如下图所示:
自定义背景图片
我们也可以使用自定义的图片来作为背景图片,使用 bgpic
来更改背景图片。下面的代码,我们将背景替换为海滩的图片(同样,也是需要 gif
格式的图片):
- import turtle
- s = turtle.Screen()
- win_width, win_height = 2000, 2000
- s.screensize(win_width, win_height, 'white')
- s.bgpic("D://sea.gif")
- turtle.exitonclick()
Turtle 制作海龟赛跑
我们把前面学到的所有关于 Turtle 的用起来,制作一个海龟赛跑的游戏。游戏主要是两只海龟,每位玩家随机一个数表示海龟移动的距离,最后看谁先移动到终点。
游戏环境设计
我们首先初始化海龟,给出他们的初始化位置(也就是对应的起点):
- player_one = turtle.Turtle()
- player_one.color("green")
- player_one.shape("turtle")
- player_one.penup()
- player_one.goto(-200,100)
- player_two = player_one.clone()
- player_two.color("blue")
- player_two.penup()
- player_two.goto(-200,-100)
上面我们两个 player 是使用了 clone,我们也可以直接定义两个,下面也使用 turtle.Turtle()
将那些初始化。接着我们给出各自对应的重点,终点使用圆来表示。起点和终点距离是 500:
- player_one.goto(300,60) # 去终点
- player_one.pendown()
- player_one.circle(40)
- player_one.penup()
- player_one.goto(-200,100) # 返回起点
- player_two.goto(300,-140)
- player_two.pendown()
- player_two.circle(40)
- player_two.penup()
- player_two.goto(-200,-100)
在完成了这些之后,我们就可以看到如下的场景:
游戏功能设计
在绘制好了上面游戏的环境之后,我们就要设计主体的功能了,主要有以下的内容。
- 我们需要能够检查海龟是否到达了终点;
- 如果没有达到终点,需要让玩家继续玩;
- 在每一步中,会从 1-6 中随机选择一个数字,代表移动的距离;
- while (player_one.pos()[0] < 260) and (player_two.pos()[0] < 260): # 游戏结束的条件
- # player one 移动
- screen.textinput("随机数", "Player One:")
- random_num = random.randint(1, 6)
- player_one.forward(random_num * 20)
- if player_one.pos()[0] >= 260:
- print('1') # 判断输赢
- break
- # player two 移动
- screen.textinput("随机数", "Player Two:")
- random_num = random.randint(1, 6)
- player_two.forward(random_num * 20)
- if player_two.pos()[0] >= 260:
- print('2') # 判断输赢
- break
上面是简单实现的操作的功能,最终的效果如下所示:
这个还是可以继续完善的,首先可以有一个随机数,决定谁先开始。其次可以制作一个显示比分的内容。然后可以有每次谁移动的,移动了多少的提示。
- 微信公众号
- 关注微信公众号
- QQ群
- 我们的QQ群号
评论