社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

小猪的Python学习之旅 —— 22.安静!吵到我用TNT了

coder-pig • 8 年前 • 904 次点击  

小猪的Python学习之旅 —— 22.安静!吵到我用TNT了

一句话概括本文:

灵感来自于5.15锤子鸟巢发布会上老罗用闪念胶囊一键生成了32张PPT, 原理利用Python-pptx库,通过编写模板的方式自动生成批量PPT!


引言:

锵锵锵,失踪人口回归,距离上一篇文章已过去14天。不是我偷懒不更新, 主要是转岗打杂了,很杂那种,每天要处理一堆和开发无关的琐事, 真的是敲码五分钟,打杂两小时...还是当个单纯的开发仔好啊。

标题没错,是第22篇不是21,不用回去翻,没看漏,第20篇写的是爬取Gank.io 接口的所有数据到MySQL,第21篇已经定好利用Flask编写一个API接口, 代码是实现了,部署还有些问题,所以还没些,先占个坑。

本节的话,是最近两天在折腾的一个东西,因为组内正在整在线早教 相关的东西,老师呢,要做很多的课件,但是大部分的内容都是重复的, 可能就图片会变下,流程可能会变一点,然后就找到我,让我想想有没有办法 自动生成减少她们的工作量,对,她们想要的就是 自动生成PPT!

还记得5.15锤子鸟巢发布会吗?不记得?看到这个图你应该想起什么了~

老罗现场展示了次时代电脑:TNT工作站 ,这里就不吐槽现场演示时的 各种小失误和理解万岁了。直播回顾的视频可以到B站看:

515锤子科技鸟巢发布会全程回顾

发布会直播我是有全程看完的,有个地方吸引了我的注意,在视频里69:08处, 老罗利用闪念胶囊直接就生成了32张PPT,给人一种错觉:

卧槽,好屌,以后连PPT都不用自己做了!

其实不然,如果是细心的观众基本会发现一个规律,生成的PPT都是非常简单 的PPT,一个黑色渐变的大背景,配几行字,或者再配个图片,PPT动画也没有。 来,来给你个这样的PPT生成给我看:

这种批量生成简单PPT的套路,我觉得思路无非这样:

根据情形,定义几套模板,然后约定一个规则,根据不同的内容调用 不同的模板,进行内容填充。

套路知道了,接下来就是看看Python有没有支持库了~ 找到两个pptx和win32com,本节只用前者,因为后者的文档是真的 看得人头皮发麻,而且网上的例子非常少...


1.python-pptx库


官方文档:python-pptx.readthedocs.io/en/latest/i… 官方仓库:github.com/scanny/pyth… 安装库:pip install python-pptx

对了,因为win32com那个库要调用微软的PowerPoint,我把电脑重装回 Win 10了,索性安装了最新版的PyCharm,然后发现创建的工程和以前 创建的工程结构不一样,多了个这样的东西:

终端运行也变成了:(venv) F:\Python> 这样,这个就是虚拟环境, 简单点说就是对开发环境进行隔离,比如你这个项目基于Python 2.x,另一个项目 基于Python 3.x,或者说着两个项目里依赖的同一个模块用着不同的版本,通过 虚拟环境可以让这两个项目互不干扰,将所需的包安装到独立的环境中。

Python中常用的创建和管理虚拟环境的工具有:virtualenv和pyvenv, Pycharm默认带有virtualenv,新建的时候就可以看到,具体怎么定制化, 自行百度吧~对了,下载模块都在**Lib/site-packages**目录下!


2.实现流程分析

首先的话,先想想有哪些模板,罗列下:

  • 1.只有一张图片
  • 2.只有一条文字
  • 3.一条文字和一张图片
  • 4.两条文字
  • 5.四条文字

然后布局大概这样咯:

接着定义一个规则,数据怎么传,这里采用最简单的套路,写个txt文件, 每行代表一个PPT,参数通过逗号间隔,于是完整的PPT对应这样的txt文件:

通过逗号分割参数,优先判断是否有图片,有的话走模板1,3, 其他再另外判断。好的,思路有了,接下来开始一步步实现吧。


3.代码实现

PS:这里不去介绍怎么用,看不懂自己去翻文档,我也是自己摸索, 有我写的示例参考,应该觉得很欣慰了。


1.定义一个厘米转英寸的方法

以为PPT里的位置和大小用到的单位都是厘米,需要转换下




    
# 厘米转英寸
def cm_to_in(cm):
    return Inches(cm / 2.54)

2.编写模板

