尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

用Turtle库绘制柯南:Python图形编程实战

用Turtle库绘制柯南:Python图形编程实战 简介这是一份用Python标准库turtle绘制动漫人物柯南的趣味编程源码压缩包内仅1个.py文件大小约2KB非常适合Python初学者、图形编程爱好者及少儿编程教学场景使用。案例以柯南形象为绘制目标完整展示从导入turtle库、创建画笔对象到逐步绘制头部、眼睛、衣服等部位的全过程代码按绘制顺序排列可直接运行并查看逐步呈现的图形效果。代码中综合运用了forward、left、right等移动指令goto进行坐标定位penup/pendown控制抬笔落笔circle与ellipse绘制圆形、椭圆配合begin_fill/end_fill、fillcolor、pencolor实现色彩填充最后以turtle.done()保持画布显示。通过调整坐标、半径与颜色参数还能举一反三创作其他角色或图案。目前已有8139人学习浏览适合作为图形编程入门练手项目也可作为课堂教学中的示例代码帮助学习者直观理解turtle绘图的坐标体系与绘图逻辑。1. 为什么用 turtle 画柯南先解决“看得见”的编程练习第一次接触 Python 的人往往卡在print输出一堆文字之后不知道该干嘛。turtle 库把代码执行变成了画笔运动一行forward(100)就能看到乌龟拖着线往前走这种即时反馈是入门阶段最需要的。用 turtle 画柯南不是要做一张高清海报而是通过拆解角色的脸型、眼镜、衣服和颜色填充把坐标系、循环、函数封装这些基础知识都过一遍。源码看起来是画图实际上练的是“先规划坐标再控制状态最后组合复用”的编程思路。这个项目适合刚学完 Python 基础语法的新手也适合需要带人入门的老手——它比打印九九乘法表更有趣又比写爬虫简单半小时内就能跑出结果。更重要的是turtle 作为 Python 自带的标准库不需要额外安装只要你把 Python 环境配好就能直接运行这也是它常年出现在 python 入门教程里的原因。2. turtle 库的坐标系与画笔控制先画准再画像2.1 画布与坐标系所有图形都是坐标游戏turtle 的默认画布大小是屏幕的 800x800 左右原点 (0,0) 在画布中心向右是 x 正方向向上是 y 正方向。这与数学坐标系一致所以画人物时要先在脑子里建一个直角坐标系。比如画柯南的头部圆心放在 (0,0)画一个半径为 100 的圆那么头顶在 (0,100)下巴在 (0,-100)。如果用goto()直接移动需要清楚当前画笔箭头指向的方向但更稳妥的做法是先用penup()抬笔goto()到目标点再pendown()落笔这样就不受当前朝向干扰。很多初学者会问“为什么我的图形是反的”或者“画笔移动到了奇怪的位置”通常就是没有理解left()和right()是相对当前朝向旋转而不是绝对角度。比如left(90)是向左转 90 度而不是转到画布左上角。如果想让画笔从当前位置移动到某个坐标直接用goto(x, y)最省心。turtle 的坐标单位是像素在 1000x800 的画布上(0,0) 到 (500,0) 正好是画布右边缘的一半所以规划人物时不要把身体画到 ±400 以外否则会出画。2.2 画笔状态与运动控制forward、penup、goto 的配合turtle 对象的常用方法分三类运动控制、状态控制和属性设置。运动控制包括forward(distance)/fd()、backward()、left(angle)、right(angle)、goto(x, y)、circle(radius, extent)状态控制包括penup()/up()、pendown()/down()属性设置包括pencolor()、fillcolor()、pensize()、speed()。这里的distance单位是像素angle单位是度。下面的代码演示了如何用三种方式画同一条直线import turtle t turtle.Turtle() t.speed(3) # 方式1forward前进100像素 t.forward(100) # 方式2goto绝对定位 t.penup() t.goto(-50, 0) t.pendown() t.setheading(0) # 设置朝向为正东 t.forward(100) # 方式3右转向后再前进模拟相对运动 t.penup() t.goto(50, 50) t.pendown() t.right(90) t.forward(100) turtle.done()setheading(0)是很多人忽略的细节它把画笔方向重置为水平向右。如果不重置上一个动作留下的朝向会影响后面的forward()方向导致画出斜线。goto()则不存在这个问题因为它用的是绝对坐标。方式 1 最简单但只能沿当前方向移动方式 2 适合跳转到指定起点方式 3 用于模拟“从某个点出发沿某个角度前进”的相对运动。在画柯南的四肢时方式 3 更好用因为身体部件之间有夹角直接用left/right转向更直观。对于需要精确控制起点的场景我一般这样搭配penup() - goto(x, y) - setheading(0) - pendown()。这个固定节奏能避免大部分定位混乱的问题。另外pensize()默认是 1画轮廓线时改成 2 或 3 会更有手绘感speed(0)表示最快但调试时建议用speed(5)能看清绘制顺序。2.3 常用绘制方法速查表方法参数作用常用值forward()distance沿当前方向前进100left()/right()angle左转/右转角度90, 45goto()x, y移动至绝对坐标(0, -100)penup()/pendown()无抬笔/落笔-pencolor()color线条颜色blackfillcolor()color填充颜色lightbluebegin_fill()/end_fill()无填充区域的起止标记-circle()radius, extent画圆或弧extent 为度数radius100, extent360speed()0-10画笔速度0 最快3这张表基本覆盖了画柯南需要的全部方法。注意circle(radius, extent)中 extent 默认是 360 度想画半圆就传 180。turtle 没有提供直接画椭圆的 API源码里如果用了ellipse()那是自定义封装不是标准库方法。我们在下一章会看到如何手动实现椭圆。另外fillcolor()在调用begin_fill()之前设置有效如果先开始填充再设置颜色颜色可能不会生效。这是初学者经常犯的次序错误。3. 拆解柯南从头部到眼镜的绘制流程3.1 头部轮廓用 circle 画圆脸和填充底色柯南的头部接近圆形但下巴略尖。为了上手先用正圆代替脸型。圆心放在原点半径设为 120填充肤色。turtle 中填充是这样工作的先begin_fill()再画封闭图形最后end_fill()。注意begin_fill()之前画笔要落笔否则即使有闭合图形也填不上色。import turtle kornan turtle.Turtle() kornan.speed(3) kornan.penup() kornan.goto(0, -120) kornan.pendown() kornan.pencolor(black) kornan.fillcolor(#FDDCB5) # 肤色 kornan.begin_fill() kornan.circle(120) kornan.end_fill()这里goto(0, -120)是圆的底部然后circle(120)逆时针画圆最终回到起点形成一个封闭圆。fillcolor的颜色值可以用十六进制字符串也可以直接用 orange 这类内置颜色名。肤色用#FDDCB5比内置bisque更贴近动漫角色的亮肤色。要画尖下巴就需要在圆圈基础上调整。常见做法是circle(120, 180)画上半圆然后用goto()连接下半部分或者用pensize()加粗下颌线。初学者先保持正圆也能看出是人物头部后续再叠加上刘海和眼镜轮廓感自然就来了。如果你用的是 Python 3.10 以上的环境turtle 的circle()曲线会更平滑低版本时线段感明显可以适当调大画布尺寸来缓解。3.2 眼睛和眼镜框用椭圆与弧线定位关键特征眼睛是柯南最具辨识度的特征典型结构是大眼眶、蓝色虹膜和高光。标准 turtle 画不出椭圆需要自己写一个ellipse函数基于circle绘制弧线拼接def ellipse(pen, radius_x, radius_y): pen.begin_fill() for _ in range(2): pen.circle(radius_x, 90) pen.circle(radius_y, 90) pen.end_fill()这个函数的原理是用两段半径不同的圆弧交替拼接四次形成一个近似的椭圆。radius_x控制横向半径radius_y控制纵向半径。比如要画一个宽 40、高 60 的眼眶调用ellipse(kornan, 20, 30)即可。接下来用绝对坐标把两个眼睛放在脸部中线的左右两侧。脸的中心是 (0,0)所以左眼中心设在 (-30, -10)右眼中心设在 (30, -10)。ellipse的起点是椭圆最左侧的端点因此左眼要从(-55, -10)开始右眼从(5, -10)开始相差正好一个横向直径 50。# 左眼眶中心(-30, -10) kornan.penup() kornan.goto(-55, -10) # 左眼椭圆最左侧起点 kornan.pendown() ellipse(kornan, 25, 35) # 右眼眶中心(30, -10) kornan.penup() kornan.goto(5, -10) kornan.pendown() ellipse(kornan, 25, 35)这里的goto坐标是椭圆最左侧端点因为ellipse函数内部从当前点开始画弧。如果画出来的椭圆位置不对先计算椭圆最左点坐标中心 x 减去横向半径再对齐 y 坐标。比如中心 (-30, -10)横向半径 25则起点 x -30 - 25 -55。把这个公式记清楚后续调整位置时就不会靠猜。3.3 头发、嘴巴与身体用直线和多边形搭出轮廓头部上方要加刘海。柯南的刘海是一簇尖角向下的形状可以用forward()和left()组合画出锯齿。更简单的方式是直接用goto()依次连接几个固定点再填充深灰色kornan.penup() kornan.goto(-70, 80) kornan.pendown() kornan.fillcolor(#333333) kornan.begin_fill() kornan.goto(-40, 30) kornan.goto(-10, 70) kornan.goto(20, 20) kornan.goto(50, 70) kornan.goto(70, 80) kornan.goto(50, 110) kornan.goto(0, 90) kornan.goto(-50, 110) kornan.goto(-70, 80) kornan.end_fill()这段代码画了一个带尖角的发帘覆盖在圆形的上半部分。注意点序列的走向是从左到右再从右边绕回左边形成闭合区域。填充色#333333是深灰色避免纯黑死板。嘴巴可以简化成一条直线加上下弯用penup()和goto()画短线段即可这里不再展开。身体部分用梯形表示西装前襟再叠两个三角形蝴蝶结# 西装主体 kornan.penup() kornan.goto(-60, -120) kornan.pendown() kornan.fillcolor(#4A90D9) kornan.begin_fill() kornan.goto(60, -120) kornan.goto(70, -220) kornan.goto(-70, -220) kornan.goto(-60, -120) kornan.end_fill() # 蝴蝶结 for side in [-1, 1]: kornan.penup() kornan.goto(0, -150) kornan.pendown() kornan.fillcolor(red) kornan.begin_fill() kornan.goto(side * 30, -140) kornan.goto(side * 20, -155) kornan.end_fill()西装梯形的上边两个点 (-60, -120) 和 (60, -120) 正好与头部下缘衔接下边更宽形成肩部。goto顺序决定了图形闭合路径不要求回到起点end_fill()会自动闭合。蝴蝶结用side取 -1 和 1分别画左侧和右侧的结避免复制两份几乎相同的代码。3.4 完整绘制代码一个可以直接运行的脚本把上面的片段组合起来再加上眼镜框和刘海就是一个完整脚本。实际源码文件用Python的turtle库画柯南.py里更细致这里给出一个可运行的精简版import turtle def ellipse(pen, rx, ry): pen.begin_fill() for _ in range(2): pen.circle(rx, 90) pen.circle(ry, 90) pen.end_fill() kornan turtle.Turtle() kornan.speed(3) # 头部 kornan.penup() kornan.goto(0, -120) kornan.pendown() kornan.fillcolor(#FDDCB5) kornan.begin_fill() kornan.circle(120) kornan.end_fill() # 左眼中心(-30, -10) kornan.penup() kornan.goto(-55, -10) kornan.pendown() ellipse(kornan, 25, 35) # 右眼中心(30, -10) kornan.penup() kornan.goto(5, -10) kornan.pendown() ellipse(kornan, 25, 35) # 西装 kornan.penup() kornan.goto(-60, -120) kornan.pendown() kornan.fillcolor(#4A90D9) kornan.begin_fill() kornan.goto(60, -120) kornan.goto(70, -220) kornan.goto(-70, -220) kornan.goto(-60, -120) kornan.end_fill() turtle.done()这个脚本运行后能看到基本的柯南轮廓圆脸、两个大眼睛和蓝色西装块。注意ellipse函数里pen.begin_fill()和end_fill()放在一起如果中间有抬笔或者跳转会导致填充区域不闭合。建议在椭圆函数内部不调用penup()保证填充路径连续。在此基础上你可以继续加刘海、眼镜框、蝴蝶结和嘴巴慢慢把细节补全。4. 颜色填充、坐标微调与函数封装让代码可维护4.1 用 fillcolor 和 pencolor 控制颜色层次turtle 的颜色系统支持英文颜色名、十六进制 RGB 值和 RGB 元组。画人物时需要一份颜色配置表把肤色、头发、西装、领结的颜色统一管理。比如柯南的头发不是纯黑而是深灰色#333333西装是藏蓝色#2E4E7E蝴蝶结是红色#E60000。直接在代码里重复写十六进制串容易出错也难调整建议先定义成变量。SKIN #FDDCB5 HAIR #333333 SUIT #2E4E7E TIE #E60000然后在使用时写成kornan.fillcolor(SKIN)。这样想换配色时只需要改顶部常量。对于大量相同形状的部件比如左右眼镜框可以先指定填充色和边框色再调用绘图函数。pencolor()默认是黑色如果线条和填充色对比强可以关掉边框即kornan.pencolor(SKIN)让轮廓线融入填充色让图形边缘更干净。很多成品代码会刻意让线条颜色和填充颜色一致模拟无描边的扁平风格。另外turtle 支持 RGB 元组比如kornan.pencolor((0.2, 0.3, 0.4))三个分量取值 0~1。这在需要渐变或者精确匹配图片颜色时很有用。对于初学者十六进制字符串最直观在浏览器里取色后直接拷贝即可。4.2 坐标相对化用 goto 和方向增量避免硬编码上面示例中的坐标都是手工硬编码的比如goto(-55, -10)。这种代码能跑但调整位置时每次都要改多个数字。更好的做法是用相对坐标以头部中心为基点定义眼睛、嘴巴、衣服到基点的偏移量。比如设置head_x, head_y 0, 0左眼中心表示为(head_x - 30, head_y - 10)。这样移动头部时眼睛会跟着动。我一般会写一个辅助函数专门画椭圆并且传入中心坐标def draw_eye(pen, cx, cy, rx, ry, fill_color): pen.penup() pen.goto(cx - rx, cy) # 从左侧起点开始 pen.pendown() pen.fillcolor(fill_color) pen.begin_fill() for _ in range(2): pen.circle(rx, 90) pen.circle(ry, 90) pen.end_fill() pen.penup() pen.goto(cx, cy)cx - rx保证了从椭圆最左侧开始cy为垂直中心。这样一来调用draw_eye(kornan, -30, -10, 25, 35, white)时不需要关心起点怎么算。封装后重复代码从每次 10 行降为 1 行也更容易批量修改左右眼的间距和大小。在调试坐标时我习惯在关键位置打印画笔坐标在绘制每个部件前把pen.position()输出到控制台。比如print(kornan.pos())这样可以快速确认goto()之后画笔是否到达预期位置。turtle 的返回值是(x, y)元组可以直接和设计值对比。4.3 将重复绘画封装成函数画眼睛、画眼镜框柯南的眼镜框是圆形两个镜片中间连一条横梁。把“画一个眼镜框”封装成函数可以同时画左镜框和右镜框def draw_glasses(pen, left_x, right_x, cy, radius): for cx in (left_x, right_x): pen.penup() pen.goto(cx - radius, cy) pen.pendown() pen.pensize(2) pen.pencolor(black) pen.fillcolor() # 不填充只画边框 pen.begin_fill() pen.circle(radius) pen.end_fill() # 横梁 pen.penup() pen.goto(left_x radius, cy) pen.pendown() pen.goto(right_x - radius, cy)提示fillcolor()并不是把颜色设为空而是告诉 turtle 不执行填充。如果你直接在fillcolor()里传空字符串部分版本会报错更稳妥的做法是用pen.fill(False)替代。这里保留begin_fill()和end_fill()是为了让圆环的路径闭合避免出现缺口。pensize(2)加粗边框让眼镜在脸上更醒目。radius是镜框半径left_x和right_x是两个镜框中心 x 坐标。封装前后的对比可以看下表方式代码量修改镜框尺寸修改位置出错率硬编码每组 10 行每个坐标都要改每个坐标都要改高函数封装每组 1 行调用只改参数只改参数低函数封装的另一好处是可以复用。画完柯南后你还可以画另一个戴眼镜的角色只需要改draw_glasses的调用参数。比如把left_x从 -35 改成 -50两个镜框会同时外移不需要动横梁代码。这就是 turtle 画图向“面向对象”思维的过渡。5. 进阶让柯南动起来以及 turtle 绘图的三个常见掉坑点5.1 用 turtle 做简单动画平移和旋转画完静态柯南后可以让他眨眼或者走动。turtle 动画的基本方式是用turtle.tracer(0)关闭自动刷新手动修改坐标后用turtle.update()刷新。比如实现一个上下移动的漂浮效果import turtle def float_move(pen, base_y, step1): for i in range(50): pen.penup() pen.goto(0, base_y i * step) pen.pendown() turtle.update() turtle.ontimer(lambda: None, 10)这里ontimer不是必须的只是为了手动控制刷新频率。tracer(0)先关掉动画缓冲update()再一次性渲染当前状态这样移动看起来流畅不会出现画笔残影。注意ontimer的回调需要函数对象lambda: None只是占位实际开发中可以用time.sleep(0.02)替代但这样会阻塞事件循环不适合复杂动画。5.2 保存绘图结果用 postscript 或屏幕截图turtle 没有直接保存 PNG 的函数但可以借助turtle.getcanvas().postscript()导出矢量格式再通过其他工具转换。代码如下ts turtle.getscreen() ts.getcanvas().postscript(fileconan.eps)这样会生成一个 EPS 文件预览软件可以打开也可以用 Ghostscript 或在线工具转成 PDF 或 PNG。如果你用的是 Windows可以导出 EPS 后在画图软件里截图Mac 用户可以直接用系统截图快捷键。对于博客配图导出 EPS 后转高分辨率 PNG 会比直接截图清晰得多尤其是线条边缘不会出现锯齿。5.3 常见掉坑点turtle 库用不了、窗口闪退、速度太快第一个高频问题是import turtle后环境报错通常是因为安装了多个 Python 版本或者使用了一些精简的 IDE。建议在终端里执行python -m pip list | findstr turtleWindows或pip list | grep turtleLinux/macOS确认 turtle 是否可用。turtle 是 Python 标准库正常情况下不需要安装如果你用的是 Anaconda注意不要和turtle-graphics第三方包混淆。在 VSCode 里运行前要先确认右下角选择的 Python 解释器是同一个环境否则会出现“在终端能跑在 VSCode 里报 ModuleNotFoundError”的诡异问题。第二个问题是窗口闪退。很多初学者把代码保存成 .py 文件后双击运行窗口一出现就消失。解决方法是确保运行环境中没有语法错误并且在脚本最后调用turtle.done()或turtle.mainloop()让事件循环保持监听。另外不要在 Jupyter Notebook 里直接运行Notebook 的交互式环境和 turtle 的 GUI 循环冲突建议在终端用python 用Python的turtle库画柯南.py运行。如果你确实想在 Notebook 里预览可以加%matplotlib inline并使用IPython.display的Image来显示导出的图片但这不是原生支持不如直接跑脚本方便。第三个问题是绘制速度。speed(0)是最快的speed(10)是普通快但画几百个线段时仍然慢。合理做法是先用speed(10)观察线段位置确认无误后改成speed(0)跑最终效果。如果填充很慢可以把tracer(0)和update()结合一次性渲染所有图形。tracer(0)会暂停所有自动刷新画笔移动时屏幕不更新直到调用update()才统一显示这比speed(0)快一个量级。注意tracer(0)之后speed()设置会失效所以要在关闭刷新前把速度调好。本文还有配套的精品资源点击获取
返回列表