用Python打造PPT生成工具:模板驱动+数据填充实战

1. 为什么我要自己动手做一个 PPT 生成工具

先说说我做这个项目的起因。我在一家做企业培训服务的公司待了快六年,岗位名义上是"课程研发",实际上从大纲撰写、讲师手册、学员讲义到课后汇报 PPT,全是我一个人包圆。最夸张的一个月,我同时推进四个不同主题的培训项目,每个项目至少三套 PPT:讲师版、学员版、汇报版。那段时间我每天的状态就是打开模板、复制粘贴、调字号、对齐文本框,然后第二天重复一遍。

市面上不是没有工具。在线模板站、AI 生成 PPT 的服务、各种插件,我几乎试了个遍。但用下来总有几个绕不过去的坎:要么是生成的内容空洞,标题下面全是"此处填写内容";要么是排版完全不可控,生成出来的东西字号忽大忽小,图片位置随机;要么是数据必须上传到别人的服务器,公司内部资料根本不敢往里放。最要命的是,这些工具大多只能处理"从零生成"这一个场景,而我真正高频的需求其实是"把已有的结构化内容快速套进一套统一风格里"。

所以ppt-master这个项目,本质上是我给自己造的一把趁手工具。它的定位很明确:输入一份结构化的内容(比如 Markdown 大纲、Excel 数据表、或者一段 JSON),输出一套风格统一、排版规整、可以直接拿去讲的 PPT 文件。它不追求"一键生成惊艳全场的大片",而是解决"内容已经有了,怎么快速变成能用的幻灯片"这个具体问题。

适合谁来参考呢?如果你也经常需要批量产出 PPT,比如做培训、做咨询报告、做项目周报、做课程讲义,而且对排版一致性有要求,那这套思路应该能帮到你。哪怕你完全不懂编程,我也会把核心逻辑讲清楚,你可以用现成工具组合出类似效果;如果你会一点 Python,那基本可以照着我的方案直接复现。

2. 整体设计思路:为什么是"模板驱动 + 数据填充"而不是"AI 自由生成"

2.1 先想清楚:PPT 的本质是"结构化容器"

我踩过的第一个坑,就是一开始迷信"AI 全自动生成"。我试过让模型直接输出完整的 PPT 文件,结果发现两个致命问题:第一,模型对版式的控制力极差,它不知道一个文本框放多少字会溢出,也不知道图片和文字的相对位置怎么摆才不别扭;第二,每次生成的结果风格都不一样,今天蓝色调明天绿色调,做系列课程的时候简直灾难。

后来我想明白了,PPT 这个东西,本质上是一个结构化的容器。一页幻灯片拆开来看,无非就是几个"槽位":标题槽、正文槽、图片槽、图表槽、页脚槽。真正需要变化的只是槽里的内容,而槽本身的位置、大小、字体、颜色,应该是固定的。这就像做菜,盘子是固定的,你换的是菜,不是盘子。

所以ppt-master的核心架构就定下来了:模板层负责"长什么样",数据层负责"写什么",渲染层负责"把数据塞进模板"。三层解耦,各管各的。

2.2 技术选型:为什么最终落在 python-pptx 上

选型这块我对比过好几条路线,列个表说清楚我的取舍逻辑。

方案优势致命短板我的判断
直接操作 XML控制力最强学习成本极高,改一个属性要翻半天规范放弃,维护不起
调用在线生成服务上手快数据要外传,排版不可控,有调用限制放弃,合规和稳定性都不行
浏览器端 JS 方案可视化好服务端批量处理麻烦,字体依赖环境放弃,不适合批处理
python-pptx纯本地、API 清晰、能读写现有模板复杂动画支持弱采用,够用且稳

python-pptx这个库最大的好处是,它允许你打开一个已经做好的 PPT 模板文件,然后像操作对象一样去替换里面的文字、插入图片、复制页面。这意味着我可以先在 PowerPoint 里把版式调到自己满意,再用代码去批量填充。这个工作流非常符合直觉:设计归设计,生产归生产。

提示:python-pptx对幻灯片母版(Slide Master)和版式(Layout)的支持是完整的,这是它能做模板驱动的关键。很多人不知道这一点,以为它只能从空白文档开始画,其实完全可以从现成模板出发。

2.3 数据源设计:为什么我坚持用 Markdown 作为主输入