先是模板1,传入Presentation的对象,这个你可以理解成PPT对象, 调用该对象的**.slides.add_slide()方法添加一张幻灯片,pptx库为我们 提供了八个不一样的模板,喜欢的可以自己一个个试,这里我们直接用第七 张空白幻灯片**,下标从0开始,所以是**prs.slide_layouts[6],幻灯片 加了之后,调用Presentation的save(ppt文件名)**函数打开生成的PPT, 然后点击设置 -> 幻灯片大小 -> 直接选择16:9或者设置幻灯片大小,比如我 的,这里的宽度和高度就是我们幻灯片的大小了,后面填充满屏的图片就要 用到这个。

然后调用add_picture函数添加一个满屏图片:

slide.shapes.add_picture(ppt_bg_path, cm_to_in(0), cm_to_in(0), cm_to_in(25.4), cm_to_in(14.288))

然后模板1就写完了,完成代码如下:

# 模板1:只有一张图片
def model_1(prs, pic_path):
    slide = prs.slides.add_slide(prs.slide_layouts[6])
    slide.shapes.add_picture(pic_path, cm_to_in(0), cm_to_in(0), cm_to_in(25.4), cm_to_in(14.288))
    
# 调用:
presentation = Presentation(ppt_file_name)
model_1(presentation, laoluo_bg_path)
    

打开生成的ppt:

哟,成功生成,接着到模板2:

填充满屏图片,然后新建一个文本框:

title_box = slide.shapes.add_textbox(cm_to_in(3.89), cm_to_in(5.35), cm_to_in(17.61), cm_to_in(3.59))

再接着添加一个文本域:

paragraph = title_box.text_frame.add_paragraph()

然后就可以对文本域里进行文字相关的操作了:

    paragraph.text = title  # 设置文本
    paragraph.vertical_anchor = MSO_VERTICAL_ANCHOR.MIDDLE  # 设置垂直方向对齐方式
    paragraph.alignment = PP_PARAGRAPH_ALIGNMENT.CENTER # 设置水平方向对齐方式
    paragraph.font.size = Pt(60)    # 设置文本大小,PT代表磅
    paragraph.font.name = '微软雅黑'    # 设置字体
    paragraph.font.color.rgb = RGBColor(255, 255, 255)  # 设置字体颜色

参数传递下,调用这个模板2,查看下生成的效果:

接下来如法炮制剩下的三个模板,主要是难点是控件的位置和宽高设置, 有两种操作:

最简单的操作:

随便填个坐标和宽高,运行后打开生成的PPT,自行调整 位置,然后记录下何时的位置和宽高,然后改代码。

复杂点的操作:

自行计算,比如模板5,三个小标题,先减去左右的间隔, 然后三等分,循环动态计算小标题的起始位置。


3.配置文件读取

接下来编写一个读取文件内容,调用对应方法的函数,代码如下

# 读取配置文件调用模板的方法
def read_rules(prs, filename):
    if os.path.exists(filename):
        with open(filename, 'r+', encoding='utf-8') as f:
            for rule in f:
                word_list = rule.replace('\n', '').split(',')
                if 'png' in rule or 'jpg' in rule:
                    if len(word_list) == 1:
                        model_1(prs, os.path.join(c.res_pictures, word_list[0]))
                    else:
                        model_3(prs, word_list[0], os.path.join(c.res_pictures, word_list[1]))
                else:
                    if len(word_list) == 1:
                        model_2(prs, word_list[0])
                    elif len(word_list) == 2:
                        model_4(prs, word_list[0], word_list[1])
                    elif len(word_list) == 4:
                        model_5(prs, word_list[0], word_list[1], word_list[2], word_list[3])

4.代码执行

调用配置文件读取的函数

if __name__ == '__main__':
    t.is_dir_existed(c.outputs_documents_path)
    ppt_existed(ppt_file_name)
    presentation = Presentation(ppt_file_name)
    read_rules(presentation, rules_path)
    presentation.save(ppt_file_name)

运行结果:


4.有些东西要说说

批量生成是挺爽的,不过呢,不支持直接生成动画哦!!! 然后这种简单的PPT,其实粘贴复制修改图片的效率可能比起你一个个 模板编写快一些...不得不说有些鸡肋,哦,对哦,这个还支持生成图表, 怎么整可以自行查阅官方文档。

另外的win32com的库,貌似功能更加强大,可能支持动画吧,但是文档是 真的难啃,就没有深入去研究了,有兴趣可以自己去折腾折腾。附上用 win32com这个库时遇到的问题的解决方法:

  • 1.Python3安装win32com
pip install pypiwin32
  • 2.哪里有win32com的文档

微软官网有,不过看到脑壳痛,介绍个工具:oleview,网上一搜一堆 不过这个用的时候会遇到一个问题,这里顺带记录下笔者win10电脑遇到的 一些情况:

IVIEWERS.DLL缺失:

