Python turtle库实现基本剖析

有关turtle的相关使用请参考《python图形绘制库turtle中文开发文档及示例大全》

本篇文为turtle库的实现剖析,但不涉及 python 的 TK库。

开始

入口探寻

在turtle中,直走是使用 forward 或者 fd 函数;在本机安装好了 turtle 库后,在以下的目录下找到了 turtle.py 文件:
在这里插入图片描述
我们先从常规的方式从入口开始探究turtle库的基本实现;新建一个turtle对象:

tt=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)

从注释中可以的到此类将会自动创建 TurtleScreen 对象以及 canvas,这一点在 __init__ 方法中有代码过程;之后调用了 RawTurtle__init__ 创建 turtle的动画部分,实现如下:

	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()

创建完一个turtle对象后,调用一下 forward 函数画一根线段。

我们打开 turtle 文件,按照一般形式的函数定义,查询 forward 函数的定义:
在这里插入图片描述
从注释中了解到,调用函数可以使用 forward | fd ,参数为传入一个距离;具体使用方法请参考文章头标注的文章,在这里并不做太多解释。

在 forward 函数底部,发现调用了 _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 类中得知 _modestandard。此时 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_方法中,最开头的注释说明了该方法的作用“从当前的位置移动到传入的end参数坐标点,在移动的过程中,绘制出线段,并且所有的 turtle 绘制方法都基于这个 goto_方法”。goto_方法中,开始定义了一个元组 go_modes :

go_modes = ( self._drawing,self._pencolor,self._pensize,isinstance(self._fillpath, list))

在go_modes 元组中,传入了 _drawing、_pencolor、_pensize,并且调用了 isinstance 方法判断 _fillpath 是否为 list;并且接下来构造了一个 undo_entry元组。判断 if self.undobuffer: 后,为空或者Null 则 self.undobuffer.push(undo_entry)。之后为默认状态下的绘制方法:

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为当前位置的坐标点,end是目标位置的坐标点,以上最主要的方法中最重要是:_drawline,使用_drawline传入了配置参数、坐标序列、笔颜色、绘制线的宽度以及 是否指定 polyitem;(具体坐标序列的算法我没搞清楚,希望有知道的同学可以告诉我这是咋算的,是什么公式,谢谢!)。

查看 _drawline 的实现:

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)

以上文章暂未全部剖析实现,之后将会更新。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/288098.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

分享我做Dotnet9博客网站时积累的一些资料

从2019年使用WordPress搭建Dotnet9网站,到现在手撸代码开发,介绍中间使用的一些资源,绝无保留,希望对大家有用。1. 申请域名、搭建WordPress网站时间点:2019年11月申请Dotnet9域名,讲个实话,站长…

基于Azure Blob冷存储的数据压缩备份总结

基于上一篇的压缩算法对比分析报告,选择了LZ4算法的普通模式,其测试压缩率为28%,20G压缩时间为256s,估计1T的冷备时间为3.5h。 接下来,将23T的HBase历史数据进行了压缩冷备,压缩后大小为3.5T,冷…

Ubuntu使用VNC运行基于Docker的桌面系统

2019独角兽企业重金招聘Python工程师标准>>> docker-ubuntu-vnc-desktop From Docker Index docker pull dorowu/ubuntu-desktop-lxde-vnc Build yourself git clone https://github.com/fcwu/docker-ubuntu-vnc-desktop.git docker build --rm -t dorowu/ubuntu-de…

【ArcGIS风暴】ArcGIS个人数据库(.mdb)中矢量字段(如Shape_Length、Shape_Area)无法删除的解决办法

ArcGIS中,某些操作,比如空间连接(Spatial Join)结果属性表中会自动产生Shape_Length和Shape_Area字段,这些字段无法删除,如下: 即使在工具箱中的【删除字段】工具,也找不到这些字段,如图所示,给我们的数据入库工作带来了一定的困扰。 解决思路: mdb是个人数据库,基…

python thinker canvas create_arc 使用详解

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.csdn.net/A757291228/article/details/106739556 ———————————————— 版权声明:本文…

剑指offer之二叉搜索树和双向链表

1 问题 比如我们搜索二叉树如下,我们需要变成双向链表 2 分析 我们知道这个变成双向链接的时候是按照树的中序遍历打印的,我们只需要在中序遍历打印的时候操作该节点,我们可以用临时变量保存这个节点,同时我们也需要单独增加一…

text 热敏打印机_只要有想象力,打印机都能做游戏

不知道为什么,文本冒险游戏最近频频被人提及。这不,现在又出来一个基于实时打印的文本冒险游戏 —— Quest Smith。这位脑洞较大的创客名叫 Bekir Dağ ,他用微型打印机和树莓派做出了这个手持游戏设备。关于文本冒险游戏的背景知识&#xff…

