Python turtle库实现基本剖析
有關(guān)turtle的相關(guān)使用請參考《python圖形繪制庫turtle中文開發(fā)文檔及示例大全》
本篇文為turtle庫的實現(xiàn)剖析,但不涉及 python 的 TK庫。
開始
入口探尋
在turtle中,直走是使用 forward 或者 fd 函數(shù);在本機安裝好了 turtle 庫后,在以下的目錄下找到了 turtle.py 文件:
我們先從常規(guī)的方式從入口開始探究turtle庫的基本實現(xiàn);新建一個turtle對象:
在文件中找到 class Turtle:
class Turtle(RawTurtle):"""RawTurtle auto-creating (scrolled) canvas.When a Turtle object is created or a function derived from someTurtle method is called a TurtleScreen object is automatically created."""_pen = None_screen = Nonedef __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)從注釋中可以的到此類將會自動創(chuàng)建 TurtleScreen 對象以及 canvas,這一點在 __init__ 方法中有代碼過程;之后調(diào)用了 RawTurtle 的 __init__ 創(chuàng)建 turtle的動畫部分,實現(xiàn)如下:
screens = []def __init__(self, canvas=None,shape=_CFG["shape"],undobuffersize=_CFG["undobuffersize"],visible=_CFG["visible"]):if isinstance(canvas, _Screen):self.screen = canvaselif isinstance(canvas, TurtleScreen):if canvas not in RawTurtle.screens:RawTurtle.screens.append(canvas)self.screen = canvaselif isinstance(canvas, (ScrolledCanvas, Canvas)):for screen in RawTurtle.screens:if screen.cv == canvas:self.screen = screenbreakelse:self.screen = TurtleScreen(canvas)RawTurtle.screens.append(self.screen)else:raise TurtleGraphicsError("bad canvas argument %s" % canvas)screen = self.screenTNavigator.__init__(self, screen.mode())TPen.__init__(self)screen._turtles.append(self)self.drawingLineItem = screen._createline()self.turtle = _TurtleImage(screen, shape)self._poly = Noneself._creatingPoly = Falseself._fillitem = self._fillpath = Noneself._shown = visibleself._hidden_from_screen = Falseself.currentLineItem = screen._createline()self.currentLine = [self._position]self.items = [self.currentLineItem]self.stampItems = []self._undobuffersize = undobuffersizeself.undobuffer = Tbuffer(undobuffersize)self._update()創(chuàng)建完一個turtle對象后,調(diào)用一下 forward 函數(shù)畫一根線段。
我們打開 turtle 文件,按照一般形式的函數(shù)定義,查詢 forward 函數(shù)的定義:
從注釋中了解到,調(diào)用函數(shù)可以使用 forward | fd ,參數(shù)為傳入一個距離;具體使用方法請參考文章頭標(biāo)注的文章,在這里并不做太多解釋。
在 forward 函數(shù)底部,發(fā)現(xiàn)調(diào)用了 _go 方法:self._go(distance) 。查看 _go 方法:
def _go(self, distance):"""move turtle forward by specified distance"""ende = self._position + self._orient * distanceself._goto(ende)在 _go 方法中,傳入了 距離,并且 ende 賦值為 self._position + self._orient * distance ,先搞懂 _position 、_orient 、distance 這幾個成員是什么東西。
_go 方法位于 TNavigator 類中,在 TNavigator 的 init 方法中,使用了 reset 方法,reset方法中有 _position 、_orient 的初始化:
def reset(self):"""reset turtle to its initial valuesWill be overwritten by parent class"""self._position = Vec2D(0.0, 0.0)self._orient = TNavigator.START_ORIENTATION[self._mode]我們再查看 Vec2D :
class Vec2D(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 additiona-b vector subtractiona*b inner productk*a and a*k multiplication with scalar|a| absolute value of aa.rotate(angle) rotation"""def __new__(cls, x, y):return tuple.__new__(cls, (x, y))def __add__(self, other):return Vec2D(self[0]+other[0], self[1]+other[1])def __mul__(self, other):if isinstance(other, Vec2D):return self[0]*other[0]+self[1]*other[1]return Vec2D(self[0]*other, self[1]*other)def __rmul__(self, other):if isinstance(other, int) or isinstance(other, float):return Vec2D(self[0]*other, self[1]*other)def __sub__(self, other):return Vec2D(self[0]-other[0], self[1]-other[1])def __neg__(self):return Vec2D(-self[0], -self[1])def __abs__(self):return (self[0]**2 + self[1]**2)**0.5def rotate(self, angle):"""rotate self counterclockwise by angle"""perp = Vec2D(-self[1], self[0])angle = angle * math.pi / 180.0c, s = math.cos(angle), math.sin(angle)return Vec2D(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查看 Vec2D 后得知,其實也就是一個元組;在查看 TNavigator 類:
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 = 0DEFAULT_ANGLEORIENT = 1隨后查看 TNavigator.START_ORIENTATION[self._mode],在 TNavigator 類中得知 _mode 為 standard。此時 TNavigator.START_ORIENTATION[self._mode] 為 Vec2D(1.0, 0.0)。
接下來查看 _goto 方法:
def _goto(self, end):"""Move the pen to the point end, thereby drawing a lineif pen is down. All other methods for turtle movement dependon this one."""## Version with undo-stuffgo_modes = ( self._drawing,self._pencolor,self._pensize,isinstance(self._fillpath, list))screen = self.screenundo_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._positionif self._speed and screen._tracing == 1:diff = (end-start)diffsq = (diff[0]*screen.xscale)**2 + (diff[1]*screen.yscale)**2nhops = 1+int((diffsq**0.5)/(3*(1.1**self._speed)*self._speed))delta = diff * (1.0/nhops)for n in range(1, nhops):if n == 1:top = Trueelse:top = Falseself._position = start + delta * nif 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 currentLineself.currentLine.append(end)if isinstance(self._fillpath, list):self._fillpath.append(end)###### vererbung!!!!!!!!!!!!!!!!!!!!!!self._position = endif self._creatingPoly:self._poly.append(end)if len(self.currentLine) > 42: # 42! answer to the ultimate question# of life, the universe and everythingself._newLine()self._update() #count=True)在 goto_方法中,最開頭的注釋說明了該方法的作用“從當(dāng)前的位置移動到傳入的end參數(shù)坐標(biāo)點,在移動的過程中,繪制出線段,并且所有的 turtle 繪制方法都基于這個 goto_方法”。goto_方法中,開始定義了一個元組 go_modes :
go_modes = ( self._drawing,self._pencolor,self._pensize,isinstance(self._fillpath, list))在go_modes 元組中,傳入了 _drawing、_pencolor、_pensize,并且調(diào)用了 isinstance 方法判斷 _fillpath 是否為 list;并且接下來構(gòu)造了一個 undo_entry元組。判斷 if self.undobuffer: 后,為空或者Null 則 self.undobuffer.push(undo_entry)。之后為默認(rèn)狀態(tài)下的繪制方法:
if self._speed and screen._tracing == 1:diff = (end-start)diffsq = (diff[0]*screen.xscale)**2 + (diff[1]*screen.yscale)**2nhops = 1+int((diffsq**0.5)/(3*(1.1**self._speed)*self._speed))delta = diff * (1.0/nhops)for n in range(1, nhops):if n == 1:top = Trueelse:top = Falseself._position = start + delta * nif 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)其中 start為當(dāng)前位置的坐標(biāo)點,end是目標(biāo)位置的坐標(biāo)點,以上最主要的方法中最重要是:_drawline,使用_drawline傳入了配置參數(shù)、坐標(biāo)序列、筆顏色、繪制線的寬度以及 是否指定 polyitem;(具體坐標(biāo)序列的算法我沒搞清楚,希望有知道的同學(xué)可以告訴我這是咋算的,是什么公式,謝謝!)。
查看 _drawline 的實現(xiàn):
def _drawline(self, lineitem, coordlist=None,fill=None, width=None, top=False):"""Configure lineitem according to provided arguments:coordlist is sequence of coordinatesfill is drawing colorwidth is width of drawn line.top is a boolean value, which specifies if polyitemwill be put on top of the canvas' displaylist so itwill 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)以上文章暫未全部剖析實現(xiàn),之后將會更新。
總結(jié)
以上是生活随笔為你收集整理的Python turtle库实现基本剖析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 「零门槛多语言 Python/C/C#
- 下一篇: python thinker canva