内容输入格式我最终选了 Markdown,理由有三个。第一,Markdown 写起来快,层级天然清晰,#是一级标题,##是二级标题,正好对应 PPT 的章节页和内容页。第二,Markdown 是纯文本,方便版本管理,改了什么一目了然。第三,它足够通用,不管是自己写、从文档导出、还是让模型帮忙整理,都能落到 Markdown 上。

我定义的映射规则很简单:一级标题生成一张章节过渡页,二级标题生成一张内容页的标题,标题下面的正文段落和列表项填充到内容区。如果某个二级标题下有多张图片,就自动拆成多页。这套规则不复杂,但覆盖了我 90% 的使用场景。

3. 核心细节拆解:模板槽位、样式继承与内容映射

3.1 模板槽位怎么设计才不容易崩

模板是整个项目的地基,地基没打好,后面全是返工。我总结了几条设计模板时的硬性经验。

第一,占位符命名要规范。在 PowerPoint 里给每个文本框、图片框设置占位符名称,比如title_main、body_content、image_hero、footer_page。代码里就靠这些名字去定位元素。如果名字乱起,后面维护就是噩梦。我一开始图省事用默认名字,结果模板一改,代码全找不到元素,排查了半天。

第二,一个槽位只干一件事。我见过有人把标题和副标题塞进同一个文本框,靠换行区分。这种做法在代码填充时极其痛苦,因为你没法单独控制副标题的样式。正确做法是拆成两个独立的占位符,各管各的。

第三,预留内容溢出空间。正文框的高度要按"最多可能出现的字数"来设计,而不是按"平均字数"。我吃过亏,有一次一份内容特别长,文字直接溢出到幻灯片外面,打印出来惨不忍睹。后来我在代码里加了字数检测,超过阈值就自动缩小字号或者拆页。

3.2 样式继承:让改一处、全局生效成为可能

模板驱动最大的价值在于样式统一,而样式统一的关键是继承。在 PowerPoint 里,字体、颜色、项目符号这些属性,应该尽量定义在母版和版式层面,而不是在每个文本框上单独设置。

我举个具体例子。我一开始在每个内容页的文本框上都手动设了"微软雅黑、18 号、深灰色"。后来公司品牌色调整,要把深灰改成深蓝,我不得不打开每一页去改,几十页改到崩溃。后来我把这些样式全部上提到母版,文本框只保留内容,颜色和字体走继承。再改的时候,只动母版一处,所有页面自动更新。

代码层面,python-pptx在替换文字时,默认会保留原有的段落格式。所以只要模板里样式设对了,填充进去的文字会自动继承。这一点非常省心,但前提是你模板本身要规范。

3.3 内容映射:从 Markdown 到幻灯片的转换逻辑

映射逻辑我写成了一个独立的解析模块,核心流程是这样的:

  1. 读取 Markdown 文本,按行扫描,识别标题层级和正文块。
  2. 遇到一级标题,记录为一个"章节",准备生成章节页。
  3. 遇到二级标题,记录为一个"内容单元",准备生成内容页。
  4. 标题下的普通段落、无序列表、有序列表,统一收集为该内容单元的正文。
  5. 遇到图片语法![alt](path),记录图片路径,准备插入。
  6. 按顺序遍历所有章节和内容单元,逐个调用渲染函数。

这里有个细节值得说:列表项的处理。Markdown 的列表在 PPT 里最好呈现为带项目符号的段落,而不是一堆独立的文本框。我的做法是把列表项合并成一个段落集合,每个列表项作为一个段落,统一设置项目符号。这样既保持了语义,又方便控制行距。

注意:Markdown 里的嵌套列表(二级、三级缩进)在 PPT 里处理起来比较麻烦,我的建议是限制到两级,再深就拆成新的一页,否则缩进层级在幻灯片上会显得很挤。

4. 实操过程:从零搭出一套可复用的生成流程

4.1 环境准备与依赖安装

先把环境搭起来。我用的是 Python 3.9 以上版本,核心依赖就一个python-pptx,另外为了处理图片尺寸,加了Pillow。

pip install python-pptx Pillow

如果你还要从 Excel 读数据,再加一个openpyxl。依赖不多,装起来很快。我建议用虚拟环境,避免和系统里的其他包冲突。

python -m venv venv source venv/bin/activate # Windows 下用 venv\Scripts\activate pip install python-pptx Pillow openpyxl

4.2 制作模板文件的关键步骤

打开 PowerPoint,新建一个空白演示文稿,然后按下面的顺序操作。

