小豬的Python學習之旅 —— 22.安靜!吵到我用TNT了

一句話概括本文

靈感來自于5.15錘子鳥巢發(fā)布會上老羅用閃念膠囊一鍵生成了32張PPT,
原理利用Python-pptx庫,通過編寫模板的方式自動生成批量PPT!


引言

鏘鏘鏘,失蹤人口回歸,距離上一篇文章已過去14天。不是我偷懶不更新,
主要是轉(zhuǎn)崗打雜了,很雜那種,每天要處理一堆和開發(fā)無關(guān)的瑣事,
真的是敲碼五分鐘,打雜兩小時...還是當個單純的開發(fā)仔好啊。

標題沒錯,是第22篇不是21,不用回去翻,沒看漏,第20篇寫的是爬取Gank.io
接口的所有數(shù)據(jù)到MySQL,第21篇已經(jīng)定好利用Flask編寫一個API接口,
代碼是實現(xiàn)了,部署還有些問題,所以還沒些,先占個坑。

本節(jié)的話,是最近兩天在折騰的一個東西,因為組內(nèi)正在整在線早教
相關(guān)的東西,老師呢,要做很多的課件,但是大部分的內(nèi)容都是重復的,
可能就圖片會變下,流程可能會變一點,然后就找到我,讓我想想有沒有辦法
自動生成減少她們的工作量,對,她們想要的就是 自動生成PPT!

還記得5.15錘子鳥巢發(fā)布會嗎?不記得?看到這個圖你應(yīng)該想起什么了~

老羅現(xiàn)場展示了次時代電腦:TNT工作站 ,這里就不吐槽現(xiàn)場演示時的
各種小失誤和理解萬歲了。直播回顧的視頻可以到B站看:

515錘子科技鳥巢發(fā)布會全程回顧

發(fā)布會直播我是有全程看完的,有個地方吸引了我的注意,在視頻里69:08處,
老羅利用閃念膠囊直接就生成了32張PPT,給人一種錯覺:

臥槽,好屌,以后連PPT都不用自己做了!

其實不然,如果是細心的觀眾基本會發(fā)現(xiàn)一個規(guī)律,生成的PPT都是非常簡單
的PPT,一個黑色漸變的大背景,配幾行字,或者再配個圖片,PPT動畫也沒有。
來,來給你個這樣的PPT生成給我看:

這種批量生成簡單PPT的套路,我覺得思路無非這樣:

根據(jù)情形,定義幾套模板,然后約定一個規(guī)則,根據(jù)不同的內(nèi)容調(diào)用
不同的模板,進行內(nèi)容填充。

套路知道了,接下來就是看看Python有沒有支持庫了~
找到兩個pptxwin32com,本節(jié)只用前者,因為后者的文檔是真的
看得人頭皮發(fā)麻,而且網(wǎng)上的例子非常少...


1.python-pptx庫


官方文檔http://python-pptx.readthedocs.io/en/latest/index.html
官方倉庫https://github.com/scanny/python-pptx
安裝庫pip install python-pptx

對了,因為win32com那個庫要調(diào)用微軟的PowerPoint,我把電腦重裝回
Win 10了,索性安裝了最新版的PyCharm,然后發(fā)現(xiàn)創(chuàng)建的工程和以前
創(chuàng)建的工程結(jié)構(gòu)不一樣,多了個這樣的東西:

終端運行也變成了:(venv) F:\Python> 這樣,這個就是虛擬環(huán)境,
簡單點說就是對開發(fā)環(huán)境進行隔離,比如你這個項目基于Python 2.x,另一個項目
基于Python 3.x,或者說著兩個項目里依賴的同一個模塊用著不同的版本,通過
虛擬環(huán)境可以讓這兩個項目互不干擾,將所需的包安裝到獨立的環(huán)境中。

Python中常用的創(chuàng)建和管理虛擬環(huán)境的工具有:virtualenvpyvenv,
Pycharm默認帶有virtualenv,新建的時候就可以看到,具體怎么定制化,
自行百度吧~對了,下載模塊都在Lib/site-packages目錄下!


2.實現(xiàn)流程分析

首先的話,先想想有哪些模板,羅列下:

  • 1.只有一張圖片
  • 2.只有一條文字
  • 3.一條文字和一張圖片
  • 4.兩條文字
  • 5.四條文字

然后布局大概這樣咯:

接著定義一個規(guī)則,數(shù)據(jù)怎么傳,這里采用最簡單的套路,寫個txt文件,
每行代表一個PPT,參數(shù)通過逗號間隔,于是完整的PPT對應(yīng)這樣的txt文件:

通過逗號分割參數(shù),優(yōu)先判斷是否有圖片,有的話走模板1,3,
其他再另外判斷。好的,思路有了,接下來開始一步步實現(xiàn)吧。


3.代碼實現(xiàn)

PS:這里不去介紹怎么用,看不懂自己去翻文檔,我也是自己摸索,
有我寫的示例參考,應(yīng)該覺得很欣慰了。


1.定義一個厘米轉(zhuǎn)英寸的方法

以為PPT里的位置和大小用到的單位都是厘米,需要轉(zhuǎn)換下

# 厘米轉(zhuǎn)英寸
def cm_to_in(cm):
    return Inches(cm / 2.54)

2.編寫模板

