Python标准库程序框架——turtle海龟绘图
我想,科普最好的对象是小朋友,那么就写一下被移植到Python标准库里的turtle库吧。以下代码基于Python 3.8.1。
概述
海龟绘图,最初来自于 Wally Feurzeig, Seymour Papert 和 Cynthia Solomon 于 1967 年所专门为引导儿童学习编程创造的 Logo 编程语言。turtle库的实现过程很形象,犹如一只海龟在画布上画画。海龟绘图很适合用来培养小朋友对编程的学习兴趣。 试想象一下,在一块画布上,起始位置在x-y平面的(0,0)点,执行```import turtle``来召唤一只拿着画笔的海龟(也就是一个点),再利用turtle库里一系列的指令(方法)来指挥这只海龟来爬。最后通过组合这些指令,海龟爬行的轨迹将可以轻松地绘制出精美的形状和图案。并且通过对应函数,能按我们的要求把海龟的轨迹在显示设备上输出显示。
目前的turtle模块是基于Python标准发行版2.5重新编写和扩展,并与原模块100%兼容。因此能以交互方式使用模块的所有命令、类和方法。
turtle模块提供面向对象和面向过程两种形式的海龟绘图基本组件。具体如下:
面向对象:
-
TurtleScreen
类定义图形窗口作为绘图海龟的运动场。它的构造器需要一个tkinter.Canvas
或ScrolledCanvas
作为参数。应在 turtle 作为某个程序的一部分的时候使用。
- Screen() 函数返回一个
TurtleScreen
子类的单例对象。此函数应在 turtle 作为独立绘图工具时使用。作为一个单例对象,其所属的类是不可被继承的。
TurtleScreen/Screen
的所有方法还存在对应的函数,即作为面向过程的接口组成部分。
RawTurtle
类定义海龟对象在TurtleScreen
上绘图。它的构造器需要一个 Canvas,ScrolledCanvas
或TurtleScreen
作为参数,以指定RawTurtle
对象在哪里绘图。从
RawTurtle
派生出子类Turtle
,该类对象在Screen
实例上绘图,若实例不存在则会自动创建。
RawTurtle/Turtle
的所有方法也存在对应的函数,即作为面向过程的接口组成部分
面向过程:
过程式接口提供与
Screen
和Turtle
类的方法相对应的函数。函数名与对应的方法名相同。当Screen
类的方法对应函数被调用时会自动创建一个Screen
对象。当Turtle
类的方法对应函数被调用时会自动创建一个 (匿名的)Turtle
对象。
当需要屏幕上有多个海龟的时候,就必须使用面向对象的接口。
可用的turtle和Screen方法
如前文所说,我们通过import turtle
召唤了一只拿着画笔的海龟,海龟的的动作以及画笔都是通过方法来控制的。同时,作为一只被召唤出来的二维生物,我们作为比他高维的生物,自然有很多降维操纵甚至打击的方法。从某种角度上来说,我们对这些turtle有近乎神迹般的操纵和控制。 然而并不是。具体方法如下。
RawTurtle/Turtle方法和对应函数
以下都是使用Turtle类的实例,命名为turtle
。
-
海龟动作
我们能控制海龟的前进后退,左转右转,爬行速度,甚至能让海龟像赛亚人或者哈利波特里的巫师一样学会幻影移行,同时能随时获知海龟的位置信息。
-
移动和绘制
forward(distance) :前进distance距离
>>> turtle.position() (0.00,0.00) >>> turtle.forward(distance) >>> turtle.position() (distance,0.00) >>> turtle.forward(distance) >>> turtle.position() (distance+distance,0.00)
backward(distance) :后退distance距离
>>> turtle.position() (0.00,0.00) >>> turtle.backward(distance) >>> turtle.position() (-distance,0.00)
right(angle) :右转angle个单位
>>> turtle.heading() 22.0 >>> turtle.right(angle) >>> turtle.heading() 22.0-angle+360.0
left(angle):左转angle个单位
>>> turtle.heading() 22.0 >>> turtle.left(angle) >>> turtle.heading() 22.0+angle
goto(x,y=None) | setposition(x,y=None) :前往/定位一个绝对坐标
setx(x) 设置x坐标
sety(y) 设置y坐标
setheading(to_angle) 设置朝向import turtle from turtle import * color('red', 'yellow') turtle.setheading(90) turtle.heading() done()
home() 返回原点
circle(radius, extent=None, steps=None) 画圆
dot(size=None, color) 画点import turtle from turtle import * color('red', 'yellow') turtle.home() turtle.position() turtle.heading() turtle.circle(50) turtle.position() turtle.heading() turtle.circle(120, 180) # draw a semicircle turtle.position() turtle.heading() done()
stamp() 印章
clearstamp(stampid) 清除印章
clearstamps(n=None) 清除多个印章
undo() :撤消海龟最近一个或多个动作
speed(speed=None) :控制海龟移动速度 -
获取海龟的状态
position() | pos() 位置:返回海龟当前的坐标
towards() :返回海龟朝向
xcor() :返回海龟x坐标
ycor() :返回海龟y坐标
distance() 返回从海龟位置到另一位置的距离
-
-
画笔控制
同时,海龟对于我们来说只是用来画画的工具龟,所以我们对于它用来画轨迹的画笔也有相应的方法来控制。
-
绘图状态
pendown() 画笔落下:海龟移动时将画线
penup() 画笔抬起:海龟移动时不画线
pensize(width=None) 画笔粗细
pen(pen=None,**pendict) 画笔pen为一个包含部分或全部下列键的字典,而pendict是下列键为关键字的关键字参数
- "shown": True/False
- "pendown": True/False
- "pencolor": 颜色字符串或颜色元组
- "fillcolor": 颜色字符串或颜色元组
- "pensize": 正数值
- "speed": 0..10 范围内的数值
- "resizemode": "auto" 或 "user" 或 "noresize"
- "stretchfactor": (正数值, 正数值)
- "outline": 正数值
- "tilt": 数值
isdown() 画笔是否落下:判断画笔是否落下,落下返回
True
,抬起返回False
。 颜色控制
pencolor(*args) :返回和设置画笔颜色
允许四种不同的输入格式:
-
pencolor()
:返回以颜色描述字符串或元组表示的当前画笔颜色; -
pencolor(colorstring)
:设置画笔颜色为colorstring指定的Tk颜色描述字符串; -
pencolor((r,g,b))
:设置画笔颜色为以r,g,b元组表示的RGB颜色; -
pencolor(r,g,b)
:设置画笔颜色为以r,g,b表示的RGB颜色;
fillcolor(*args) :返回和设置填充颜色
同pencolor(*args)一样,有四种不同的输入格式:
-
fillcolor()
:返回以颜色描述字符串或元组表示的当前填充颜色; -
fillcolor(colorstring)
:设置填充颜色为colorstring指定的Tk颜色描述字符串; -
fillcolor((r,g,b))
:设置填充颜色为以r,g,b元组表示的RGB颜色; -
fillcolor(r,g,b)
:设置填充颜色为以r,g,b表示的RGB颜色;
color(* args) 返回或设置画笔颜色和填充颜色
color()允许多种输入格式:
-
color()
:返回以一对颜色描述字符串或元组表示的当前画笔颜色和填充颜色; -
color(colorstring)
,color((r,g,b))
,color(r,g,b)
和pencolor()
相同,同时设置填充和画笔颜色为指定的值; -
color(colorstring1,colorstring2)
,color((r1,g1,b1),(r2,g2,b2))
则相当于pencolor(colorstring1)
加fillcolor(colorstring2)
,rbg格式也与值类似;
填充
filling() 是否填充
begin_fill() 开始填充
end_fill() 结束填充-
更多绘图控制
reset() 重置:从屏幕中删除海龟的绘图,同时海龟返回原点并重置所有变量为默认值
clear() 清空:从屏幕中只删除海龟的绘图
write(*arg, move=False, align="left", font=("Arial",8,"normal")) :书写文本write()的参数具体为:
arg -- 要书写到TurtleScreen的对象'
move -- True/False
align -- 字符串"left","center"或"right"
font -- 一个三元组(fontname, fontsize, fonttype)
-
-
海龟状态
可见性
showturtle() 显示海龟
hideturtle() 隐藏海龟:隐藏海龟可显著加快绘制速度
isvisible() 检查是否可见-
外观
shape(name=None)设置海龟形状为name指定的形状名,如未指定形状名则返回当前的形状名
resizemode(rmode=None) :大小调整模式
rmode为以下值之一:"auto","user","noresize";
不同的大小调整模式有不同的效果如下:
auto: 根据画笔的粗细值调整海龟外观;
user: 根据拉伸因子和轮廓宽度值调整海龟的外观,两者通过
shapesize()
设置,user模式会在shapesize()
带参数调用时生效;noresize:不调整海龟的外观大小;
shapesize(stretch_wid=None, strech_len=None, outline=None) 形状大小
shearfactor(shear=None) 剪切因子
tiltangle(angle=None) :设置或返回当前的倾角
tilt(angle) :海龟形状自其当前倾角转动angle的角度
shapetransform(t11=None, t12=None, t21=None, t22=None) :设置或返回海龟形状的当前变形矩阵
get_shapepoly() 获取形状多边形
-
使用事件
onclick(fun, btn=1, add=None)
当鼠标点击,触发事件fun函数并传入两个参数表示在画布上点击的坐标,
fun -- 函数,调用时传入两个参数, 以表示在画布上点击的坐标;
btn -- 鼠标按钮编号,默认值为1,即鼠标左键;
add -- True或False, 为False将取代先前的绑定
onrelease(fun, btn=1,add=None) 当鼠标释放
当鼠标按键释放,触发事件,三个参数同
onclick()
ondrag(fun, btn=1,add=None) 当鼠标拖动
当鼠标移动,触发事件,三个参数同上
-
特殊海龟方法
begin_poly() 开始记录多边形
end_poly() 结束记录多边形
get_poly() 获取多边形
clone() 克隆
getturtle() | getpen() 获取海龟画笔
getscreen() 获取屏幕
setundobuffer() 设置撤消缓冲区
undobufferentries() 撤消缓冲区条目数
TurtleScreen/Screen方法及对应函数*
-
窗口控制
bgcolor(*args) :设置或返回背景颜色
bgpic(picname=None) 设置背景图片或返回当前背景图片名称picname可以为字符串(若为文件名,则将相应图片设为背景),
nopic
(删除当前背景图片),None
(返回当前背景图片文件名)clear() | clearscreen() 清屏
reset() | resetscreen() 重置
screensize(canvwidth=None, canvheight=None, bg=None) 屏幕大小canviwidth -- 以像素表示画布的新宽度值
canvheight -- 以像素表示画面的新高度值
bg -- 以颜色字符串或颜色元组表示的新背景颜色
setworldcoordinates(llx, lly, urx, ury) 设置世界坐标系
-
动画控制
delay() 延迟
tracer(n=None, delay=None)启用/禁用海龟动画并设置刷新图形的延迟时间
update() :执行一次TurtleScreen刷新
-
使用屏幕事件
listen(xdummy=None, ydummy=None) :
设置焦点到TurtleScreen, 以接收按键事件
onkey(fun, key)
绑定指定fun函数到按键释放事件
onkeypress(fun, key)
绑定指定fun函数到指定按键的按下事件
onclick(fun, btn=1, add=None)
绑定指定fun函数到指定按键的点击屏幕事件
ontimer(fun, t=0)
安装一个计时器,在t毫秒后,调用指定fun函数
mainloop() | done() 主循环
调用Tkinter的mainloop函数
-
输入方法
textinput(title, prompt) 文本输入
弹出一个对话框窗口用来输入一个字符串。
title -- 对话框窗口的标题
prompt -- 一条用来描述要输入的信息的文本numinput(title, prompt, default=None, minval=None, maxval=Non) 数字输入
弹出一个对话框窗口用来输入一个数值。
title -- 对话框窗口的标题
prompt -- 一条用来描述要输入的数值信息的文本
default -- 默认值
minval -- 可输入的最小值
maxval -- 可输入的最大值 -
Screen 专有方法
bye()
关闭海龟绘图窗口
exitonclick()
将bye()方法绑定到Screen上的鼠标事件
setup(width=CFG["width"], height= CFG["height"],startx=CFG["leftright"],starty=_CFG["topbottom"])
设置主窗口的大小和位置。
width -- 数值为整形则表示大小的像素值,若为浮点数则表示屏幕的占比,默认为屏幕50%
height -- 数值为整形表示高度的像素值,若为浮点数则表示屏幕的占比,,默认为屏幕75%
startx -- 正值表示初始位置距离屏幕左边缘像素值,负值表示距离右边缘,None表示窗口水平居中
starty -- 正值表示初始位置距离屏幕上边缘多少像素,负值表示距离下边缘,None表示窗口垂直居中
title(titlestring)
设置海龟窗口标题为title string指定的文本
结语
本想着写一篇面向儿童的科普文。写着写着,被Python官方的手册带偏了,权当是给教儿童科普编程的人小小的对turtle方法的归类和介绍吧。
python是一门简约但不简单的语言,在python的编译器中输入import this
执行后出来的《Python之禅》中,从Python准则的诗句中,可见一斑。Python是一样工具,更是一门语言,它应该很有趣。而通常有趣到极致的代名词是儿童。同时,一门语言想要延续,就应当能吸引年轻的血液。而Python标准库下的turtle模块,应该就是实现这两种功能的法宝之一。
一九九几年和两千零几年说的信息化,再过几天就是2020年,离2000年也已过二十年,现在讨论的和追捧的也已经是智能化。离1946年2月14日也就快74年时间。二十年时间超过了计算机寿命的四分之一,而计算机家用早已推广,还捧出来一个世界首富。可是,对于应用计算机最为核心的编程,到目前还有很多人不掌握。苹果ceo库克拜访上海某小学推广swift语言和社交媒体提到某个学编程的儿童成为了新闻,这种事情个人认为是不正常的。
套用某部电影说的话”21世纪最缺的是人才“。虽然最不缺和不值钱的貌似也是人才。人才从哪里来?人才是从小培养的。而如果有那么一种编程语言,能让小朋友都能学会,并且通过这个语言以后能更容易学习更多其他的语言,无疑是非常好的。对于儿童能学习知识,也能方便生活。对于社会,如果这种语言是如swift一样有盈利公司开发的,公司能赚小朋友父母钱;如果这种语言是开源的,那么整个社会的编程水平总会提高那么一丢丢,这就简直是造福全世界了。
最后,附上一个搜集资料(https://www.cnblogs.com/yangbin1212/p/9763936.html)时看到的使用turtle画出来的有趣的《小猪佩奇》python源代码吧。
完成效果图如下:
"""
@time :12/30/2019
@author: menkwan
"""
# coding:utf-8
import turtle as t
# 绘制小猪佩奇
# =======================================
t.pensize(4)
t.hideturtle()
t.colormode(255)
t.color((255, 155, 192), "pink")
t.setup(840, 500)
t.speed(10)
# 鼻子
t.penup()
t.goto(-100,100)
t.pendown()
t.seth(-30)
t.begin_fill()
a = 0.4
for i in range(120):
if 0 <= i < 30 or 60 <= i < 90:
a = a+0.08
t.left(3) # 向左转3度
t.forward(a) # 向前走a的步长
else:
a = a-0.08
t.left(3)
t.forward(a)
t.end_fill()
t.penup()
t.seth(90)
t.forward(25)
t.seth(0)
t.forward(10)
t.pendown()
t.pencolor(255, 155, 192)
t.seth(10)
t.begin_fill()
t.circle(5)
t.color(160, 82, 45)
t.end_fill()
t.penup()
t.seth(0)
t.forward(20)
t.pendown()
t.pencolor(255, 155, 192)
t.seth(10)
t.begin_fill()
t.circle(5)
t.color(160, 82, 45)
t.end_fill()
# 头
t.color((255, 155, 192), "pink")
t.penup()
t.seth(90)
t.forward(41)
t.seth(0)
t.forward(0)
t.pendown()
t.begin_fill()
t.seth(180)
t.circle(300, -30)
t.circle(100, -60)
t.circle(80, -100)
t.circle(150, -20)
t.circle(60, -95)
t.seth(161)
t.circle(-300, 15)
t.penup()
t.goto(-100, 100)
t.pendown()
t.seth(-30)
a = 0.4
for i in range(60):
if 0 <= i < 30 or 60 <= i <90:
a = a+0.08
t.left(3) # 向左转3度
t.forward(a) # 向前走a的步长
else:
a = a-0.08
t.left(3)
t.forward(a)
t.end_fill()
# 耳朵
t.color((255, 155, 192), "pink")
t.penup()
t.seth(90)
t.forward(-7)
t.seth(0)
t.forward(70)
t.pendown()
t.begin_fill()
t.seth(100)
t.circle(-50, 50)
t.circle(-10, 120)
t.circle(-50, 54)
t.end_fill()
t.penup()
t.seth(90)
t.forward(-12)
t.seth(0)
t.forward(30)
t.pendown()
t.begin_fill()
t.seth(100)
t.circle(-50, 50)
t.circle(-10, 120)
t.circle(-50, 56)
t.end_fill()
#眼睛
t.color((255, 155, 192), "white")
t.penup()
t.seth(90)
t.forward(-20)
t.seth(0)
t.forward(-95)
t.pendown()
t.begin_fill()
t.circle(15)
t.end_fill()
t.color("black")
t.penup()
t.seth(90)
t.forward(12)
t.seth(0)
t.forward(-3)
t.pendown()
t.begin_fill()
t.circle(3)
t.end_fill()
t.color((255, 155, 192), "white")
t.penup()
t.seth(90)
t.forward(-25)
t.seth(0)
t.forward(40)
t.pendown()
t.begin_fill()
t.circle(15)
t.end_fill()
t.color("black")
t.penup()
t.seth(90)
t.forward(12)
t.seth(0)
t.forward(-3)
t.pendown()
t.begin_fill()
t.circle(3)
t.end_fill()
# 腮
t.color((255, 155, 192))
t.penup()
t.seth(90)
t.forward(-95)
t.seth(0)
t.forward(65)
t.pendown()
t.begin_fill()
t.circle(30)
t.end_fill()
# 嘴
t.color(239, 69, 19)
t.penup()
t.seth(90)
t.forward(15)
t.seth(0)
t.forward(-100)
t.pendown()
t.seth(-80)
t.circle(30, 40)
t.circle(40, 80)
# 身体
t.color("red", (255, 99, 71))
t.penup()
t.seth(90)
t.forward(-20)
t.seth(0)
t.forward(-78)
t.pendown()
t.begin_fill()
t.seth(-130)
t.circle(100,10)
t.circle(300,30)
t.seth(0)
t.forward(230)
t.seth(90)
t.circle(300,30)
t.circle(100,3)
t.color((255,155,192),(255,100,100))
t.seth(-135)
t.circle(-80,63)
t.circle(-150,24)
t.end_fill()
# 手
t.color((255,155,192))
t.penup()
t.seth(90)
t.forward(-40)
t.seth(0)
t.forward(-27)
t.pendown()
t.seth(-160)
t.circle(300,15)
t.penup()
t.seth(90)
t.forward(15)
t.seth(0)
t.forward(0)
t.pendown()
t.seth(-10)
t.circle(-20,90)
t.penup()
t.seth(90)
t.forward(30)
t.seth(0)
t.forward(237)
t.pendown()
t.seth(-20)
t.circle(-300,15)
t.penup()
t.seth(90)
t.forward(20)
t.seth(0)
t.forward(0)
t.pendown()
t.seth(-170)
t.circle(20,90)
# 脚
t.pensize(10)
t.color((240,128,128))
t.penup()
t.seth(90)
t.forward(-75)
t.seth(0)
t.forward(-180)
t.pendown()
t.seth(-90)
t.forward(40)
t.seth(-180)
t.color("black")
t.pensize(15)
t.forward(20)
t.pensize(10)
t.color((240, 128, 128))
t.penup()
t.seth(90)
t.forward(40)
t.seth(0)
t.forward(90)
t.pendown()
t.seth(-90)
t.forward(40)
t.seth(-180)
t.color("black")
t.pensize(15)
t.forward(20)
# 尾巴
t.pensize(4)
t.color((255, 155, 192))
t.penup()
t.seth(90)
t.forward(70)
t.seth(0)
t.forward(95)
t.pendown()
t.seth(0)
t.circle(70, 20)
t.circle(10, 330)
t.circle(70, 30)
t.done()