C#内建接口:IConvertible

这节来讲一个比较有意思的接口:IConvertible。IConvertible接口处于System.Runtime命名空间下,这个接口规定了一批ToXxx()方法,凡是实现了这个接口的方法,我们都可以尝试将其转换为自己想要的类型。IConvertible中有什么&#xff…

ArcGIS升级地理数据库

本博文教大家如何快速地将低版本地理数据库升级为高版本地理数据库。 方法一:使用“升级地理数据库”工具箱二、使用ArcCatalog地理数据库→右键→属性。三、创建不同版本的地理数据库

汉高软件服务器安装系统,如何安装了如指掌眼镜管理系统的服务器和客户端,还需要安装什么软件的?...

满意答案ven85202014.05.09采纳率:53% 等级:7已帮助:356人易软眼镜店管理系统它整合了整个眼镜店的收费、客户登记、验光单管理、配镜管理、财务管理和查旬报表以及库存管理一体化的管理系统。随时可以查看客户的验光单、对比,…

Python3 实现单例设计模式

单例模式的一般实现 饿汉式 懒汉式就是通过一个方法才能实现单例,我不是很常用,所以在此就写个饿汉式。以后再补懒汉式。 单例模式的核心作用是保证一个类只有一个该类型的对象。在一个对象被过多调用时避免过多的消耗内存,即可使用单例模式…

基于casbin的ABAC/RBAC权限实践

五一假期疫情封在家也没事做,就想来优化一下一个前端容器小项目之前的TODOlist里面有一项是权限这块时隔2年了还一直没有动手迟迟没搞主要还是我太懒了,哈哈 其实我一直想要找一个轻量级的权限通用方案权限的数据源可以切换,但是逻辑基本不用…

Python3 实现建造者模式

建造者模式 建造者模式用于创建复杂的对象。使用建造者模式可以使复杂的过程层次明了、清晰,把对象的创建以及使用进行了解耦。实际上从代码的角度上看,是进行了多次封装,使代码结构更为规范合理,层次结构更加鲜明。 在一个复杂…

系列网络服务器机柜,什么是网络机柜 网络机柜和服务器机柜有哪些区别【详解】...

【网络机柜】什么是网络机柜 网络机柜和服务器机柜区别服务器机柜和网络机柜的区别服务器机柜 :用来组合安装面板、插件、插箱、电子元件、器件和机械零件与部件,使其构成一个整体的安装箱。可以配置:专用固定托盘、专用滑动托盘、电源插排、…

【3D Max】3D max如何删除环境贴图

问题描述:在用3dm max贴图的时候,如果不选中对象,很容易将图贴到背景环境中去,情况如下所示: 解决办法有二: 一、不参与渲染 快捷键8,在“环境和效果”窗口中去掉“使用贴图”前面的√。 二、…

C#语法糖系列 —— 第四篇:聊聊 Span 的底层玩法

把 Span 归于语法糖,可能有些偏了,但偏了就偏了,哈哈,只要是分享就好,C# 发展至今,已经是一门非常重的语言了,所有想要的它都要,即可以:面向过程编程面向对象编程面向函数…

【ArcGIS风暴】河流水系左斜体样式经典设置方法

目录 一、效果预览 二、实现方法 一、效果预览 河流水系在作图时一般设置为左斜体、蓝色,如黄河、青海湖、洮河等,如下图所示: 二、实现方法 下面介绍在ArcGIS 10.5中的实现方法。 1、ArcGIS设置方法 绘图工具添加文字或者将标注转为注记,双击,打开属性,点击更改符号…

HBase简介(很好的梳理资料)

HBase HBaseHadoop网络应用数据结构NoSQL 一、 简介 history started by chad walters and jim 2006.11 G release paper on BigTable 2007.2 inital HBase prototype created as Hadoop contrib 2007.10 First useable Hbase 2008.1 Hadoop become Apache top-level project …

python 实现原型设计模式

原型设计模式主要在当新建一个对象的时候,觉得很麻烦,并且你又要保留当前对象。在这种情况下使用原型设计模式是一个很好的解决办法。 例如你写一个东西更新,不同的版本,这个时候以前的版本肯定要保留,并且从此基础上…

装服务器显示磁盘脱机,服务器磁盘处于脱机

服务器磁盘处于脱机 内容精选换一换配置目的端或启动目的端时提示“SMS.1311 目的端磁盘个数不够”。在配置目的端服务器过程中,会校验目的端磁盘数量是否和源端一致。当出现该错误时,检查目的端服务器磁盘数量是否少于源端服务器磁盘数量,或…