先是模板1,傳入Presentation的對象,這個你可以理解成PPT對象,
調(diào)用該對象的.slides.add_slide()方法添加一張幻燈片,pptx庫為我們
提供了八個不一樣的模板,喜歡的可以自己一個個試,這里我們直接用第七
空白幻燈片,下標從0開始,所以是prs.slide_layouts[6],幻燈片
加了之后,調(diào)用Presentation的save(ppt文件名)函數(shù)打開生成的PPT,
然后點擊設(shè)置 -> 幻燈片大小 -> 直接選擇16:9或者設(shè)置幻燈片大小,比如我
的,這里的寬度和高度就是我們幻燈片的大小了,后面填充滿屏的圖片就要
用到這個。

然后調(diào)用add_picture函數(shù)添加一個滿屏圖片:

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))
    
# 調(diào)用:
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()

然后就可以對文本域里進行文字相關(guān)的操作了:

    paragraph.text = title  # 設(shè)置文本
    paragraph.vertical_anchor = MSO_VERTICAL_ANCHOR.MIDDLE  # 設(shè)置垂直方向?qū)R方式
    paragraph.alignment = PP_PARAGRAPH_ALIGNMENT.CENTER # 設(shè)置水平方向?qū)R方式
    paragraph.font.size = Pt(60)    # 設(shè)置文本大小,PT代表磅
    paragraph.font.name = '微軟雅黑'    # 設(shè)置字體
    paragraph.font.color.rgb = RGBColor(255, 255, 255)  # 設(shè)置字體顏色

參數(shù)傳遞下,調(diào)用這個模板2,查看下生成的效果:

接下來如法炮制剩下的三個模板,主要是難點是控件的位置和寬高設(shè)置
有兩種操作:

最簡單的操作:

隨便填個坐標和寬高,運行后打開生成的PPT,自行調(diào)整
位置,然后記錄下何時的位置和寬高,然后改代碼。

復雜點的操作:

自行計算,比如模板5,三個小標題,先減去左右的間隔,
然后三等分,循環(huán)動態(tài)計算小標題的起始位置。


3.配置文件讀取

接下來編寫一個讀取文件內(nèi)容,調(diào)用對應(yīng)方法的函數(shù),代碼如下

# 讀取配置文件調(diào)用模板的方法
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.代碼執(zhí)行

調(diào)用配置文件讀取的函數(shù)

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)

運行結(jié)果


4.有些東西要說說

批量生成是挺爽的,不過呢,不支持直接生成動畫哦?。?!
然后這種簡單的PPT,其實粘貼復制修改圖片的效率可能比起你一個個
模板編寫快一些...不得不說有些雞肋,哦,對哦,這個還支持生成圖表,
怎么整可以自行查閱官方文檔。

另外的win32com的庫,貌似功能更加強大,可能支持動畫吧,但是文檔是
真的難啃,就沒有深入去研究了,有興趣可以自己去折騰折騰。附上用
win32com這個庫時遇到的問題的解決方法:

  • 1.Python3安裝win32com
pip install pypiwin32
  • 2.哪里有win32com的文檔

微軟官網(wǎng)有,不過看到腦殼痛,介紹個工具:oleview,網(wǎng)上一搜一堆
不過這個用的時候會遇到一個問題,這里順帶記錄下筆者win10電腦遇到的
一些情況:

IVIEWERS.DLL缺失:

網(wǎng)上搜下這個dll文件,下載完把文件復制到Windows/system32,
然后管理員模式打開命令提示符,鍵入:regsvr32 iviewers.dll
注冊這個dll運行庫就可以了。不過呢,win10 64位這樣的執(zhí)行是會報錯的:

模塊iviewers.dll可能與您正在運行的Windows版本不兼容,檢查該模塊是否與
regsvr.exe的x86或x64版本兼容

你要做的是把dll文件拷貝到Windows/SysWOW64,然后管理員模式打開命令提示符
cd到這個目錄下,接著執(zhí)行regsvr32 iviewers.dll即可。

如果出現(xiàn):對DllRegisterServer的調(diào)用失敗,錯誤代碼為0x80070005
那是UAC的緣故,你沒有以管理員模式打開命令提示符

如果解決了,應(yīng)該能正常打開,左側(cè)招到PowerPoint那項,雙擊打開,
然后呢,這個工具不支持查找,你可以把內(nèi)容全選然后復制到如Sublime Text
這樣的代碼查看工具上,ctrl + f 查找關(guān)鍵字,然后一步步定位出實現(xiàn)某個
功能需要用到的一些函數(shù)。


小結(jié)

本節(jié)講解了一波利用Python-pptx批量生成N張PPT的套路,不禁再一次感嘆
人生苦短,我用Python,最后祝六一兒童節(jié)快樂~


參考文獻


附:最終代碼(都可以在:https://github.com/coder-pig/ReptileSomething 找到):

image
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')


# 厘米轉(zhuǎn)英寸
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)
    # 動態(tài)構(gòu)建小標題
    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)


# 讀取配置文件調(diào)用模板的方法
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加群,交易,屁眼 中的一個關(guān)鍵詞即可通過;

image

驗證通過后回復 加群 即可獲得加群鏈接(不要把機器人玩壞了?。?!)~~~
歡迎各種像我一樣的Py初學者,Py大神加入,一起愉快地交流學♂習,van♂轉(zhuǎn)py。


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容