第一步,进入"视图"菜单,打开"幻灯片母版"。在这里设置全局的字体、配色、页脚样式。我一般会把正文字体设为微软雅黑,标题字体设为思源黑体,颜色用公司品牌色。

第二步,关闭母版视图,回到普通视图。在"版式"里选择或新建几个版式:一个"章节页"版式,一个"内容页"版式,一个"图文页"版式。每个版式里放好占位符。

第三步,给占位符命名。选中占位符,在"开始"菜单里找到"选择窗格",在里面双击名称就能改。把标题框改成title_main,正文框改成body_content,图片框改成image_hero。

第四步,保存为.pptx格式,放在项目目录下,命名为template.pptx。

这套模板做好之后,基本可以长期复用。后面内容再怎么变,模板不用动。

4.3 核心渲染代码的编写

下面是我项目里最核心的一段渲染逻辑,做了简化,但关键点都在。

from pptx import Presentation from pptx.util import Pt import re def parse_markdown(md_text): """把 Markdown 解析成章节和内容单元的列表""" sections = [] current_section = None current_unit = None for line in md_text.splitlines(): line = line.rstrip() if line.startswith('# '): current_section = {'title': line[2:].strip(), 'units': []} sections.append(current_section) current_unit = None elif line.startswith('## '): current_unit = {'title': line[3:].strip(), 'body': [], 'images': []} if current_section is None: current_section = {'title': '', 'units': []} sections.append(current_section) current_section['units'].append(current_unit) elif line.startswith('!['): match = re.match(r'!\[.*?\]\((.*?)\)', line) if match and current_unit is not None: current_unit['images'].append(match.group(1)) elif line.strip() and current_unit is not None: current_unit['body'].append(line.strip()) return sections def fill_placeholder(slide, name, text): """按占位符名称填充文字""" for shape in slide.shapes: if shape.name == name and shape.has_text_frame: tf = shape.text_frame tf.clear() tf.paragraphs[0].text = text return True return False def render(template_path, sections, output_path): prs = Presentation(template_path) layout_section = prs.slide_layouts[1] # 章节页版式 layout_content = prs.slide_layouts[2] # 内容页版式 for sec in sections: # 生成章节页 slide = prs.slides.add_slide(layout_section) fill_placeholder(slide, 'title_main', sec['title']) for unit in sec['units']: slide = prs.slides.add_slide(layout_content) fill_placeholder(slide, 'title_main', unit['title']) body_text = '\n'.join(unit['body']) fill_placeholder(slide, 'body_content', body_text) prs.save(output_path)

这段代码的逻辑很直白:解析、遍历、填充、保存。真正让它好用起来的,是前面模板的规范性和后面细节的打磨。

4.4 参数计算:字号和行距怎么定才不溢出

这是很多人忽略但极其重要的一环。PPT 里文字溢出,十有八九是字号和框高没算对。我总结了一个粗略的估算公式。

假设正文框的高度是 H 厘米,字号是 S 磅,行距是 L 倍。那么一页能容纳的行数大约是:

行数 ≈ (H * 28.35) / (S * L * 1.2)

其中 28.35 是厘米转磅的系数,1.2 是经验修正值。举个例子,框高 10 厘米,字号 18 磅,行距 1.5 倍,那么:

行数 ≈ (10 * 28.35) / (18 * 1.5 * 1.2) ≈ 283.5 / 32.4 ≈ 8.75 行

也就是说这个框大概能放 8 到 9 行。如果内容超过这个数,就得考虑缩小字号或者拆页。我在代码里加了一个自动判断:先按默认字号算行数,超了就逐级降字号,降到 14 磅还放不下就自动拆成两页。这个逻辑救了我无数次。

提示:这个公式是经验值,不同字体、不同行距会有偏差。建议你用自己的模板实测几次,校准出一个适合自己场景的系数。

5. 常见问题与排查技巧实录

5.1 填充后文字样式变了怎么办

这是最高频的问题。现象是:模板里明明设好了字体和颜色,代码填进去之后变成了默认的宋体黑色。原因通常是tf.clear()把原有段落的格式也清掉了。

解决办法有两个。一是不要用clear(),而是直接修改现有段落的文字,保留格式。二是填充后重新设置字体属性。我一般用第一种,更省事。

# 保留格式的写法 tf = shape.text_frame tf.paragraphs[0].text = text # 直接赋值,不清空

