安靜!吵到我用TNT了!Python才是最屌的!

安靜!吵到我用TNT了!Python才是最屌的!

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

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

還記得5.15錘子鳥巢發佈會嗎?不記得?看到這個圖你應該想起什麼了~

安靜!吵到我用TNT了!Python才是最屌的!

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

安靜!吵到我用TNT了!Python才是最屌的!

安靜!吵到我用TNT了!Python才是最屌的!

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

根據情形,定義幾套模板,然後約定一個規則,根據不同的內容調用 不同的模板,進行內容填充。

安靜!吵到我用TNT了!Python才是最屌的!

套路知道了,接下來就是看看Python有沒有支持庫了~ 找到兩個 pptx

win32com ,本節只用前者,因為後者的文檔是真的 看得人頭皮發麻,而且網上的例子非常少...

安靜!吵到我用TNT了!Python才是最屌的!

安靜!吵到我用TNT了!Python才是最屌的!

2.實現流程分析

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

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

然後佈局大概這樣咯:

安靜!吵到我用TNT了!Python才是最屌的!

接著定義一個規則,數據怎麼傳,這裡採用最簡單的套路,寫個txt文件, 每行代表一個PPT,參數通過逗號間隔,於是完整的PPT對應這樣的txt文件:

安靜!吵到我用TNT了!Python才是最屌的!

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

3.代碼實現

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

安靜!吵到我用TNT了!Python才是最屌的!

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或者設置幻燈片大小,比如我 的,這裡的寬度和高度就是我們幻燈片的大小了,後面填充滿屏的圖片就要 用到這個。

安靜!吵到我用TNT了!Python才是最屌的!

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:

安靜!吵到我用TNT了!Python才是最屌的!

喲,成功生成,接著到模板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,查看下生成的效果:

安靜!吵到我用TNT了!Python才是最屌的!

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

最簡單的操作:

隨便填個座標和寬高,運行後打開生成的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)

運行結果:

安靜!吵到我用TNT了!Python才是最屌的!

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 查找關鍵字,然後一步步定位出實現某個 功能需要用到的一些函數。

安靜!吵到我用TNT了!Python才是最屌的!

小結

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

安靜!吵到我用TNT了!Python才是最屌的!

參考文獻:

  • python-pptx庫中文文檔及使用樣例
  • python使用win32com的心得

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

安靜!吵到我用TNT了!Python才是最屌的!

import pptximport config as cimport tools as tfrom pptx import Presentationfrom pptx.dml.color import RGBColorfrom pptx.util import Inches, Ptfrom pptx.enum.text import MSO_VERTICAL_ANCHOR, PP_PARAGRAPH_ALIGNMENTimport osrules_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) 


分享到:


相關文章: