Python内存流存图用于PPTX时遇AttributeError问题求助
AttributeError when adding Matplotlib plot to PPTX via PIL Image
这个错误我之前也踩过坑!问题根源很明确:slide.shapes.add_picture() 方法只接受两种类型的输入:
- 本地图片文件的路径字符串
- 类文件对象(也就是有
read()方法的对象,比如io.BytesIO)
而你传入的是PIL的PngImageFile实例,它没有read()方法,自然会抛出AttributeError。
最简单的解决方案:直接用BytesIO流传递
其实你完全不需要经过PIL这一步——matplotlib的savefig()已经把图片写入了BytesIO流,这个流本身就符合add_picture()的要求,直接传进去就行:
import io from pptx import Presentation from pptx.util import Inches import matplotlib.pyplot as plt import pandas as pd # 1. 初始化PPT并添加幻灯片 prs = Presentation('Template.pptx') title_slide_layout = prs.slide_layouts[6] slide = prs.slides.add_slide(title_slide_layout) title = slide.shapes.title title.text = 'FileName' left = top = Inches(1) # 2. 绘制Matplotlib图表 fig, ax = plt.subplots() df.groupby('Name').plot(x='Time', y='Score', ax=ax) plt.tight_layout() # 可选,防止图表元素被截断 # 3. 将图表保存到内存流(跳过PIL) buf = io.BytesIO() fig.savefig(buf, format='png', dpi=120) # dpi可以调整图片清晰度 buf.seek(0) # 必须把流指针移回开头,否则pptx读不到内容 # 4. 直接把流传给add_picture pic = slide.shapes.add_picture(buf, left, top) # 最后别忘保存PPT prs.save('Result.pptx')
如果需要用PIL处理图片(比如调整尺寸)
要是你确实需要对图片做裁剪、缩放等操作,那处理完后得把PIL对象重新写入一个新的BytesIO流,再传给add_picture():
import io from PIL import Image from pptx import Presentation from pptx.util import Inches import matplotlib.pyplot as plt import pandas as pd # 1. 初始化PPT部分和之前一样 prs = Presentation('Template.pptx') title_slide_layout = prs.slide_layouts[6] slide = prs.slides.add_slide(title_slide_layout) title = slide.shapes.title title.text = 'FileName' left = top = Inches(1) # 2. 绘制图表 fig, ax = plt.subplots() df.groupby('Name').plot(x='Time', y='Score', ax=ax) plt.tight_layout() # 3. 保存到流 -> 用PIL处理 -> 写入新流 buf = io.BytesIO() fig.savefig(buf, format='png') buf.seek(0) im = Image.open(buf) # 示例:调整图片尺寸 im = im.resize((900, 600)) # 把处理后的图片写入新的BytesIO processed_buf = io.BytesIO() im.save(processed_buf, format='png') processed_buf.seek(0) # 4. 添加处理后的图片到PPT pic = slide.shapes.add_picture(processed_buf, left, top) prs.save('Processed_Result.pptx')
额外注意点
- 每次操作
BytesIO后一定要调用seek(0),不然读取流的时候会从末尾开始,导致读不到任何内容。 - 记得导入所有需要的库(比如你原来的代码里没导入
matplotlib.pyplot,运行会报错)。 - 可以通过
savefig()的dpi参数调整图片清晰度,数值越高图片越清晰,但PPT文件也会越大。
内容的提问来源于stack exchange,提问作者FunkyMore