如果内容有多段,就逐段赋值,不要一次性清空。

5.2 图片插入后变形或位置不对

图片变形是因为没有保持宽高比。python-pptx插入图片时,如果不指定尺寸,它会按原始尺寸放,可能超出页面。我的做法是先读取占位符的宽高,然后按比例缩放图片,让它刚好填满占位符。

from PIL import Image def fit_image(placeholder, img_path): img = Image.open(img_path) img_ratio = img.width / img.height ph_ratio = placeholder.width / placeholder.height if img_ratio > ph_ratio: new_width = placeholder.width new_height = int(new_width / img_ratio) else: new_height = placeholder.height new_width = int(new_height * img_ratio) left = placeholder.left + (placeholder.width - new_width) // 2 top = placeholder.top + (placeholder.height - new_height) // 2 return left, top, new_width, new_height

这段逻辑就是经典的"等比缩放 + 居中",算出来的位置和尺寸直接传给add_picture就行。

5.3 批量生成时速度慢怎么优化

如果一次要生成上百页,速度确实会慢。我实测下来,瓶颈主要在图片处理上。优化思路是:图片先统一压缩到合适尺寸再插入,不要每次都读原图。另外,模板文件只加载一次,不要每页都重新Presentation()。

下面这张表是我整理的问题速查表,遇到问题可以先对照排查。

现象可能原因解决方向
文字样式丢失清空了段落格式改用直接赋值,保留格式
图片变形未保持宽高比按占位符比例等比缩放
文字溢出页面字号过大或内容过长自动降字号或拆页
找不到占位符名称不匹配检查选择窗格里的命名
生成速度慢重复加载模板或图片过大模板只加载一次,图片预压缩
章节页缺失Markdown 层级不规范统一用一级标题做章节

5.4 几个我踩过的坑

第一个坑,模板里用了特殊字体,换台电脑就变样。解决办法是把字体嵌入 PPT,或者干脆用系统通用字体。我后来统一用微软雅黑,跨平台基本没问题。

第二个坑,占位符被误删。有次我调整模板,不小心删了一个正文占位符,代码跑起来直接报错。后来我加了一个校验函数,生成前先检查所有必需的占位符是否存在,缺了就提前报错,而不是跑到一半崩掉。

第三个坑,中文换行问题。英文按空格换行,中文是按字符换的,有时候会出现标点符号跑到行首的情况。这个在 PPT 里不好完全避免,我的经验是适当调整文本框宽度,给标点留点余地。

6. 这套方案还能怎么扩展

项目跑顺之后,我又做了几个扩展,这里分享两个我觉得最实用的。

一个是接 Excel 数据源。做数据汇报的时候,我把表格里的每一行映射成一页,列名映射成占位符。这样一份几十行的数据,几秒钟就能变成一套汇报 PPT。核心代码就是把 Markdown 解析换成 Excel 读取,后面的渲染逻辑完全复用。

另一个是加一个简单的配置文件。把字号、行距、颜色这些参数抽到一个 YAML 文件里,不同项目用不同配置,不用改代码。这个改动不大,但让工具的适应性上了一个台阶。

我个人的体会是,做这类工具,不要一上来就追求大而全。先把一个最痛的点解决掉,跑通闭环,再慢慢加功能。我最初版本只有一百多行代码,只能处理最简单的标题加正文,但就是这一百多行,帮我省下了大量重复劳动。后面所有的扩展,都是在这个稳定内核上长出来的。

如果你也想动手,我的建议是先从你自己的一个真实需求出发,做一个小到不能再小的版本,用起来,再迭代。工具是长出来的,不是设计出来的。

相关新闻

如何上手UserRepGen-4B:从模型下载到首次生成的新手完整教程

如何上手UserRepGen-4B:从模型下载到首次生成的新手完整教程

人工智能大模型微调 【免费下载链接】UserRepGen-4B 项目地址: https://ai.gitcode.com/hf_mirrors/microsoft/UserRepGen-4B 点击查看 免费下载 UserRepGen-4B 是微软开源的一款用户表征生成模型,基于 Qwen3.5-4B 微调,能够从多轮用户与助手…

2026/10/20 5:57:21 阅读更多 →
字母象形与五行水象:从W/S/M到命名设计的认知工具

字母象形与五行水象:从W/S/M到命名设计的认知工具

