这篇文章主要目的有两个
1. 实现如下效果
2. turtle源码分析,关键函数分析
- turtle坐标系 turtle使用的是数学当中的直角坐标系,其原点坐标为(0,0)
在这张图中,可以想想一下有一个小乌龟位于原点(0,0)位置处,然后根据你的调用进行移动;
疑问一:小乌龟初始移动方向哪?
class TNavigator(object):
"""Navigation part of the RawTurtle.
Implements methods for turtle movement.
"""
START_ORIENTATION = {
"standard": Vec2D(1.0, 0.0),
"world" : Vec2D(1.0, 0.0),
"logo" : Vec2D(0.0, 1.0) }
DEFAULT_MODE = "standard"
DEFAULT_ANGLEOFFSET = 0
DEFAULT_ANGLEORIENT = 1
def __init__(self, mode=DEFAULT_MODE):
self._angleOffset = self.DEFAULT_ANGLEOFFSET
self._angleOrient = self.DEFAULT_ANGLEORIENT
self._mode = mode
self.undobuffer = None
self.degrees()
self._mode = None
self._setmode(mode)
TNavigator.reset(self)
我们知道Turtle是继承自TNavigator类,在这个类中,定义了一个DEFAULT_MODE = “standard”的属性;
在构造TNavigator类的时候,会调用init方法,此时会传入DEFAULT_MODE,这个值就表示小乌龟默认的移动方向;该值表示默认小乌龟默认向东移动;
疑问二:怎么改变小乌龟初始移动方向?
# coding=utf-8
import turtle
turtle.mode("logo");
rectangleTurtle = turtle.Turtle();
rectangleTurtle.forward(100);
turtle.done()
如上,我们可以通过调用mode并传入方向参数来改变小乌龟的初始运行轨迹;其实这个和调用right或left效果是一样的,效果如下
疑问三:怎么关闭小乌龟在移动时产生的动画效果
# coding=utf-8
import turtle
turtle.mode("logo");
rectangleTurtle = turtle.Turtle();
rectangleTurtle.tracer(False)
rectangleTurtle.tracer(8, 25)
rectangleTurtle.forward(100);
rectangleTurtle.tracer(True)
turtle.done()
我们通过tracer传入false即可关闭动画效果,在绘制完成之后注意恢复为true
疑问四:怎么关闭小乌龟的箭头
# coding=utf-8
import turtle
turtle.mode("logo");
rectangleTurtle = turtle.Turtle();
rectangleTurtle.hideturtle();
rectangleTurtle.tracer(False)
rectangleTurtle.tracer(8, 25)
rectangleTurtle.forward(100);
rectangleTurtle.tracer(True)
turtle.done()
如上可以通过hideturtle方法来让箭头消失;同理可以调用showturtle方法把箭头再显示出来
疑问五:小乌龟绘制的屏幕是怎么产生的,默认大小是是多少
class Turtle(RawTurtle):
"""RawTurtle auto-creating (scrolled) canvas.
When a Turtle object is created or a function derived from some
Turtle method is called a TurtleScreen object is automatically created.
"""
_pen = None
_screen = None
def __init__(self,
shape=_CFG["shape"],
undobuffersize=_CFG["undobuffersize"],
visible=_CFG["visible"]):
if Turtle._screen is None:
Turtle._screen = Screen()
RawTurtle.__init__(self, Turtle._screen,
shape=shape,
undobuffersize=undobuffersize,
visible=visible)
在构建Turtle对象的时候,可以发现调用了Screen()方法完成对Turtle._screen属性的赋值
def Screen():
"""Return the singleton screen object.
If none exists at the moment, create a new one and return it,
else return the existing one."""
if Turtle._screen is None:
Turtle._screen = _Screen()
return Turtle._screen
在Screen方法中,我们可以发现,他是构造_Screen类来完成赋值的,其类代码如下
class _Screen(TurtleScreen):
_root = None
_canvas = None
_title = _CFG["title"]
def __init__(self):
# XXX there is no need for this code to be conditional,
# as there will be only a single _Screen instance, anyway
# XXX actually, the turtle demo is injecting root window,
# so perhaps the conditional creation of a root should be
# preserved (perhaps by passing it as an optional parameter)
if _Screen._root is None:
_Screen._root = self._root = _Root()
self._root.title(_Screen._title)
self._root.ondestroy(self._destroy)
if _Screen._canvas is None:
width = _CFG["width"]
height = _CFG["height"]
canvwidth = _CFG["canvwidth"]
canvheight = _CFG["canvheight"]
leftright = _CFG["leftright"]
topbottom = _CFG["topbottom"]
self._root.setupcanvas(width, height, canvwidth, canvheight)
_Screen._canvas = self._root._getcanvas()
TurtleScreen.__init__(self, _Screen._canvas)
self.setup(width, height, leftright, topbottom)
.....
在这个类中几个属性
1. width = _CFG[“width”]
2. height = _CFG[“height”]
3. canvwidth = _CFG[“canvwidth”]
4. canvheight = _CFG[“canvheight”]
这四个属性就是我们屏幕和画布相关的几个属性,其值是字典_CFG
_CFG = {"width" : 0.5, # Screen
"height" : 0.75,
"canvwidth" : 400,
"canvheight": 300,
"leftright": None,
"topbottom": None,
"mode": "standard", # TurtleScreen
"colormode": 1.0,
"delay": 10,
"undobuffersize": 1000, # RawTurtle
"shape": "classic",
"pencolor" : "black",
"fillcolor" : "black",
"resizemode" : "noresize",
"visible" : True,
"language": "english", # docstrings
"exampleturtle": "turtle",
"examplescreen": "screen",
"title": "Python Turtle Graphics",
"using_IDLE": False
}
字典_CFG相关key,value的解释如下
1. “width” : 0.5 : 绘画的屏幕的宽度占据主屏幕宽的一半
2. “height” : 0.75 绘画的屏幕的高度占据主屏幕高的一半
3. “canvwidth” : 400 默认画布的宽度是400像素
4. “canvheight”: 300 默认画布的高度是300像素
5. “title”: “Python Turtle Graphics” 默认的绘制图形的标题是Python Turtle Graphics
通过上面对问题的解答,相信大家对turtle有了个更深刻的认识,那我们该如何实现文章最开始的ui
分析如下
1. 首先获得屏幕的宽和高 width 和 height
2. 蓝色长方形的顶点坐标应该是(-width - 30,height);这里应该注意到蓝色长方形的顶点坐标不是(-30,15),这是因为turtle采用的是直角坐标系,而不像Android里面,采用的是左手坐标系,android里面view的原点在屏幕的左上角
3. 通过goto的方法将绘制的起点放到(-width - 30,height)
4. 通过计算文字的坐标位置,调用write的方法完成整个图形的绘制
# coding:utf-8
# 引入turtle外部库,turtle是python中一个绘制图像的外部库
import turtle
# 画长方形的画笔
rectangleTurtle = turtle.Turtle();
# 画文字的画笔
# fontT = turtle.Turtle();
# 行数
rows = 10;
# 列数
columns = 9;
def iniTurtle():
# 隐藏箭头
rectangleTurtle.hideturtle();
rectangleTurtle.penup();
rectangleTurtle.getscreen().screensize(600, 400, "white");
rectangleTurtle.color('blue')
# 通过global可以管理修改全局变量,在函数里定义的变量是局部的,如果此处没有global,相当于新创建了一个变量screenWidth
global screenWidth;
screenWidth = rectangleTurtle.getscreen().getcanvas().canvwidth;
print screenWidth;
global screenHeight;
screenHeight = rectangleTurtle.getscreen().getcanvas().canvwidth;
print screenHeight;
global interval;
interval = 15;
global rectangleHeight;
rectangleHeight = 50;
global startX;
startX = screenWidth / 2 - interval;
print startX;
global startY;
startY = screenHeight / 2 - interval;
print startY;
rectangleTurtle.goto(-startX, startY);
rectangleTurtle.pendown();
# 通过penup提笔和pendown落笔这样就可以指定开始绘图的起始位置了,如果不使用penup和pendown方法,
# 那么默认的就会画一条从原点到goto方法指定参数的一条连线
def drawRectangle():
rectangleTurtle.forward(screenWidth - interval * 2);
rectangleTurtle.right(90);
rectangleTurtle.forward(rectangleHeight);
rectangleTurtle.right(90);
rectangleTurtle.forward(screenWidth - interval * 2);
rectangleTurtle.right(90);
rectangleTurtle.forward(rectangleHeight);
rectangleTurtle.penup();
rectangleTurtle.goto(-startX + interval * 20, startY - interval * 2.5);
rectangleTurtle.pendown();
rectangleTurtle.write("九九乘法口诀表", False, "center");
iniTurtle();
drawRectangle();
turtle.done();