最近看到一个非常有意思的标题:画只可爱小龙〰。很多人第一反应会想到 PS、Procreate 或者 AI 绘画工具,但其实这件事用 Python 也能做,而且很适合作为编程入门案例。本文就把这个项目完整拆开:从 turtle 绘图库的基础用法讲起,再到小龙的造型拆解、完整可运行代码、常见报错排查,最后给出参数化和动画优化的建议。
不管你是刚学 Python 的新手,还是带学生做趣味项目的老师,都可以照着这篇文章一步步把代码跑起来,再用自己的思路去调整颜色、坐标和造型。
1. 背景:用 Python 画出可爱小龙
1.1 turtle 是什么
turtle 是 Python 自带的标准库,它模拟了一只“海龟”在画布上移动,海龟走过的路径会留下线条,所以也叫“海龟绘图”。
它的使用方式非常直观:通过 forward() 前进、left() 左转、right() 右转、goto() 跳到指定坐标,就可以画出任意图形。因为不需要额外安装第三方库,它经常被用来给初学者讲解循环、函数、坐标系和程序设计思想。
严格来说,turtle 依赖底层图形库 tkinter。在 Windows 和 macOS 上使用官方 Python 安装包时,tkinter 通常已经内置;在部分 Linux 环境下,需要单独安装 python3-tk。这点会在后面的环境准备里详细说明。
1.2 为什么用它画卡通角色
用 turtle 画卡通角色,听起来不如专业绘图软件方便,但它有两个明显的优势。
第一,整个绘画过程是“程序化”的。每个线条、每个圆、每块颜色都是由代码控制,你可以精确知道某个坐标画的是什么,这对学习编程非常有帮助。比如画眼睛,就是“在这个位置画一个白色圆,再叠加一个黑色圆和一个白色高光点”。
第二,修改成本很低。想改变龙的肤色,只需改一个颜色常量;想让头更大,只需调整半径数值。这种参数化能力正是编程思维的一部分,也是从“画一张图”走向“设计一个可复用的绘图程序”的关键步骤。
1.3 这个项目能练习哪些技术点
画一只可爱的 Q 版小龙,至少需要掌握这些知识点:
turtle画布坐标系的建立;- 画笔的移动、转向、抬笔和落笔;
- 圆、椭圆的绘制与颜色填充;
- 将复杂图形拆分成多个简单函数;
- 通过坐标规划来组合各个身体部位;
- 常见运行时错误的诊断与解决。
这些技能不仅可以用在 turtle 上,也能迁移到 Python 后端开发、自动化脚本甚至简单的图形数据处理中。很多开发者第一次理解“把复杂问题拆成小函数”,就是从类似的小项目开始的。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境准备与版本说明
2.1 环境要求
本文示例代码以 Python 3.8 及以上版本为主要环境,但核心语法在 Python 3.6 之后基本都能直接运行。
- 操作系统:Windows / macOS / Linux 均可;
- Python 版本:建议 3.8 或更高;
- 第三方库:不需要,
turtle是标准库; - 图形环境:需要
tkinter支持。
如果你的 Python 是通过 Anaconda、Miniconda 安装的,通常已经包含 tkinter。如果使用 Linux 发行版自带的 Python,可能需要额外安装系统包,例如 Ubuntu / Debian 系列执行:
bash复制sudo apt install python3-tk
不同发行版的包管理器命令不同,请根据实际系统调整。本文的重点是绘图思路和代码组织,而不是某个特定版本的环境配置,所以版本号不必完全一致。
2.2 验证 turtle 是否可用
打开终端或命令行,先确认 Python 版本:
bash复制python --version
然后验证 turtle 能否正常导入:
bash复制python -c "import turtle; print('turtle ok')"
如果打印出 turtle ok,说明绘图环境正常。如果这里报错 ModuleNotFoundError: No module named 'tkinter',就需要先解决图形库缺失的问题,再继续下面的内容。
2.3 开发工具建议
turtle 程序有三种常见的运行方式:
- 直接在命令行执行 Python 脚本;
- 在 PyCharm 或 VS Code 中创建项目运行;
- 打开 IDLE,把代码粘贴到编辑器中运行。
对于新手,我更推荐第二种方式。因为 PyCharm 和 VS Code 能看到运行日志,代码出现语法错误或者运行时异常时,报错信息更完整,查找问题也更容易。
3. turtle 绘图基础:坐标、画笔与常用命令
3.1 坐标系与画笔状态
turtle 的画布本质上是一个二维坐标系。窗口中心是原点 (0, 0),水平向右是 x 轴正方向,垂直向上是 y 轴正方向。
画笔有四个核心状态:
- 位置:当前海龟所在的坐标;
- 方向:海龟面向的角度,向右是 0 度,向上是 90 度;
- 颜色:分为画笔颜色和填充颜色;
- 抬落状态:
penup()表示抬起画笔移动不留下痕迹,pendown()表示落下画笔开始绘制。
理解坐标系之后,很多布局问题就能迎刃而解。例如你想让小龙的头在身体右上方,就可以把头的中心点设置成身体的中心点偏移某个坐标值,而不是凭感觉乱画。
3.2 常用命令速查
下面是最常用的 turtle 命令,后续代码中会大量用到:
| 命令 | 作用 |
|---|---|
turtle.Screen() |
创建画布窗口 |
screen.setup(w, h) |
设置窗口宽高 |
screen.bgcolor("MistyRose") |
设置背景色 |
turtle.Turtle() |
创建画笔对象 |
t.speed(6) |
设置画笔速度,0 最快 |
t.pensize(3) |
设置线条粗细 |
t.pencolor("#2E7D32") |
设置画笔颜色 |
t.fillcolor("#4CAF50") |
设置填充颜色 |
t.penup() / t.pendown() |
抬笔 / 落笔 |
t.goto(x, y) |
移动到指定坐标 |
t.setheading(angle) |
设置海龟朝向 |
t.forward(d) |
向前移动 d 像素 |
t.left(angle) / t.right(angle) |
左转 / 右转 |
t.circle(r, extent) |
画半径为 r 的圆或弧 |
t.begin_fill() / t.end_fill() |
开始 / 结束填充 |
t.hideturtle() |
隐藏海龟图标 |
screen.exitonclick() |
点击窗口后退出 |
新手最容易忽略的点是:begin_fill() 和 end_fill() 必须成对出现,并且中间画的必须是闭合路径,否则填充效果会不完整。
3.3 如何画圆和椭圆
turtle 原生支持 circle() 画圆,但画椭圆没有现成函数。一个比较通用的做法是用参数方程:水平半径 rx、垂直半径 ry,每次计算圆周上的坐标,再用 goto() 连线。
python复制import math
import turtle
t = turtle.Turtle()
t.speed(8)
def draw_ellipse(center_x, center_y, rx, ry, fill_color, outline_color="#2E7D32"):
t.penup()
t.goto(center_x + rx, center_y)
t.pendown()
t.pencolor(outline_color)
t.fillcolor(fill_color)
t.begin_fill()
steps = 72
for i in range(steps + 1):
angle = math.radians(i * 360 / steps)
x = center_x + rx * math.cos(angle)
y = center_y + ry * math.sin(angle)
t.goto(x, y)
t.end_fill()
draw_ellipse(0, 0, 80, 50, "#C8E6C9", "#2E7D32")
t.hideturtle()
turtle.done()
这段代码画的是一个水平方向的椭圆。steps 控制精度,数值越大,边缘越平滑,但绘制时间也会变长。在后面的小龙项目中,我会继续复用这个函数,用它画身体、肚皮和脚掌。
4. 小龙造型设计:拆解与坐标规划
4.1 先拆解身体部位
画一只可爱小龙之前,最好先把它拆成多个独立部件。我的方案是把龙分成这些部分:
- 尾巴:从身体左侧向左上方翘起,末端带卷;
- 翅膀:背部上方的小色块;
- 腿:两条短腿加上脚掌;
- 身体:一个绿色椭圆,肚子位置叠一个浅色椭圆;
- 头:一个圆;
- 角:头顶两个小三角形;
- 眼睛:眼白、瞳孔、高光三层;
- 腮红:脸颊两侧的粉色圆;
- 嘴巴:用折线画出微笑;
- 鼻孔:鼻孔下方两个小点。
这样做的好处是,每个身体部位都可以封装成一个独立函数。哪一部分画得不好看,就只调整那个函数里的坐标和颜色,不用改动整个程序。
4.2 颜色方案
可爱风格的关键是颜色明亮、对比温和。我选择了一套适合 Q 版小龙的配色:
| 部位 | 颜色 | 色值 |
|---|---|---|
| 身体主色 | 清新绿 | #4CAF50 |
| 肚皮 | 浅绿 | #C8E6C9 |
| 翅膀 | 浅蓝 | #81D4FA |
| 角 | 暖橙色 | #FFB74D |
| 腮红 | 粉色 | #F48FB1 |
| 眼睛 | 白色和深灰 | #FFFFFF / #1B1B1B |
| 背景 | 玫瑰米色 | MistyRose |
可以看到,主色和辅助色之间有明显的明度差,这样可爱又清晰,不会因为颜色过于杂乱而失去重点。
4.3 绘制顺序
绘制顺序对最终效果影响很大。你应该先画被遮挡的、远处的部位,再画靠近前端的部位。
以这只小龙为例,绘制顺序是:
- 尾巴;
- 后腿和前腿;
- 翅膀;
- 身体和肚皮;
- 头;
- 角;
- 眼睛;
- 腮红、嘴巴、鼻孔;
- 背景装饰。
如果先画身体再画尾巴,尾巴就会被身体盖住,看起来像没有尾巴。所以顺序本身也是逻辑的一部分。
5. 完整实战代码:绘制一只可爱小龙
5.1 完整代码总览
下面就是完整代码。建议新建一个 Python 文件,命名为 cute_dragon.py,然后把代码复制进去运行。
python复制# 文件:cute_dragon.py
import turtle
import math
# ---------- 配色常量 ----------
OUTLINE = "#2E7D32" # 深绿色轮廓
BODY = "#4CAF50" # 身体主色
BELLY = "#C8E6C9" # 肚皮浅绿
WING = "#81D4FA" # 翅膀浅蓝
HORN = "#FFB74D" # 角橙色
EYE = "#FFFFFF" # 眼白
PUPIL = "#1B1B1B" # 瞳孔
CHEEK = "#F48FB1" # 腮红粉
MOUTH = "#8B0000" # 嘴巴深红
STAR = "#FFD54F" # 星星黄色
# ---------- 画布与画笔 ----------
screen = turtle.Screen()
screen.setup(900, 700)
screen.bgcolor("MistyRose")
screen.title("可爱小龙")
t = turtle.Turtle()
t.speed(6)
t.pensize(2)
t.hideturtle()
def draw_ellipse(center_x, center_y, rx, ry, fill_color, outline_color=OUTLINE):
"""以 (center_x, center_y) 为中心画水平椭圆。"""
t.penup()
t.goto(center_x + rx, center_y)
t.pendown()
t.pencolor(outline_color)
t.fillcolor(fill_color)
t.begin_fill()
steps = 72
for i in range(steps + 1):
angle = math.radians(i * 360 / steps)
x = center_x + rx * math.cos(angle)
y = center_y + ry * math.sin(angle)
t.goto(x, y)
t.end_fill()
def draw_circle(center_x, center_y, radius, fill_color, outline_color=OUTLINE):
"""以 (center_x, center_y) 为圆心画正圆。"""
draw_ellipse(center_x, center_y, radius, radius, fill_color, outline_color)
def draw_tail():
"""尾巴:从身体左侧向左上方翘起。"""
t.penup()
t.goto(-85, -35)
t.pendown()
t.pensize(8)
t.pencolor(BODY)
t.setheading(180)
t.circle(-35, 100)
t.circle(22, 70)
t.pensize(2)
def draw_leg(x_center, y_center):
"""在指定位置画一条腿和脚掌。"""
t.penup()
t.goto(x_center, y_center)
t.pendown()
t.pensize(12)
t.pencolor(BODY)
t.setheading(270)
t.forward(30)
t.pensize(2)
draw_ellipse(x_center, y_center - 35, 14, 8, BODY)
def draw_wing():
"""背上的小翅膀。"""
t.penup()
t.goto(20, 25)
t.pendown()
t.fillcolor(WING)
t.begin_fill()
t.goto(0, 95)
t.goto(35, 90)
t.goto(70, 80)
t.goto(60, 30)
t.goto(40, 10)
t.goto(20, 25)
t.end_fill()
def draw_body():
"""身体与肚皮。"""
draw_ellipse(0, -50, 85, 55, BODY)
draw_ellipse(0, -55, 45, 28, BELLY)
def draw_head():
"""圆圆的头。"""
draw_circle(75, 0, 48, BODY)
def draw_horns():
"""头顶两侧的小角。"""
t.penup()
t.goto(52, 28)
t.pendown()
t.fillcolor(HORN)
t.begin_fill()
t.goto(40, 78)
t.goto(72, 42)
t.goto(52, 28)
t.end_fill()
t.penup()
t.goto(75, 45)
t.pendown()
t.begin_fill()
t.goto(82, 92)
t.goto(97, 40)
t.goto(75, 45)
t.end_fill()
def draw_eyes():
"""大眼睛:眼白、瞳孔、高光三层。"""
eyes = [(60, 8), (90, 8)]
for ex, ey in eyes:
draw_circle(ex, ey, 14, EYE)
pupils = [(64, 10), (94, 10)]
for px, py in pupils:
draw_circle(px, py, 7, PUPIL)
draw_circle(61, 14, 3, "#FFFFFF", outline_color="#FFFFFF")
draw_circle(91, 14, 3, "#FFFFFF", outline_color="#FFFFFF")
def draw_cheeks():
"""脸颊上的腮红。"""
draw_circle(50, -12, 10, CHEEK, outline_color=CHEEK)
draw_circle(92, -12, 10, CHEEK, outline_color=CHEEK)
def draw_mouth():
"""嘴巴:用折线画出微笑。"""
t.penup()
t.goto(52, -20)
t.pendown()
t.pensize(2)
t.pencolor(MOUTH)
t.goto(60, -27)
t.goto(68, -20)
t.goto(76, -27)
t.goto(84, -20)
def draw_nose():
"""两个小鼻孔。"""
draw_circle(72, -8, 3, PUPIL, outline_color=PUPIL)
draw_circle(82, -8, 3, PUPIL, outline_color=PUPIL)
def draw_star(x, y, size, color=STAR):
"""在指定位置画一颗五角星。"""
t.penup()
t.goto(x, y)
t.pendown()
t.fillcolor(color)
t.begin_fill()
t.setheading(0)
for _ in range(5):
t.forward(size)
t.right(144)
t.end_fill()
def draw_decorations():
"""画背景星星,让画面更完整。"""
draw_star(-180, 230, 40)
draw_star(150, 180, 30)
draw_star(-260, -150, 25)
draw_star(260, -180, 35)
def main():
"""按遮挡关系依次绘制小龙。"""
draw_tail()
draw_leg(-30, -95)
draw_leg(30, -95)
draw_wing()
draw_body()
draw_head()
draw_horns()
draw_eyes()
draw_cheeks()
draw_mouth()
draw_nose()
draw_decorations()
screen.exitonclick()
if __name__ == "__main__":
main()
5.2 关键函数讲解
这里重点说一下两个函数的设计思路。
draw_ellipse 是整个项目的几何基础。它接收圆心坐标、水平半径、垂直半径和填充颜色,然后通过 math.cos 与 math.sin 计算椭圆上的点。在画身体时,我用 85 和 55 作为水平和垂直半径,这样身体是横向的椭圆,更像小动物的身体。在画肚皮时,把半径缩小并向下偏移,形成肚子内凹的视觉效果。
draw_eyes 则体现了“多层叠加”的绘图思想。眼白是一个白色圆,瞳孔是一个深灰圆,高光是一个更小的白色圆。三个圆依次叠加,眼睛立刻有了光泽感。如果需要让小龙眼珠看向不同方向,只需要修改瞳孔的坐标。
draw_leg 里的脚掌也是类似思路:先画一条粗短腿,再在腿末端画一个扁平椭圆作为脚掌。这样比直接画两条线更完整。
5.3 运行与预期效果
运行方式:
bash复制python cute_dragon.py
如果一切正常,会弹出一个玫瑰米色背景的窗口,窗口标题是“可爱小龙”。随后可以看到一只绿色 Q 版小龙缓缓被画出来:尾巴先出现,然后是腿和翅膀,接着是身体和圆圆的头,最后是角、眼睛、腮红、嘴巴和鼻孔。
画面顶部和底部还有四颗黄色星星作为装饰。绘制结束后,窗口不会自动关闭,点击画面任意位置即可退出程序。
6. 常见问题与排查
6.1 问题速查表
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
No module named 'tkinter' |
Linux 环境缺少 tk 图形库 | 安装 python3-tk,重新验证导入 |
| 窗口一闪而过 | 脚本没有阻塞等待点击 | 在末尾加 screen.exitonclick() |
| 海龟图标挡住画面 | 没有隐藏海龟 | 调用 t.hideturtle() |
| 画出来不是闭合图形 | 填充路径没有封闭 | 检查 begin_fill 后是否回到起点 |
| 图形位置偏出窗口 | 坐标规划不合理 | 适当缩小半径,或者微调坐标 |
| 画得慢 | 精度太高或速度太低 | 增大 t.speed(),减少 steps |
| 关闭窗口后再次运行报错 | 海龟对象被销毁后继续调用 | 把逻辑放入 main(),每次重新创建画笔 |
6.2 典型问题详解
第一个比较常见的问题是 tkinter 缺失。Windows 下官方 Python 通常没有这个问题;Linux 下如果你使用 apt 安装的 Python,可能只安装了标准库的一部分,需要额外安装 tk 图形组件。
第二个常见问题是窗口一闪而过。很多初学者在代码最后没有加 screen.exitonclick() 或 turtle.done(),脚本执行完最后一句话后进程退出,窗口立刻关闭。解决办法就是让程序在绘图结束后保持等待状态。
第三个问题是绘制速度。draw_ellipse 默认把圆形分成 72 个点连接,这已经能满足视觉要求,但如果电脑性能偏低,可以改成 48 或者 60。设置 t.speed(0) 可以最大化绘图速度,不过有时候速度太快反而体验不到绘制过程。
7. 进阶优化方向
7.1 参数化小龙
目前小龙的尺寸是写死的。如果想把龙变大或者变小,可以给每个函数增加一个缩放比例参数。例如:
python复制def draw_body(scale=1.0):
draw_ellipse(0, -50, 85 * scale, 55 * scale, BODY)
draw_ellipse(0, -55, 45 * scale, 28 * scale, BELLY)
这样,调用 draw_body(1.2) 就能画出一只更大的龙。如果你想把整个龙复制到画布的另一侧,还可以在绘制前统一平移坐标系:
python复制t.penup()
t.goto(150, 0)
t.pendown()
利用坐标偏移和缩放参数,最终可以把小龙封装成一个可复用的绘图对象。
7.2 让小翅膀动起来
turtle 支持最基本的动画效果,思路是“擦掉重画”。例如利用 t.clear() 清除最近一次绘制结果,然后改变翅膀函数中的坐标参数,再重新绘制。
更简单的方法是用多个静态帧配合 time.sleep() 模拟动态效果。不过 turtle 的定位是基于矢量路径的动画,适合做简单交互;如果想做复杂动画,建议转到 Pygame 或前端 Canvas。
7.3 导出图片
turtle 本身没有直接保存 PNG 的功能,但可以借助底层 canvas 对象导出 PostScript 文件:
python复制screen.getcanvas().postscript(file="dragon.ps")
生成的 .ps 文件需要借助 Ghostscript、ImageMagick 或 Pillow 才能转换成 PNG。这里不再展开,但思路是可以拓展的。如果你要批量生成图片,更推荐先用 turtle 生成路径数据,再用 matplotlib 或 Pillow 绘制,这样能避免弹窗依赖。
8. 最佳实践与工程建议
8.1 函数职责要单一
从上面的代码可以看到,每个绘制函数只负责一个身体部位。这样代码读起来非常清晰:draw_tail() 画尾巴,draw_eyes() 画眼睛,draw_body() 画身体。如果某个部位出错,只需要进入对应函数排查,不用在大段代码里寻找。
这种“职责单一”的思想,不只适用于 turtle,也适用于日常的 Python 项目、Web 接口和数据处理脚本。
8.2 颜色、尺寸用常量集中管理
把颜色定义为常量,是防止混乱的好习惯。如果你直接在代码里写几十个 "#4CAF50",后期想换主题色时会非常痛苦。集中定义的好处是:修改一个常量,全图同步更新。
尺寸也一样。身体半径、头半径这些数值,建议放在代码顶部或者函数开头集中说明。尤其是给初学者使用的时候,常量名本身就是注释。
8.3 坐标尽量使用相对偏移
在绘制多个部位时,不要每个地方都凭空写死绝对坐标。例如腿的坐标,是在身体下方的偏移;眼睛的坐标,是在头部范围内的偏移。先把身体中心定义为一个变量,再由它派生出其他部位坐标,这样调整整个龙的位置时,只需要改动基础坐标。
如果每一步都用绝对坐标,即使只是想让整只龙向右平移 50 像素,也需要改动十几个函数,非常容易出错。
8.4 绘制顺序决定了图层的遮挡关系
记住一个原则:先画后面的,再画前面的。尾巴、翅膀这类部位要先画,否则会被身体挡住;肚皮要覆盖在身体内部,所以以独立椭圆叠在身体上方;眼睛要最后画在头顶上,才会清晰可见。
8.5 分享代码时提供完整运行入口
如果你要把代码分享给同事或学生,一定要有清晰的入口函数和运行说明。入口函数是 main(),条件判断 if __name__ == "__main__" 保证脚本被导入时不会自动运行。这些细节虽然简单,却是工程化代码的基本素养。
9. 总结与下一步
这个项目虽然叫“画只可爱小龙〰”,但它真正教会你的不是画龙,而是如何用程序控制坐标、颜色、图层和形状的组合。
跑通代码之后,建议做几件小事:
- 把小龙的绿色改成蓝色或紫色,看看配色对气质的影响;
- 把头部半径增大,让小龙变得更 Q 更萌;
- 把尾巴的弧线参数改一下,试着画一个不同卷曲方向的尾巴;
- 尝试自己设计一个新角色,比如小猫、小狗或小恐龙。
我给出的坐标只是一个参考版本,最终让小龙“可爱”起来的,往往是你反复调整出来的细节。把这套代码保存下来,之后想画其他角色时,直接复制函数骨架再改坐标,会比自己从零开始画快很多。
如果这篇文章对你有帮助,可以收藏备用。也欢迎把你画出来的小龙造型分享出来,大家一起讨论怎么让 turtle 的角色更生动。
