Python turtle库绘制小黄人完整代码及运行教程
本文提供使用Python内置turtle库绘制经典小黄人的完整源代码与逐步实现教程。涵盖图层分解、坐标几何定位、画笔性能优化及常见运行报错排查,帮助读者在标准Python环境下顺利完成图形绘制。
turtle 库绘制经典小黄人卡通形象,核心在于将复杂的卡通形体分解为满足“油画师算法”层叠规则的几何图元:底层肢体、胶囊形黄色躯干、牛仔背带工装裤、钛灰双护目镜、眼球高光以及微笑表情。本文提供一套无需安装任何第三方扩展包、在标准 Python 3 环境下可直接拷贝执行的完整源码,并详细拆解图层绘制顺序、坐标几何推导公式、显存加速策略与窗口生命周期管理,帮助读者高效理解代码并成功呈现图形。
一、小黄人图层分解与绘制原理
在计算机图形学中,turtle 绘图依赖于“油画师算法”(Painter's Algorithm),即先绘制处于底层(距离观察者较远)的图形对象,后绘制处于表层(距离观察者较近)的图形对象。如果绘图顺序颠倒,例如先绘制了胸前的工装裤口袋,后绘制躯干的大色块,则后绘制的黄色填充将彻底遮挡先绘制的细节。
为了使代码结构清晰、便于调试和维护,我们将小黄人的整体形象自底向上严格划分为 6 个图层:
- Layer 1(底层肢体与鞋袜):包含下方两侧的黑色圆角鞋履和两侧伸展的手臂与黑色手套。先绘制手臂与鞋袜,当后续绘制黄色躯干时,躯干的外轮廓会自动遮挡手臂与躯干的连接缝隙,无需处理复杂的交线裁剪。
- Layer 2(主体胶囊躯干):小黄人的基础形体是一个上下对称的圆角胶囊体。使用明黄色(
#FCE029)填充,左右两条垂直线连接顶底各 180° 的圆弧,形成闭合区域。 - Layer 3(工装牛仔背带裤):覆盖在躯干下半部分的经典深丹宁蓝(
#2C5282)服饰,包括两侧裤腿、斜向跨越的背带、固定纽扣以及胸口的中央圆角口袋。 - Layer 4(护目镜皮带与镜框):贯穿头部左右的黑色皮质固定带,叠加两组对称的钛灰金属外环(
#8E99A4)和内嵌镜框槽。 - Layer 5(眼球、虹膜与高光点):嵌套在镜框内部的纯白巩膜、棕色虹膜、黑色核心瞳孔以及微小的偏心白色反光点。
- Layer 6(面部表情与头顶发丝):最上层的装饰线,包括微翘的微笑弧线唇形和头顶向外辐射的散落发丝。
二、准备工作与运行环境要求
在运行代码之前,需要明确运行环境的具体要求,避免陷入初学者常见的文件命名与环境配置误区:
- Python 版本:支持 Python 3.6 及以上任意主流版本。
- 标准库依赖:
turtle和底层图形引擎tkinter是 Python 官方标准库自带模块,绝对不要执行pip install turtle。第三方 PyPI 上的turtle库是多年前为 Python 2 设计的废弃第三方包,安装后反而会在导入时报语法错误。 - 运行环境形态:建议在具备本地图形界面的操作系统(macOS、Windows 或已安装桌面环境的 Linux)中执行。若在无 X11 图形支持的纯云服务器或远程 SSH 终端中运行,会因找不到显示设备而抛出
_tkinter.TclError异常。
turtle.pyPython 在执行
import turtle 时,会首先在当前脚本所在目录进行模块检索。如果你的文件命名为 turtle.py,Python 会导入脚本自身,导致标准库无法加载,并在运行时报出经典的 AttributeError: partially initialized module 'turtle' has no attribute 'setup' 错误。请务必将文件命名为 minion_drawing.py。
三、小黄人绘制完整代码实现
以下为完整的、开箱即用的 Python 代码。代码采用函数模块化封装,每个函数对应一个独立的逻辑组件,并提供详细的参数注释:
# -*- coding: utf-8 -*- """ Python turtle 库绘制经典小黄人完整脚本 运行环境:Python 3.x(标准库,无需额外安装依赖) 执行命令:python minion_drawing.py """ import turtle as t def jump_to(x, y): """精准跳转画笔至目标坐标,自动抬笔与落笔,杜绝拖拽杂线""" t.penup() t.goto(x, y) t.pendown() def init_canvas(): """初始化绘图窗口与基础环境参数""" t.setup(width=800, height=800) t.title("Python Turtle - 经典小黄人绘制教程") t.bgcolor("#F2F6FA") t.speed(0) # 关闭逐帧动画刷新,显著降低绘图耗时,最后调用 update() 一次性输出全图 t.tracer(False) t.hideturtle() def draw_feet(): """绘制底部双脚与黑色圆角鞋履(图层 1:底层)""" t.pensize(2) t.pencolor("#1E272C") t.fillcolor("#1E272C") # 左鞋履 (对称中心偏左 -40 左右) jump_to(-55, -230) t.setheading(0) t.begin_fill() for _ in range(2): t.forward(35) t.circle(8, 90) t.forward(12) t.circle(8, 90) t.end_fill() # 右鞋履 (对称中心偏右 +20 左右) jump_to(20, -230) t.setheading(0) t.begin_fill() for _ in range(2): t.forward(35) t.circle(8, 90) t.forward(12) t.circle(8, 90) t.end_fill() def draw_arms_and_hands(): """绘制双侧手臂与黑色手套(图层 1:底层)""" t.pensize(2) t.pencolor("#2B2B2B") # 左手臂(黄色) jump_to(-98, -40) t.setheading(220) t.fillcolor("#FCE029") t.begin_fill() t.forward(70) t.left(90) t.forward(22) t.left(90) t.forward(65) t.end_fill() # 左手套(深黑色) jump_to(-148, -95) t.setheading(210) t.fillcolor("#222222") t.begin_fill() t.circle(15) t.end_fill() # 右手臂(黄色) jump_to(98, -40) t.setheading(320) t.fillcolor("#FCE029") t.begin_fill() t.forward(70) t.right(90) t.forward(22) t.right(90) t.forward(65) t.end_fill() # 右手套(深黑色) jump_to(148, -95) t.setheading(330) t.fillcolor("#222222") t.begin_fill() t.circle(15) t.end_fill() def draw_body(): """绘制经典黄色胶囊形主体躯干(图层 2)""" jump_to(-100, -120) t.setheading(90) t.pensize(3) t.pencolor("#2B2B2B") t.fillcolor("#FCE029") t.begin_fill() # 1. 左侧垂直上延至肩部 t.forward(220) # 2. 顶部半圆拱头 (半径 100,顺时针偏转 180 度) t.right(90) t.circle(-100, 180) # 3. 右侧垂直下延至底部 t.forward(220) # 4. 底部半圆拱底 (半径 100,顺时针偏转 180 度) t.right(90) t.circle(-100, 180) t.end_fill() def draw_overalls(): """绘制深丹宁蓝牛仔工装背带裤(图层 3)""" t.pensize(2) t.pencolor("#1E3448") t.fillcolor("#2C5282") # 1. 裤身主体与胸口围兜多边形 jump_to(-100, -120) t.setheading(0) t.begin_fill() t.forward(200) t.left(90) t.forward(65) # 裤身右侧高 t.left(90) t.forward(25) t.right(90) t.forward(30) # 右胸围兜凸起 t.left(90) t.forward(150) # 围兜顶部水平上缘 t.left(90) t.forward(30) # 左胸围兜凹陷 t.right(90) t.forward(25) t.left(90) t.forward(65) # 裤身左侧回到底部起点 t.end_fill() # 2. 裤腿下部圆弧贴合胶囊下底 jump_to(-100, -120) t.setheading(270) t.begin_fill() t.circle(100, 180) t.left(90) t.forward(200) t.end_fill() # 3. 裤裆中间暗色分界缝线 jump_to(0, -120) t.setheading(270) t.pensize(2) t.pencolor("#1A2E40") t.forward(50) # 4. 左肩斜向背带 jump_to(-100, -45) t.setheading(30) t.fillcolor("#2C5282") t.begin_fill() t.forward(45) t.right(80) t.forward(18) t.right(100) t.forward(45) t.end_fill() # 5. 右肩斜向背带 jump_to(100, -45) t.setheading(150) t.begin_fill() t.forward(45) t.left(80) t.forward(18) t.left(100) t.forward(45) t.end_fill() # 6. 两端金属固定小纽扣 for bx, by in [(-72, -35), (72, -35)]: jump_to(bx, by) t.fillcolor("#1C1C1C") t.pencolor("#111111") t.begin_fill() t.circle(4.5) t.end_fill() # 7. 胸前弧底工装口袋 jump_to(-35, -70) t.setheading(0) t.pensize(2) t.pencolor("#1E3448") t.fillcolor("#25456E") t.begin_fill() t.forward(70) t.right(90) t.forward(30) t.circle(-10, 90) t.forward(50) t.circle(-10, 90) t.forward(30) t.end_fill() # 口袋中心圆形徽标 jump_to(0, -92) t.fillcolor("#1C1C1C") t.begin_fill() t.circle(9) t.end_fill() def draw_goggles(): """绘制护目镜皮质固定带与双金属镜框(图层 4)""" # 贯穿头部的黑色固定皮带 (Y: 72 到 98) jump_to(-100, 72) t.setheading(0) t.pensize(2) t.pencolor("#1A1A1A") t.fillcolor("#262626") t.begin_fill() for _ in range(2): t.forward(200) t.left(90) t.forward(26) t.left(90) t.end_fill() # 左右对称钛灰金属双镜框 for cx in [-42, 42]: # 外圈金属环 (半径 38,落笔点 Y = 85 - 38 = 47) jump_to(cx, 47) t.setheading(0) t.pensize(2) t.pencolor("#4B5563") t.fillcolor("#8E99A4") t.begin_fill() t.circle(38) t.end_fill() # 镜框内嵌暗色槽 (半径 30,落笔点 Y = 85 - 30 = 55) jump_to(cx, 55) t.setheading(0) t.pencolor("#374151") t.fillcolor("#5F6B76") t.begin_fill() t.circle(30) t.end_fill() def draw_eyes(): """绘制眼白、虹膜、核心瞳孔与微小偏心高光(图层 5)""" for cx in [-42, 42]: # 1. 纯白巩膜 (半径 27,落笔点 Y = 85 - 27 = 58) jump_to(cx, 58) t.setheading(0) t.pensize(1) t.pencolor("#CBD5E1") t.fillcolor("#FFFFFF") t.begin_fill() t.circle(27) t.end_fill() # 2. 经典深棕色虹膜 (半径 14,落笔点 Y = 85 - 14 = 71) jump_to(cx, 71) t.pencolor("#633918") t.fillcolor("#7B431B") t.begin_fill() t.circle(14) t.end_fill() # 3. 黑色核心瞳孔 (半径 7,落笔点 Y = 85 - 7 = 78) jump_to(cx, 78) t.pencolor("#0F172A") t.fillcolor("#0F172A") t.begin_fill() t.circle(7) t.end_fill() # 4. 右上偏心反光高光点 (半径 2.5,微小纯白提升灵动感) jump_to(cx + 3, 85) t.pencolor("#FFFFFF") t.fillcolor("#FFFFFF") t.begin_fill() t.circle(2.5) t.end_fill() def draw_mouth(): """绘制生动的弧形微笑唇线(图层 6)""" jump_to(-35, 20) t.setheading(305) t.pensize(3) t.pencolor("#422A14") # 借助平滑圆弧向上拟合出弧形微笑唇 t.circle(42, 110) def draw_hair(): """绘制头顶向外散落发丝(图层 6)""" t.pensize(2.5) t.pencolor("#1E293B") # 5 根发丝的起始绝对坐标、航向角度与步长 strands = [ (0, 200, 90, 28), # 正中央直立发丝 (-14, 197, 108, 24), # 左侧斜翘发丝 1 (-28, 192, 125, 22), # 左侧低偏发丝 2 (14, 197, 72, 24), # 右侧斜翘发丝 1 (28, 192, 55, 22), # 右侧低偏发丝 2 ] for hx, hy, angle, length in strands: jump_to(hx, hy) t.setheading(angle) t.forward(length) def main(): """主程序:统筹调度时序拓扑与显存刷新""" init_canvas() # 严格按油画师算法时序渲染 draw_feet() draw_arms_and_hands() draw_body() draw_overalls() draw_goggles() draw_eyes() draw_mouth() draw_hair() # 一次性将缓冲区画面刷入物理窗口 t.update() # 阻塞主线程,保持窗口持续显示,监听鼠标关闭事件 t.done() if __name__ == "__main__": main()
四、核心绘制函数与坐标几何详解
很多读者在自行修改或模仿 turtle 脚本时,常遇到圆弧画歪、眼球偏出镜框或对称失调等问题。以下结合几何原理详细解析三个关键技术点:
1. 胶囊躯干连续闭合路径计算
小黄人的主体是一根宽 200、高 360 的圆角柱体。我们设腰部水平线为 X 轴,中央轴线为 Y 轴:
- 躯干起始点设在左腰腹下部
(-100, -120),画笔航向设为90°(朝正上方)。 - 向前直行 220 单位到达头顶起拱点
(-100, 100)。 - 在头顶画半圆拱:由于此时画笔向正上方,若顺时针向右转弯,使用
t.circle(-100, 180)。负半径表示圆心位于画笔行进方向的右侧,圆心坐标即为(0, 100),画完半圆后画笔精准落在右侧(100, 100),且朝向自动调整为270°(正朝下)。 - 再向下直行 220 单位到达底部起拱点
(100, -120),同样执行t.circle(-100, 180),平滑闭合回起始点。
2. 同心圆定位与落笔点偏移定理
这是使用 turtle.circle(r) 最容易混淆的概念:circle(r) 的起点是圆周的最下沿,而不是圆心本身!
当画笔朝向为 0°(正右方)时,调用 circle(r) 会逆时针在画笔左侧画圆,圆心位于 (x, y + r)。因此,若我们要以 (cx, cy) 为圆心绘制半径为 r 的同心圆,画笔的初始绝对定位必须落位在 (cx, cy - r)。
3. 工装背带多边形与闭合染色
在绘制工装背带裤时,不能仅用画线函数。必须在移动笔尖前调用 t.begin_fill(),在沿边缘绘制完整封闭路径后调用 t.end_fill()。
由于下沿弧度与躯干底部完全重合,我们复用了 t.circle(100, 180) 的底部轨迹,保证了牛仔裤与黄色身体边缘严丝合缝,不产生多余的露白间隙。
五、脚本运行步骤与显示控制
完成脚本编写后,按照以下标准步骤在终端或开发工具中执行:
1. 命令行执行方式
打开系统原生终端(Windows 为 CMD 或 PowerShell,macOS / Linux 为 Terminal),进入保存脚本的目录并运行:
# 进入存放脚本的工程目录 cd /path/to/your/project # 执行脚本 python minion_drawing.py
2. 渲染速度与显示机制解析
在代码中,我们特意设置了 t.tracer(False) 与 t.update() 的组合。这是编写复杂 turtle 程序的黄金模式:
- 逐帧渲染模式(默认状态):画笔每向前移动一步、每转动一个角度,Tkinter 都会触发画布重绘。绘制数千个像素点可能需要耗费几十秒,体验非常缓慢。
- 显存缓冲模式(
tracer(False)):彻底关闭界面的中间重绘,所有绘制操作在后台显存中即时完成;在main()结尾调用一次t.update(),瞬间呈现成品画面,耗时低于 0.1 秒。 - 观察绘制过程:如果你希望直观观察画笔一笔一划的动态绘制轨迹,只需将
init_canvas()中的t.tracer(False)这一行注释掉,并将画笔速度设为t.speed(6)即可。
3. 窗口生命周期保持
很多新手反馈“运行后窗口闪了一下就没了”。这是因为 Python 脚本在执行完最后一行语句后,主线程即刻结束,随之销毁了 Tkinter 窗口句柄。t.done() 的作用就是调用底层 Tkinter 的 mainloop() 事件主循环,挂起主线程,保持窗口长久驻留,直到用户主动点击窗口关闭按钮。
六、常见运行报错与图形变形排查
针对执行本教程代码时可能出现的典型报错与图形异常,整理如下速查对照表:
| 故障表现 / 报错信息 | 根本原因分析 | 具体解决方法 |
|---|---|---|
AttributeError: module 'turtle' has no attribute 'setup' |
当前目录下或导入路径中存在名为 turtle.py 的脚本,形成了同名模块覆盖。 |
将该脚本更名为 minion_drawing.py,并删除该目录下自动生成的 __pycache__ 缓存文件夹。 |
| 绘图窗口瞬间闪退,控制台直接提示 Process finished | 脚本末尾遗漏了阻塞窗口的指令。 | 在代码最后一行确保存在 t.done() 或 turtle.mainloop()。 |
| 图形各个器官之间出现长长的黑色拖拽拉线 | 在坐标位移时忘记抬笔,或者调用 goto() 前画笔仍处于落笔状态。 |
严禁直接裸调 t.goto(x, y),必须统一调用封装好的 jump_to(x, y) 函数。 |
_tkinter.TclError: no display name and no $DISPLAY |
在没有 X11 / 桌面 GUI 环境的 Linux 云主机、Docker 容器或纯 SSH 会话中执行。 | 转移到配备本地桌面系统的环境(Windows、macOS 或安装有桌面环境的 Linux)中运行。 |
| 圆弧开口方向相反,嘴巴画成了向下的哭脸 | 调用 circle() 时的起始朝向(setheading)未设定正确。 |
每次画弧前检查起始角度。在画唇线前严格执行 t.setheading(305)。 |
七、关键设计参数与二次创作清单
本套小黄人代码具有良好的参数解耦性。读者可以根据个人审美或创作需求,对核心常量进行二次定制与扩展:
| 组件对象 | 对应函数 | 标准设计参数 | 扩展修改建议 |
|---|---|---|---|
| 躯干身形 | draw_body() |
半宽 R=100,侧身直立高度 H=220 | 增大 H 可绘制高个小黄人 Kevin(凯文),减小 H 可绘制矮萌小黄人 Bob(鲍勃)。 |
| 视觉双眼 | draw_goggles() / draw_eyes() |
双镜框中心 X ∈ [-42, 42],镜框 R=38 | 若要绘制经典单眼小黄人 Stuart(斯图尔特),只需将循环改为单一中心 cx = 0,并将镜框 R 放大至 52。 |
| 工装色调 | draw_overalls() |
经典丹宁深蓝 #2C5282 |
可调整为紫色暗黑版或橙色囚服版,更换填色十六进制代码即可一键换装。 |
通过掌握图层时序分解、路径连续拟合与坐标绝对定位三大核心技术,不仅能够精准绘制小黄人,还能将相同的方法论迁移到机器猫、皮卡丘等任意二维复合卡通图形的 Python 编程绘制中。
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