网上搜下这个dll文件,下载完把文件复制到Windows/system32, 然后管理员模式打开命令提示符,键入:regsvr32 iviewers.dll 注册这个dll运行库就可以了。不过呢,win10 64位这样的执行是会报错的:

模块iviewers.dll可能与您正在运行的Windows版本不兼容,检查该模块是否与 regsvr.exe的x86或x64版本兼容

你要做的是把dll文件拷贝到Windows/SysWOW64,然后管理员模式打开命令提示符 cd到这个目录下,接着执行regsvr32 iviewers.dll即可。

如果出现:对DllRegisterServer的调用失败,错误代码为0x80070005 那是UAC的缘故,你没有以管理员模式打开命令提示符。

如果解决了,应该能正常打开,左侧招到PowerPoint那项,双击打开, 然后呢,这个工具不支持查找,你可以把内容全选然后复制到如Sublime Text 这样的代码查看工具上,ctrl + f 查找关键字,然后一步步定位出实现某个 功能需要用到的一些函数。


小结

本节讲解了一波利用Python-pptx批量生成N张PPT的套路,不禁再一次感叹 人生苦短,我用Python,最后祝六一儿童节快乐~


参考文献:


附:最终代码(都可以在:github.com/coder-pig/R… 找到):

import pptx
import config as c
import tools as t
from pptx import Presentation
from pptx.dml.color import RGBColor
from pptx.util import Inches, Pt
from pptx.enum.text import MSO_VERTICAL_ANCHOR, PP_PARAGRAPH_ALIGNMENT
import os

rules_path = os.path.join(c.res_documents, 'ppt_rules.txt')
ppt_bg_path = os.path.join(c.res_pictures, 'ppt_bg.png')
laoluo_bg_path = os.path.join(c.res_pictures, 'laoluo.jpg')
last_bg_path = os.path.join(c.res_pictures, 'last.png')
story_bg_path = os.path.join(c.res_pictures, 'story.png')
ppt_file_name = os.path.join(c.outputs_documents_path, 'result.pptx')


# 厘米转英寸
def cm_to_in(cm):
    return Inches(cm / 2.54)


# 判断课件是否存在,不存在的新建一个空白
def ppt_existed(ppt_name):
    if not os.path.exists(ppt_name):
        prs = Presentation()
        prs.slide_height = cm_to_in(14.35)
        prs.slide_width = cm_to_in(25.5)
        prs.save(ppt_name)


# 模板1:只有一张图片


    

def model_1(prs, pic_path):
    slide = prs.slides.add_slide(prs.slide_layouts[6])
    slide.shapes.add_picture(pic_path, cm_to_in(0), cm_to_in(0), cm_to_in(25.4), cm_to_in(14.288))


# 模板2:只有一个标题
def model_2(prs, title):
    slide = prs.slides.add_slide(prs.slide_layouts[6])
    slide.shapes.add_picture(ppt_bg_path, cm_to_in(0), cm_to_in(0), cm_to_in(25.4), cm_to_in(14.288))
    title_box = slide.shapes.add_textbox(cm_to_in(3.89), cm_to_in(5.35), cm_to_in(17.61), cm_to_in(3.59))
    paragraph = title_box.text_frame.add_paragraph()
    paragraph.text = title
    paragraph.vertical_anchor = MSO_VERTICAL_ANCHOR.MIDDLE
    paragraph.alignment = PP_PARAGRAPH_ALIGNMENT.CENTER
    paragraph.font.size = Pt(60)
    paragraph.font.name = '微软雅黑'
    paragraph.font.color.rgb = RGBColor(255, 255, 255)


#  模板3:有字,有图片
def model_3(prs, title, pic_path):
    slide = prs.slides.add_slide(prs.slide_layouts[6])
    slide.shapes.add_picture(ppt_bg_path, cm_to_in(0), cm_to_in(0), cm_to_in(25.4), cm_to_in(14.288))
    img = slide.shapes.add_picture(pic_path, cm_to_in(0), cm_to_in(0), height=cm_to_in(11.72))
    img.left = int(prs.slide_width / 2 + (prs.slide_width / 2 - img.width) / 2)
    img.top = int((prs.slide_height - img.height) / 2)
    title_box = slide.shapes.add_textbox(cm_to_in(2), cm_to_in(5.35), int(prs.slide_width / 3), cm_to_in(3.59))
    paragraph = title_box.text_frame.add_paragraph()
    paragraph.text = title
    paragraph.font.size = Pt(44)
    paragraph.font.name = '微软雅黑'
    paragraph.font.color.rgb = RGBColor(255, 255, 255)


