Python:线条的魅力
简 介
相对冷冰冰的文字输出,图形的输出对于小朋友更有吸引力。线条配合上重复可以创造出各种赏心悦目的图形。
01 新的教材
《父与子的编程之旅》除了包括Python的基础知识之外,还包括了使用pygame模块的图形编程。只不过,pygame模块的使用对于准2年级小朋友而言,使用起来还是有些复杂。因此,turtle模块是一个不错的替代方案,除了可以练习数组循环等概念,学习坐标轴概念和基本的图形绘制基础,最重要的一点是,有视觉化的输出,对于小朋友的乐趣是文字输出远远比不上的。关于学习的顺序,我觉得先学《父与子的编程之旅》中的基础知识,再学《教孩子学编程》,最后再学《父与子》中的pygame编程。
这本书的基本学习思路是首先让小朋友照着书本把代码输入到电脑,其中的输入过程有助于小朋友练习对键盘按键的熟悉。另外,输入的过程或多或少会出现一些输入错误,在调试的过程中,培养了对代码的理解能力以及细心。在程序可以运行之后,让小朋友一行一行的解释代码作用,通过这个过程可以了解到小朋友的知识欠缺点。再之后,就可以让小朋友自己改改参数或者自己添加点代码来看看不同的图形输出效果。在此之后,便可以给小朋友出一些挑战,让小朋友自己修改代码解决挑战问题。
在这个阶段还是需要耐心。首先,小朋友在键盘上输入字母的速度还是不快。其次,小朋友从0到1打框架还是有些困难,不如在给小朋友框架之后,让小朋友添加或者修改代码来达到不同的效果,所以这个阶段的重点还是逻辑以及理解已有代码。
02 turtle模块
因为之前学过scratch,所以有助于理解turtle模块中的Pen对象。不过,即使没有学过scratch,Pen对象也比较容易理解。首先使用turtle.Pen()创建一个Pen的实例。Pen可以提起来penup,也可以落在屏幕上pendown。和现实中画画一样,要pendown把Pen放在纸上。另外,Pen有方向,用setheading来设置,向前移动使用forward,画圆则用circle。了解了这几个函数,就可以画出一些基本图形。如果改变Pen的颜色,则使用pencolor。另外,如果需要填充绘制出的图形的话,则需要使用begin_fill和end_fill函数。
下面几个图形中除了房子那个,剩余的图形都是小朋友输入的代码绘制的。小朋友的代码基本和书本上一致,或者稍稍改动了一些。
关于的房子的绘制,就是使用基本的penup,pendown,forward,circle,pencolor,fillcolor等来动态绘制的。难点是绘制每一个图形前都要计算该图形的绘制位置。下面是代码:
import turtleimport random#generate a 2 dimensional arraydef generate2array(height,width):return [[0,0],[width,0],[width,height],[0,height]]def move2arraybyXY(startposX,startposY,arr):return [[arr[i][0]+startposX,arr[i][1]+startposY]for iin range((len(arr)))]#heading=0 means drawing upwardsdef drawcircle(tpen,bottompos,radius,heading=0,color="black",width=1,fillcolor="none"): tpen.pencolor(color) tpen.width(width) tpen.penup() tpen.setposition(bottompos) tpen.setheading(heading) tpen.pendown()if fillcolor!="none": tpen.begin_fill() tpen.fillcolor(fillcolor) tpen.circle(radius) tpen.end_fill()else: tpen.circle(radius) tpen.penup()def drawray(tpen,centerpos,innerraidus,outradius,numofrays=8,color="black",width=1): tpen.pencolor(color) tpen.width(width)for iin range(numofrays): tpen.penup() tpen.setposition(centerpos) tpen.forward(innerraidus) tpen.pendown() tpen.forward(outradius) tpen.right(360/numofrays)def drawpoligonbypoints(tpen,color="black",width=1,fillcolor="none",*pos): tpen.pencolor(color) tpen.width(width) tpen.penup() tpen.setposition(pos[-1]) tpen.pendown()if fillcolor!="none": tpen.fillcolor(fillcolor) tpen.begin_fill()for iin range(len(pos)): tpen.setposition(pos[i]) tpen.end_fill()else:for iin range(len(pos)): tpen.setposition(pos[i]) tpen.penup()#moveangledistance, e.g. [60 (degree left turn), 100 (pixel distance)]def drawpoligonbyvector(tpen,pos,color="black",width=1,fillcolor="none",*moveangledistance): tpen.pencolor(color) tpen.width(width) tpen.penup() tpen.setposition(pos) tpen.setheading(0) tpen.pendown()if fillcolor!="none": tpen.fillcolor(fillcolor) tpen.begin_fill()for iin range(len(moveangledistance)): tpen.left(moveangledistance[i][0]) tpen.forward(moveangledistance[i][1]) tpen.end_fill()else:for iin range(len(moveangledistance)): tpen.left(moveangledistance[i][0]) tpen.forward(moveangledistance[i][1]) tpen.penup()def drawgrass(tpen,rootpos,height=40,numofcurve=20,color="green",width=5): tpen.pencolor(color) tpen.width(width) tpen.penup() tpen.setposition(rootpos) tpen.setheading(90) tpen.pendown()for iin range(numofcurve): tpen.forward(height/numofcurve) tpen.left(2) tpen.penup() tpen.setposition(rootpos) tpen.setheading(90) tpen.pendown()for iin range(numofcurve): tpen.forward(height / numofcurve) tpen.right(2)if __name__=="__main__": turtle.bgcolor("white") turtle.setup(800,600) t=turtle.Pen() t.speed(0)#draw the sun drawcircle(t,[260,160],45,0,"red",3,"red") drawray(t,[260,205],50,30,8,"red",3)#draw a house houseroof=[[-300,0],[-100,0],[-200,150]]#draw houseroof drawpoligonbypoints(t,"black",1,"blue",*houseroof) housebody=[[-300,0],[-300,-200],[-100,-200],[-100,0]]#draw housebody drawpoligonbypoints(t,"black",1,"gray",*housebody) housewindow=[90,40]#90 degree and 40 pixel draw windows housewindowset=[housewindowfor iin range(4)] housewindowpos=[-150,-90] drawpoligonbyvector(t,housewindowpos,"red",3,"white",*housewindowset) housewindowpos = [-150+40, -90] drawpoligonbyvector(t, housewindowpos, "red", 3, "white", *housewindowset) housewindowpos = [-150, -90+40] drawpoligonbyvector(t, housewindowpos, "red", 3, "white", *housewindowset) housewindowpos = [-150+40, -90+40] drawpoligonbyvector(t, housewindowpos, "red", 3, "white", *housewindowset) housedoor=move2arraybyXY(-280,-170,generate2array(80,60)) drawpoligonbypoints(t,"black",3,"white",*housedoor)#draw door knob doorknobpos=[-260,-130] drawcircle(t,doorknobpos,10,0,"black",3,"orange")#draw grass for iin range(15): drawgrass(t, [random.randint(0,400),random.randint(-300,-100)],random.randint(20,40),random.randint(10,20),"green",random.randint(1,5)) turtle.done()