把“字母象形”和“阴阳五行”放在一起琢磨,原本是一件很容易被人当成故弄玄虚的事;但如果你手里刚好有一串字母——w,s,m,f,o,e,n,i——并且试图解释它们为什么都跟“水”有关系,这事就变得特别有意思。我先说结论:这串对应关系不…

2026/10/20 5:56:20 阅读更多 →
PC寄存器详解:CPU程序计数器原理与跨架构实践

PC寄存器详解:CPU程序计数器原理与跨架构实践

1. 什么是PC寄存器?它不是“电脑”,而是CPU的“导航员”很多人第一次看到“PC寄存器”这个词,下意识以为是“Personal Computer”的缩写,结果一查发现完全不是一回事——这恰恰暴露了计算机底层概念最容易被表层符号误导的典型陷阱…

2026/10/20 5:56:20 阅读更多 →

最新新闻

Mem0实践:给大模型装上海马体,实现AI长期记忆与个性化对话

Mem0实践:给大模型装上海马体,实现AI长期记忆与个性化对话

1. 项目概述1.1 这个项目是做什么的“Mem0:给 AI 加上长期记忆”,光看标题就很有意思——它解决的是一个让所有搞 AI 应用的人都头疼的问题:大模型记不住事儿。用过 ChatGPT 的朋友都知道,你上午跟它聊完项目方案,下午…

2026/10/20 6:43:40 阅读更多 →
Matlab/Simulink下的5MW海上永磁直驱风电并网仿真全解析

Matlab/Simulink下的5MW海上永磁直驱风电并网仿真全解析

看到“Matlab/Simulink 平台—5MW海上永磁风电直驱1200V风电并网”这个标题,估计不少做风电仿真的人第一反应是“又一套毕业设计模型”。但说实话,把“5MW海上直驱”和“1200V直流母线”这两个数字摆在一起,再配上真实风速时序数据去跑并网&a…

2026/10/20 6:43:40 阅读更多 →
学生党云GPU训练全攻略:从SSH连接到省钱实战

学生党云GPU训练全攻略:从SSH连接到省钱实战

最近好几个朋友都跑来跟我吐槽:实验室的卡只有6G显存,跑个预训练语言模型都要看它脸色,再往上做点生成式模型的微调,内存和显存直接爆掉。学生党想认真做深度学习模型训练,最现实的路径还真不是攒钱买卡,而…

2026/10/20 6:43:40 阅读更多 →
I2S输出场景通话回音排查指南:AEC参考路径与增益调试实战

I2S输出场景通话回音排查指南:AEC参考路径与增益调试实战

先说一个我自己调试时经常遇到的场景:在杰理平台做蓝牙音箱、车载免提这类项目时,音频通路为了接外置D类功放或者独立Codec,会习惯性地把音源输出配置成I2S输出。音乐和提示音一切正常,但只要一进入通话,对方马上反馈听…

2026/10/20 6:43:40 阅读更多 →
FLUX.2-klein-base-9b-nvfp4 实测对比:vs BF16 全精度版,画质、速度与显存一文看懂

FLUX.2-klein-base-9b-nvfp4 实测对比:vs BF16 全精度版,画质、速度与显存一文看懂

【免费下载链接】FLUX.2-klein-base-9b-nvfp4 项目地址: https://ai.gitcode.com/hf_mirrors/black-forest-labs/FLUX.2-klein-base-9b-nvfp4 点击查看 免费下载 FLUX.2-klein-base-9b-nvfp4 是 Black Forest Labs 官方发布的 90 亿(9B)参数…

2026/10/20 6:43:40 阅读更多 →
采购制度如何落地:从PDF到ERP的流程控制点拆解

采购制度如何落地:从PDF到ERP的流程控制点拆解

简介:一份恒大中国控股有限公司采购管理制度全文PDF,适合房地产企业采购、成本管理、供应商管理相关从业者及制度编写参考。内容系统覆盖供应商资质审查与绩效评价、集中采购与联合采购模式、公开招标与竞争性谈判等采购方式,并明确集团成本管…

2026/10/20 6:42:40 阅读更多 →

日新闻

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
ESP32舵机控制指南:原理、多路驱动与避坑实战

ESP32舵机控制指南:原理、多路驱动与避坑实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
位移法实现一阶低通滤波:单片机ADC采样降噪实战

位移法实现一阶低通滤波:单片机ADC采样降噪实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/19 10:02:04 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/19 17:32:41 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 2:54:50 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 9:10:22 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 12:16:16 阅读更多 →