# 模板4:两行文字,一大一小
def model_4(prs, title, content):
    slide = prs.slides.add_slide(prs.slide_layouts[6])
    slide.shapes.add_picture(ppt_bg_path, cm_to_in(0), cm_to_in(0), cm_to_in(25.4), cm_to_in(14.288))
    # 一级标题
    title_box_1 = slide.shapes.add_textbox(cm_to_in(1.27), cm_to_in(2.04), cm_to_in(22.86), cm_to_in(3.18))
    paragraph_1 = title_box_1.text_frame.add_paragraph()
    paragraph_1.text = title
    paragraph_1.vertical_anchor = MSO_VERTICAL_ANCHOR.MIDDLE
    paragraph_1.alignment = PP_PARAGRAPH_ALIGNMENT.CENTER
    paragraph_1.font.size = Pt(44)
    paragraph_1.font.name = '微软雅黑'
    paragraph_1.font.color.rgb = RGBColor(255, 255, 255)
    # 二级标题
    title_box_2 = slide.shapes.add_textbox(cm_to_in(7.46), cm_to_in(6.4), cm_to_in(10.47), cm_to_in(2.39))
    paragraph_2 = title_box_2.text_frame.add_paragraph()
    paragraph_2.text = title
    paragraph_2.vertical_anchor = MSO_VERTICAL_ANCHOR.MIDDLE
    paragraph_2.alignment = PP_PARAGRAPH_ALIGNMENT.CENTER
    paragraph_2.font.size = Pt(32)
    paragraph_2.font.name = '微软雅黑'
    paragraph_2.font.color.rgb = RGBColor(255, 255, 255)


# 模板5:一行文字,多个小标题
def model_5(prs, title, *content):
    slide = prs.slides.add_slide(prs.slide_layouts[6])
    slide.shapes.add_picture(ppt_bg_path, cm_to_in(0), cm_to_in(0), cm_to_in(25.4), cm_to_in(14.288))
    title_box_1 = slide.shapes.add_textbox(cm_to_in(1.27), cm_to_in(2.04), cm_to_in(22.86), cm_to_in(3.18))
    paragraph_1 = title_box_1.text_frame.add_paragraph()
    paragraph_1.text = title
    paragraph_1.vertical_anchor = MSO_VERTICAL_ANCHOR.MIDDLE
    paragraph_1.alignment = PP_PARAGRAPH_ALIGNMENT.CENTER
    paragraph_1.font.size = Pt(44)
    paragraph_1.font.name = '微软雅黑'
    paragraph_1.font.color.rgb = RGBColor(255, 255, 255)
    # 动态构建小标题
    module_width = (prs.slide_width - cm_to_in(1.27) * 2) / len(content)
    for i in range(0, 3):
        title_box = slide.shapes.add_textbox(cm_to_in(1.27) + i * module_width, cm_to_in(6.4), module_width,
                                             cm_to_in(2.39))
        paragraph = title_box.text_frame.add_paragraph()
        paragraph.text = content[i]
        paragraph.vertical_anchor = MSO_VERTICAL_ANCHOR.MIDDLE
        paragraph.alignment = PP_PARAGRAPH_ALIGNMENT.CENTER
        paragraph.font.size = Pt(32)
        paragraph.font.name = '微软雅黑'
        paragraph.font.color.rgb = RGBColor(255, 255, 255)


# 读取配置文件调用模板的方法
def read_rules(prs, filename):
    if os.path.exists(filename):
        with open(filename, 'r+', encoding='utf-8') as f:
            for rule in f:
                word_list = rule.replace('\n', '').split(',')
                if 'png' in rule or 'jpg' in rule:
                    if len(word_list) == 1:
                        model_1(prs, os.path.join(c.res_pictures, word_list[0]))
                    else:
                        model_3(prs, word_list[0], os.path.join(c.res_pictures, word_list[1]))
                else:
                    if len(word_list) == 1:
                        model_2(prs, word_list[0])
                    elif len(word_list) == 2:
                        model_4(prs, word_list[0], word_list[1])
                    elif len(word_list) == 4:
                        model_5(prs, word_list[0], word_list[1], word_list[2], word_list[3])


if __name__ == '__main__':
    t.is_dir_existed(c.outputs_documents_path)
    ppt_existed(ppt_file_name)
    presentation = Presentation(ppt_file_name)
    read_rules(presentation, rules_path)
    presentation.save(ppt_file_name)

来啊,Py交易啊

想加群一起学习Py的可以加下,智障机器人小Pig,验证信息里包含: Python,python,py,Py,加群,交易,屁眼 中的一个关键词即可通过;

验证通过后回复 加群 即可获得加群链接(不要把机器人玩坏了!!!)~~~ 欢迎各种像我一样的Py初学者,Py大神加入,一起愉快地交流学♂习,van♂转py。



今天看啥 - 高品质阅读平台
本文地址:http://www.jintiankansha.me/t/R3Nja7e1W0
Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/12853