[Python] 借助 Turtle 逐字展示《醉翁亭记》

背景

turtle --- Turtle graphics 中提供了关于 turtle\text{turtle} turtle 模块的介绍。我想到可以用 turtle\text{turtle} turtle 来展示经典的古文,比如《醉翁亭记》,于是用 trae 完成了代码(我做了少量调整)。

正文

整体思路

我想借助 turtle --- Turtle graphics 来逐字展示《醉翁亭记》,示例效果如下图所示 ⬇️

有几个核心的问题

  • 界面的长和宽需要根据字数动态计算,而不是 hard-code
  • 每过一个固定的时间,就多展示一个字,我们可以在 main 方法里实现这个主循环

借助 trae,我们就不必自己处理这些细节了。在它生成的代码的基础上,再让它进行调整(或者自己亲自调整)即可。这次的代码,主要是 trae 完成的,我只做了些微小的调整,就不赘述写代码的过程了(我在它提供的代码的基础上,反复运行并让它继续调整)。

完整的代码

python 复制代码
import math
import time
from turtle import Screen, Turtle

ARTICLE = (
    "環滁皆山也。 其西南諸峰,林壑尤美。望之蔚然而深秀者,琅琊也。"
    "山行六七里,漸聞水聲潺潺,而瀉出於兩峰之間者,讓泉也。"
    "峰回路轉,有亭翼然臨於泉上者,醉翁亭也。作亭者誰?山之僧曰智僊也。"
    "名之者誰?太守自謂也。太守與客來飲於此,飲少輒醉,而年又最高,"
    "故自號曰醉翁也。醉翁之意不在酒,在乎山水之間也。"
    "山水之樂,得之心而寓之酒也。"
    "\n\n"
    "若夫日出而林霏開,雲歸而巖穴暝,晦明變化者,山間之朝暮也。"
    "野芳發而幽香,佳木秀而繁陰,風霜高潔,水落而石出者,山間之四時也。"
    "朝而往,暮而歸,四時之景不同,而樂亦無窮也。"
    "\n\n"
    "至於負者歌於途,行者休於樹,前者呼,後者應,傴僂提攜,"
    "往來而不絕者,滁人遊也。臨谿而漁,谿深而魚肥;釀泉為酒,泉香而酒洌;"
    "山肴野蔌,雜然而前陳者,太守宴也。宴酣之樂,非絲非竹,射者中,"
    "弈者勝,觥籌交錯,起坐而諠譁者,眾賓懽也;蒼顏白髮,"
    "頹然乎其間者,太守醉也。"
    "\n\n"
    "已而夕陽在山,人影散亂,太守歸而賓客從也。樹林陰翳,鳴聲上下,"
    "遊人去而禽鳥樂也。然而禽鳥知山林之樂,而不知人之樂;"
    "人知從太守遊而樂,而不知太守之樂其樂也。醉能同其樂,"
    "醒能述以文者,太守也。太守謂誰?廬陵歐陽脩也。"
)

CHAR_SIZE = 20
CHAR_WIDTH = 36
CHARS_PER_COLUMN = 20
COLUMN_SPACING = 40
MARGIN = 80

CHAR_DELAY_MS = 150

FONT_PATH = "/System/Library/Fonts/PingFang.ttc"

PUNCTUATION = set(",。、;:?!「」『』()【】《》""''......---·〈〉﹝﹞〔〕")


def load_article():
    return "".join(
        ch for ch in ARTICLE if ch not in PUNCTUATION and not ch.isspace()
    )


def draw_char(x, y, ch, pen):
    pen.penup()
    pen.goto(x, y)
    pen.pendown()
    font = (FONT_PATH, CHAR_SIZE, "normal")
    pen.write(ch, align="center", font=font)


def setup_screen(width, height):
    screen = Screen()
    screen.setup(width, height)
    screen.title("醉翁亭記")
    screen.tracer(0)
    return screen


def setup_pen():
    pen = Turtle()
    pen.hideturtle()
    pen.color("black")
    return pen


def draw_next_char(position, characters, screen, pen, start_x, start_y):
    col = position // CHARS_PER_COLUMN
    row = position % CHARS_PER_COLUMN

    x = start_x - col * COLUMN_SPACING
    y = start_y - row * CHAR_WIDTH

    ch = characters[position]
    draw_char(x, y, ch, pen)
    screen.update()


def main():
    characters = list(load_article())

    total = len(characters)
    column_count = math.ceil(total / CHARS_PER_COLUMN)
    content_width = (column_count - 1) * COLUMN_SPACING
    content_height = (CHARS_PER_COLUMN - 1) * CHAR_WIDTH + CHAR_SIZE

    screen_width = content_width + 2 * MARGIN
    screen_height = content_height + 2 * MARGIN

    start_x = screen_width // 2 - MARGIN
    start_y = screen_height // 2 - MARGIN - CHAR_SIZE

    screen = setup_screen(screen_width, screen_height)
    pen = setup_pen()

    for position in range(total):
        draw_next_char(position, characters, screen, pen, start_x, start_y)
        time.sleep(CHAR_DELAY_MS / 1000)

    screen.mainloop()


if __name__ == "__main__":
    main()

运行效果

请将以上代码保存为 main.py。使用如下命令可以运行 main.py

bash 复制代码
python3 main.py

运行中的效果如下图所示 ⬇️

全文都展示好之后,效果如下图所示 ⬇️

说明

字体

我是在 Mac 电脑上进行操作的,代码里有一个地方用到了系统相关的路径(具体位置如下图所示),如果您用的是其他系统,请将其替换为合适的值

《醉翁亭记》

为了便于读者使用,我把《醉翁亭记》全文嵌在代码里了,这并不是好的编程风格。

《醉翁亭记》的内容,我是从 这个链接 复制过来(为了方便展示,我删掉了其中的两个注释符号)的。《醉翁亭记》是北宋欧阳修的作品,所以没有版权问题。

参考资料

相关推荐
神也畏惧菠萝8 小时前
Day08 Python流程控制从入门到上瘾:if/match/while/for四件套,从B站登录一路打到猜数字游戏
python·算法·数据存储·for·while·if·ifelse
言乐68 小时前
通过 3D 动画直观演示球形瓶内可滑动半圆机构在倾倒时防粘盖、回正时刮膏体的运动学过程
python·3d·django·virtualenv·pygame
quantdash_cc8 小时前
ETF 行情数据清洗实战:字段语义拆解、标的池整理与五个校验清单
python·数据分析·pandas·量化交易·股票数据·quantdash
小熊恋旧8 小时前
Python 面向对象编程:方法、属性、继承与多态详解
开发语言·python
威哥爱编程9 小时前
Python 智能体开发实战 05:一问教材内容它就编?先把答案从你自己的资料里检索出来
python·agent
java_prinln9 小时前
RAG 面试题介绍
python·agent
威哥爱编程9 小时前
Python 智能体开发实战 06:一个函数里塞了八百行?用状态机把批改流程拆成能回退的步骤
python·agent
Patrick在香港10 小时前
一个斜杠,把毫秒变成十几秒
android·数据库·python·性能优化·正则表达式·回溯·redos
2601_9679801110 小时前
Python 初学者必备的常用内置函数
python·编程语言·内置函数·初学者·常用函数
OnlineProxy10 小时前
移动应用数据爬取:通过模拟器与流量嗅探对 API 进行逆向工程
爬虫·python·httpx