当前位置: 首页 > 后端技术 > Python

使用pythonTurtle库绘制一棵“精致花香小清新风格树”

时间:2023-03-26 00:13:02 Python

Turtle库手册可查询python图形绘图库turtle中文开发文档及实例。手册中有现成的例子,不用自己动手也可以看demo。用Turtle画树,看了一下网上的代码。基本上,核心方法是使用递归;其次,通过递归传递参数来改变笔的粗细来改变绘制时的线段,改变树的树干大小。遍历到最后一个节点时,改变笔的颜色和粗细,画出树顶的花瓣或绿叶。这篇博文是根据网上的代码修改而来的。基本上递归都是用来写简单的写法的,就拿过来用了。本来想顺便画一下环境的,但是明天还要上班,今天还有别的事情要做,只能作罢,有空再写个案例让环境变好;下面是最终代码运行后的结果(代码没有优化,效率可能不是很好):感觉有点小清新,哈哈哈,颜色是有意搭配的,碎花风格!好了,现在开始看怎么写。最后的代码在最下面,上面是对遇到的一些问题的一步一步的解释。首先,我们用最简单的方式画一个树干,代码如下:fromturtleimport*left(80)fd(100)right(30)fd(30)right(30)fd(40)input()结果如下:上面的代码先用t.left(80)把海龟转到几乎垂直于水平线的位置,然后用t.fd(100)在里面画一条100个单位的线段切割头指向的方向,然后t.right(30)向右转30度,然后t.fd(30)向前画30个单位的线段;此时线段之间有一个节点,是右转角度生成的,模拟树干;最后t。right(30)向右转30度,t.fd(40)向前绘制40个单位长度的线段以延伸最终躯干。不过上面的线段并不适合把树描述成植物,不过别着急,我们先改变画出的线段的粗细和颜色,让画出的画面看起来更像一棵树。color|pensize()通过t.pensize()和t.color()改变笔的粗细和颜色,代码如下:fromturtleimport*color('#5E5E5E')pensize(10)left(80)fd(100)right(30)fd(30)right(30)fd(40)input()改变笔的粗细后,绘制结果如下:没关系,我们慢慢改代码;现在让我们加长树的树枝并减少笔的粗细:fromturtleimport*color('#5E5E5E')pensize(6)left(80)fd(150)right(30)fd(50)right(30)fd(60)input()的结果如下:看起来好多了,整个主干部分可以分为主干和散支,我们可以分几个部分来写;先画树干,再用函数画树枝:fromturtleimport*defdrawTree(length):right(20)fd(length)color('#5E5E5E')pensize(5)up()goto(0,-300)#跳转到向下绘制的起点()left(80)fd(140)drawTree(60)input()运行结果如下:此时代码开始从绘制最多的区域底部开始绘制,使用color('#5E5E5E')设置绘制颜色;pensize(5)设置绘制线段的粗细;goto(0,-300)跳转到绘图区域的下半部分为起点;然后左转80度,画一条线段作为树干;然后调用函数drawTree(120)传入长度来绘制树枝。drawTree函数中right(20)向右转20度,fd(length)画一条线段作为分支。现在函数写好了,我们可以使用递归开始绘制树枝了:fromturtleimport*defdrawTree(length):iflength>1:right(20)fd(length)drawTree(length-10)color('#5E5E5E')pensize(5)up()goto(0,-300)#跳转到绘图起点down()left(80)fd(140)drawTree(60)input()运行结果如下:从代码上看,只修改drawTree函数部分的代码内容;函数中使用了递归,递归后传入的值为当前长度减去10个长度,函数中判断只有长度大于1才会执行,从而防止递归没有死循环由转义条件产生。当前长度有一个固定的差异。使用随机数让绘制的树枝长度随机,更接近真实的树枝(这里只贴出修改过的部分代码):defdrawTree(length):iflength>1:right(20)fd(length)randlen=random.random()drawTree(length-10*randlen)运行结果:代码使用随机数,将随机值减去固定差10,得到值后进行减法运算。所以这里,旋转角度也是固定的,我们来随机化旋转角度:defdrawTree(length):iflength>1:randangle=random.random()randlen=random.random()right(20*randangle)fd(length)drawTree(length-10*randlen)运行结果:正式地,我们的分支现在只有一个方向,即向右,我们现在添加左分支绘制:fromturtleimport*importrandomdefdrawTree(length):iflength>1:randangle=random.random()randlen=random.random()right(20*randangle)fd(length)drawTree(length-10*randlen)left(40*randangle)fd(length)color('#5E5E5E')pensize(5)up()goto(0,-300)#跳转到绘图起点down()left(80)fd(140)drawTree(60)input()但是出了问题:为什么会这样?那是因为我们需要跳转到上一个绘制位置,就用backward函数,把drawTree函数改成如下:defdrawTree(length):iflength>1:randangle=random.random()randlen=random.random()right(20*randangle)fd(length)up()backward(length)down()left(40*randangle)fd(length)drawTree(length-10*randlen)结果如下:其实这个效果还是不错的,只是不是我们想要的。这个效果可能对画狗尾草有好处;更改代码:fromturtleimport*importrandomdefdrawTree(length):iflength>1:#随机角度和长度randangle=random.random()randlen=random.random()#每次使用函数,画一条线先分段,再调整角度,这里是右转角fd(length)right(20*randangle)drawTree(length-10*randlen)#这里是左转角left(40*randangle)drawTree(length-10*randlen)#为什么要向右转20度?那是因为我总共向左转了40度。使用backward回退的时候,一定要是同一个角度,不然我回退的时候角度不一样。right(20*randangle)up()backward(length)down()tracer(False)color('#5E5E5E')pensize(5)up()goto(0,-300)#跳转到向下绘制的起点()left(80)fd(140)drawTree(60)input()result如下:代码解释在注释里,是一个简单的递归,但是因为传入的长度不长,绘制的线段树枝不多,会导致整棵树长得不够茂盛,我们修改一下传入的length,改成120,绘制结果如下。注意,因为画的太长了,可以直接使用tracer(False)直接显示效果,就不用画一次渲染一次了(这个要看你的参数,改成False就可以了);这里要改变坐标系,自定义成比当前坐标系更大的坐标系,否则屏幕显示不全,使用代码setworldcoordinates(-1000,-750,1000,750)即对,整体代码如下:fromturtleimport*importrandomdefdrawTree(length):iflength>1:#随机角度和长度randangle=random.random()randlen=random.random()#每隔一段时间画一条线段当你使用函数时,再次调整角度,这里是右转角fd(length)right(20*randangle)drawTree(length-10*randlen)#这里是左转角left(40*randangle)drawTree(length-10*randlen)#为什么我们需要再右转20度?那是因为我总共向左转了40度。使用backward回退的时候,一定要是同一个角度,不然我回退的时候角度不一样。Right(20*randangle)up()backward(length)down()setworldcoordinates(-1000,-750,1000,750)tracer(False)color('#5E5E5E')pensize(5)up()goto(0,-300)#跳转到绘图起点down()left(80)fd(140)drawTree(120)input()的结果如下:但是,由于随机数的角和长度分支不限制最小长度,可以随机乘一个合适的数,再往下调整起始位置,代码如下:randangle=2*random.random()randlen=2*random.random()......goto(0,-700)结果如下:大致的树形已经绘制完成,现在,填充树枝的绿色和红色花朵;一般来说,树的树枝越靠近顶端,越小,判断长度的大小,如果在一定范围内是绿色,在一定范围内是红色,就可以模拟树开花的效果,树绿叶,代码如下,颜色代码可以自己调整:fromturtleimport*importrandomdefdrawTree(length):iflength>1:iflength<30andlength>14:#Reducethetrunkpensize(4)eliflength<15andlength>5:#长度在这个范围内,那么就是Greenleafcolor('#04B486')#pensize(3)eliflength<5andlength>1:#safflowercolor('#FE2E9A')pensize(2)else:color('#5E5E5E')#其他范围正常Thetrunkpensize(5)#随机角度和长度randangle=2*random.random()randlen=2*random.random()#每次使用函数,先画线段,再调整角度,这里是右转的角度fd(length)right(20*randangle)drawTree(length-10*randlen)#这里是左转的角度left(40*randangle)drawTree(length-10*randlen)#为什么要向右转20度?那是因为我总共向左转了40度。使用backward回退的时候,一定要是同一个角度,不然我回退的时候角度不一样。Right(20*randangle)up()backward(length)down()setworldcoordinates(-1000,-750,1000,750)tracer(False)color('#5E5E5e')pensize(5)up()goto(0,-700)#跳转到绘图起点down()left(80)fd(140)drawTree(120)input()运行结果如下:画落花树有点长郁郁葱葱,哈哈哈;每个随机树的形状都不一样,所以我不明白它不能放在一个屏幕上,但没关系,我们现在开始画落叶和花朵。落花函数如下:deffallingFlowers(m):x,y=-1000,-750yval=50foriinrange(m):a=100*random.random()b=2*random.random()print(a)如果a>59:color('#FE2E9A')else:color('#04B486')circle(5)up()goto(x,y+(yval*b))fd(a)yval+=50down()的工作原理如下:因为我们自己定义的坐标系是[-1000,-750]到[1000,750],我们从左下角开始画落花,取值x和y坐标的在代码中定义为x,y=-1000,-750,然后在循环中每次使用一个y值递增,每次绘制的位置从左下角往上,所以每次循环增加y坐标,给定随机乘法,这样会更好Random,每次都用goto(x,y+yval)跳转到指定的x,y坐标位置,不过这里需要注意的是x的值是不变的;并且在循环中我设置了一个Variable,这个变量a主要作为fd(a)在x轴上随机化,相同的值会导致相同的排列,不是很美观,随机的原因值b乘以yval也是一样的。再次修改代码,在外层放一个循环,加上x坐标值,水平填充绘图区域:fromturtleimport*importrandomdefdrawTree(length):iflength>1:iflength<30andlength>14:#shrinkthetrunkpensize(4)eliflength<15andlength>5:#这个范围的长度是绿叶颜色('#04B486')#pensize(3)eliflength<5andlength>1:#红花color('#FE2E9A')pensize(2)else:color('#5E5E5E')#其他范围为正常树干pensize(5)#随机角度和长度randangle=2*random.random()randlen=2*random.random()#每次使用函数,先画线段,然后调整角度,这里是直角旋转fd(length)right(20*randangle)drawTree(length-10*randlen)#这里是左转角度left(40*randangle)drawTree(length-10*randlen)#为什么要向右转20度?那是因为我总共向左转了40度。使用backward回退的时候,一定要是同一个角度,不然我回退的时候角度不一样。Right(20*randangle)up()backward(length)down()deffallingFlowers(m):x,y=-1000,-750foriinrange(30):up()goto(x,y)x+=100down()yval=50foriinrange(m):a=100*random.random()b=2*random.random()print(a)如果a>59:color('#FE2E9A')else:color('#04B486')circle(5)up()goto(x,y+(yval*b))fd(a)yval+=50down()setworldcoordinates(-1000,-750,1000,750)tracer(False)color('#5E5E5E')pensize(5)#up()#goto(0,-700)#跳转到起点ofdrawing#down()#left(80)#fd(140)#drawTree(120)fallingFlowers(10)input()操作结果:新加入的外层循环增加x的值,最后平铺下半部分绘图区最终结果结合树的绘制代码,打开注释,设置背景色bgcolor("#F5F6CE"):fromturtleimport*importrandomdefdrawTree(length):iflength>1:iflength<30andlength>14:#减少树干pensize(4)eliflength<15andlength>5:#长度在此范围内,则为绿叶色('#04B486')#pensize(3)eliflength<5andlength>1:#红花色('#FE2E9A')pensize(2)else:color('#5E5E5E')#其他范围为正常树干pensize(5)#随机角度和长度randangle=2*random.random()randlen=2*随机。random()#每次使用函数,先画线段,再调整角度,这里是直角旋转fd(length)right(20*randangle)drawTree(length-10*randlen)#这里是leftanglerotationleft(40*randangle)drawTree(length-10*randlen)#为什么要向右转20度?那是因为我总共向左转了40度。使用backward回退的时候,一定要是同一个角度,不然我回退的时候角度不一样。Right(20*randangle)up()backward(length)down()deffallingFlowers(m):x,y=-1000,-750foriinrange(30):up()goto(x,y)x+=100down()yval=50foriinrange(m):a=100*random.random()b=2*random.random()print(a)如果a>59:color('#FE2E9A')else:color('#04B486')circle(5)up()goto(x,y+(yval*b))fd(a)yval+=50down()setworldcoordinates(-1000,-750,1000,750)tracer(False)fallingFlowers(10)#draw落叶bgcolor("#F5F6CE")color('#5E5E5E')pensize(5)up()goto(0,-700)#跳转到绘图起点down()left(80)fd(140)drawTree(120)input()结果如下:如果你认为落叶落花不够,可以改一下直接点击参数,运行效果太慢是因为代码没有优化,优化后再贴上去。