For the related usage of turtle, please refer to ["Python graphics drawing library turtle Chinese development documentation and examples"] (https://blog.csdn.net/A757291228/article/details/105884899)
This article is an analysis of the realization of the turtle library, but does not involve the python TK library.
In turtle, the forward or fd function is used to go straight; after the turtle library is installed on this machine, the turtle.py file is found in the following directory:
Let's start by exploring the basic implementation of the turtle library from the entrance in a conventional way; create a new turtle object:
tt=Turtle()
Find class Turtle in the file:
classTurtle(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)
It can be seen from the comments that this class will automatically create the TurtleScreen
object and canvas
, this is in the code process in the __init__
method; after that, the __init__
of RawTurtle
is called to create the animation part of the turtle, and it is realized as follows:
screens =[]
def __init__(self, canvas=None,
shape=_CFG["shape"],
undobuffersize=_CFG["undobuffersize"],
visible=_CFG["visible"]):ifisinstance(canvas, _Screen):
self.screen = canvas
elif isinstance(canvas, TurtleScreen):if canvas not in RawTurtle.screens:
RawTurtle.screens.append(canvas)
self.screen = canvas
elif isinstance(canvas,(ScrolledCanvas, Canvas)):for screen in RawTurtle.screens:if screen.cv == canvas:
self.screen = screen
breakelse:
self.screen =TurtleScreen(canvas)
RawTurtle.screens.append(self.screen)else:
raise TurtleGraphicsError("bad canvas argument %s"% canvas)
screen = self.screen
TNavigator.__init__(self, screen.mode())
TPen.__init__(self)
screen._turtles.append(self)
self.drawingLineItem = screen._createline()
self.turtle =_TurtleImage(screen, shape)
self._poly = None
self._creatingPoly = False
self._fillitem = self._fillpath = None
self._shown = visible
self._hidden_from_screen = False
self.currentLineItem = screen._createline()
self.currentLine =[self._position]
self.items =[self.currentLineItem]
self.stampItems =[]
self._undobuffersize = undobuffersize
self.undobuffer =Tbuffer(undobuffersize)
self._update()
After creating a turtle object, call the forward function to draw a line segment.
We open the turtle file and query the definition of the forward function according to the general form of the function definition:
I understand from the comments that forward | fd can be used to call the function, and the parameter is to pass in a distance; please refer to the article marked at the head of the article for specific usage, and I will not explain too much here.
At the bottom of the forward function, it is found that the _go method is called: self._go(distance)
. View the _go method:
def _go(self, distance):"""move turtle forward by specified distance"""
ende = self._position + self._orient * distance
self._goto(ende)
In the _go method, the distance is passed in, and the ende is assigned to self._position + self._orient * distance
. First, understand what the _position, _orient, and distance members are.
_ The go method is located in the TNavigator class. In the init method of TNavigator, the reset method is used. The reset method has the initialization of _position and _orient:
def reset(self):"""reset turtle to its initial values
Will be overwritten by parent class"""
self._position =Vec2D(0.0,0.0)
self._orient = TNavigator.START_ORIENTATION[self._mode]
Let's look at Vec2D again:
classVec2D(tuple):"""A 2 dimensional vector class, used as a helper classfor implementing turtle graphics.
May be useful for turtle graphics programs also.
Derived from tuple, so a vector is a tuple!Provides(for a, b vectors, k number):
a+b vector addition
a-b vector subtraction
a*b inner product
k*a and a*k multiplication with scalar
| a| absolute value of a
a.rotate(angle) rotation
"""
def __new__(cls, x, y):return tuple.__new__(cls,(x, y))
def __add__(self, other):returnVec2D(self[0]+other[0], self[1]+other[1])
def __mul__(self, other):ifisinstance(other, Vec2D):return self[0]*other[0]+self[1]*other[1]returnVec2D(self[0]*other, self[1]*other)
def __rmul__(self, other):ifisinstance(other, int) or isinstance(other, float):returnVec2D(self[0]*other, self[1]*other)
def __sub__(self, other):returnVec2D(self[0]-other[0], self[1]-other[1])
def __neg__(self):returnVec2D(-self[0],-self[1])
def __abs__(self):return(self[0]**2+ self[1]**2)**0.5
def rotate(self, angle):"""rotate self counterclockwise by angle
"""
perp =Vec2D(-self[1], self[0])
angle = angle * math.pi /180.0
c, s = math.cos(angle), math.sin(angle)returnVec2D(self[0]*c+perp[0]*s, self[1]*c+perp[1]*s)
def __getnewargs__(self):return(self[0], self[1])
def __repr__(self):return"(%.2f,%.2f)"% self
After looking at Vec2D, we know that it is actually a tuple; looking at the TNavigator class:
classTNavigator(object):"""Navigation part of the RawTurtle.
Implements methodsfor 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
Then check TNavigator.START_ORIENTATION[self._mode]
, and learn that _mode
is standard
in the TNavigator
class. At this time, TNavigator.START_ORIENTATION[self._mode]
is Vec2D(1.0, 0.0)
.
Next look at the _goto method:
def _goto(self, end):"""Move the pen to the point end, thereby drawing a line
if pen is down. All other methods for turtle movement depend
on this one."""
## Version with undo-stuff
go_modes =( self._drawing,
self._pencolor,
self._pensize,isinstance(self._fillpath, list))
screen = self.screen
undo_entry =("go", self._position, end, go_modes,(self.currentLineItem,
self.currentLine[:],
screen._pointlist(self.currentLineItem),
self.items[:]))if self.undobuffer:
self.undobuffer.push(undo_entry)
start = self._position
if self._speed and screen._tracing ==1:
diff =(end-start)
diffsq =(diff[0]*screen.xscale)**2+(diff[1]*screen.yscale)**2
nhops =1+int((diffsq**0.5)/(3*(1.1**self._speed)*self._speed))
delta = diff *(1.0/nhops)for n inrange(1, nhops):if n ==1:
top = True
else:
top = False
self._position = start + delta * n
if self._drawing:
screen._drawline(self.drawingLineItem,(start, self._position),
self._pencolor, self._pensize, top)
self._update()if self._drawing:
screen._drawline(self.drawingLineItem,((0,0),(0,0)),
fill="", width=self._pensize)
# Turtle now at end,if self._drawing: # now update currentLine
self.currentLine.append(end)ifisinstance(self._fillpath, list):
self._fillpath.append(end)
###### vererbung!!!!!!!!!!!!!!!!!!!!!!
self._position = end
if self._creatingPoly:
self._poly.append(end)iflen(self.currentLine)>42: # 42! answer to the ultimate question
# of life, the universe and everything
self._newLine()
self._update() #count=True)
In the goto_ method, the first comment explains the function of the method "Move from the current position to the incoming end parameter coordinate point, in the process of moving, draw the line segment, and all turtle drawing methods are based on this goto_method". In the goto_ method, a tuple go_modes is defined at the beginning:
go_modes =( self._drawing,
self._pencolor,
self._pensize,isinstance(self._fillpath, list))
In the go_modes tuple, _drawing, _pencolor, and _pensize are passed in, and the isinstance method is called to determine whether _fillpath is a list; and then an undo_entry tuple is constructed. After judging if self.undobuffer:
, if it is empty or Null, then self.undobuffer.push(undo_entry)
. Then, the drawing method in the default state:
if self._speed and screen._tracing ==1:
diff =(end-start)
diffsq =(diff[0]*screen.xscale)**2+(diff[1]*screen.yscale)**2
nhops =1+int((diffsq**0.5)/(3*(1.1**self._speed)*self._speed))
delta = diff *(1.0/nhops)for n inrange(1, nhops):if n ==1:
top = True
else:
top = False
self._position = start + delta * n
if self._drawing:
screen._drawline(self.drawingLineItem,(start, self._position),
self._pencolor, self._pensize, top)
self._update()if self._drawing:
screen._drawline(self.drawingLineItem,((0,0),(0,0)),
fill="", width=self._pensize)
Among them, start is the coordinate point of the current position, and end is the coordinate point of the target position. The most important of the above main methods is: _drawline
, using _drawline
to pass in the configuration parameters, coordinate sequence, pen color, and drawing line Width and whether to specify polyitem
; (I haven't figured out the algorithm of the specific coordinate sequence. I hope some students who know can tell me what the calculation is and what the formula is, thank you!).
View the implementation of _drawline:
def _drawline(self, lineitem, coordlist=None,
fill=None, width=None, top=False):"""Configure lineitem according to provided arguments:
coordlist is sequence of coordinates
fill is drawing color
width is width of drawn line.
top is a boolean value, which specifies if polyitem
will be put on top of the canvas' displaylist so it
will not be covered by other items."""
if coordlist is not None:
cl =[]for x, y in coordlist:
cl.append(x * self.xscale)
cl.append(-y * self.yscale)
self.cv.coords(lineitem,*cl)if fill is not None:
self.cv.itemconfigure(lineitem, fill=fill)if width is not None:
self.cv.itemconfigure(lineitem, width=width)if top:
self.cv.tag_raise(lineitem)
**The above articles have not been fully analyzed and implemented, and will be updated later. **
Recommended Posts