基于Python开发PPTX压缩工具

引言

在日常办公中,PPT文件往往因为图片过大而导致文件体积过大,不便于传输和存储。为了应对这一问题,我们可以使用Python的wxPython图形界面库结合python-pptxPillow,开发一个简单的PPTX压缩工具。本文将详细介绍如何实现这一功能。

全部代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
import wx
import os
from pptx import Presentation
from PIL import Image
import io
 
class CompressorFrame(wx.Frame):
    def __init__(self):
        super().__init__(parent=None, title='PPTX压缩工具')
        self.panel = wx.Panel(self)
        self.create_ui()
         
    def create_ui(self):
        vbox = wx.BoxSizer(wx.VERTICAL)
         
        # 文件选择部分
        hbox1 = wx.BoxSizer(wx.HORIZONTAL)
        self.file_path = wx.TextCtrl(self.panel, size=(300, -1))
        browse_btn = wx.Button(self.panel, label='选择文件')
        browse_btn.Bind(wx.EVT_BUTTON, self.on_browse)
        hbox1.Add(self.file_path, proportion=1, flag=wx.EXPAND|wx.ALL, border=5)
        hbox1.Add(browse_btn, flag=wx.ALL, border=5)
         
        # 压缩按钮
        compress_btn = wx.Button(self.panel, label='开始压缩')
        compress_btn.Bind(wx.EVT_BUTTON, self.on_compress)
         
        # 进度条
        self.progress = wx.Gauge(self.panel, range=100, size=(400, 25))
         
        # 状态文本
        self.status_text = wx.StaticText(self.panel, label="")
         
        vbox.Add(hbox1, flag=wx.EXPAND|wx.ALL, border=5)
        vbox.Add(compress_btn, flag=wx.ALIGN_CENTER|wx.ALL, border=5)
        vbox.Add(self.progress, flag=wx.EXPAND|wx.ALL, border=5)
        vbox.Add(self.status_text, flag=wx.EXPAND|wx.ALL, border=5)
         
        self.panel.SetSizer(vbox)
        self.Fit()
         
    def on_browse(self, event):
        with wx.FileDialog(self, "选择PPTX文件", wildcard="PowerPoint files (*.pptx)|*.pptx",
                         style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST) as fileDialog:
            if fileDialog.ShowModal() == wx.ID_CANCEL:
                return
            path = fileDialog.GetPath()
            path = os.path.normpath(path.strip('"'))
            self.file_path.SetValue(path)
             
    def update_status(self, text):
        wx.CallAfter(self.status_text.SetLabel, text)
             
    def on_compress(self, event):
        if not self.file_path.GetValue():
            wx.MessageBox('请先选择文件', '提示', wx.OK | wx.ICON_INFORMATION)
            return
             
        input_path = self.file_path.GetValue().strip('"')
        input_path = os.path.normpath(input_path)
         
        if not os.path.exists(input_path):
            wx.MessageBox('文件不存在,请检查路径', '错误', wx.OK | wx.ICON_ERROR)
            return
             
        output_path = self._get_output_path(input_path)
         
        try:
            self._compress_pptx(input_path, output_path)
            wx.MessageBox('压缩完成!n保存路径:' + output_path,
                         '成功', wx.OK | wx.ICON_INFORMATION)
        except Exception as e:
            wx.MessageBox(f'压缩过程中出错:{str(e)}',
                         '错误', wx.OK | wx.ICON_ERROR)
        finally:
            self.progress.SetValue(0)
            self.update_status("")
             
    def _get_output_path(self, input_path):
        directory = os.path.dirname(input_path)
        filename = os.path.basename(input_path)
        name, ext = os.path.splitext(filename)
        return os.path.join(directory, f"{name}_compressed{ext}")
         
    def _compress_pptx(self, input_path, output_path):
        try:
            prs = Presentation(input_path)
        except Exception as e:
            raise Exception(f"无法打开PPTX文件: {str(e)}")
             
        total_slides = len(prs.slides)
        processed_images = 0
        skipped_images = 0
         
        for i, slide in enumerate(prs.slides):
            self.update_status(f"正在处理第 {i+1}/{total_slides} 张幻灯片")
             
            shapes_with_images = []
            for shape in slide.shapes:
                if hasattr(shape, "image"):
                    shapes_with_images.append(shape)
             
            for shape in shapes_with_images:
                try:
                    # 获取图片数据
                    image_bytes = shape.image.blob
                     
                    # 使用PIL压缩图片
                    with Image.open(io.BytesIO(image_bytes)) as img:
                        # 转换RGBA为RGB
                        if img.mode == 'RGBA':
                            img = img.convert('RGB')
                             
                        # 压缩图片
                        # 如果图片较大,调整尺寸
                        max_size = 800  # 最大尺寸为1024像素
                        if img.width > max_size or img.height > max_size:
                            ratio = min(max_size/img.width, max_size/img.height)
                            new_size = (int(img.width * ratio), int(img.height * ratio))
                            img = img.resize(new_size, Image.LANCZOS)
                         
                        output_buffer = io.BytesIO()
                        img.save(output_buffer, format='JPEG', quality=10, optimize=True)
                         
                        # 替换原图片
                        shape._element.blip.embed.rId = shape._element.blip.embed.rId
                        new_image = output_buffer.getvalue()
                         
                        # 更新图片数据
                        image_part = shape.image
                        image_part._blob = new_image
                         
                    processed_images += 1
                except Exception as e:
                    print(f"处理图片时出错: {str(e)}")
                    skipped_images += 1
                    continue
                     
            # 更新进度条
            progress = int((i + 1) / total_slides * 100)
            wx.CallAfter(self.progress.SetValue, progress)
             
        self.update_status(f"完成!成功处理 {processed_images} 张图片,跳过 {skipped_images} 张图片")
             
        try:
            prs.save(output_path)
        except Exception as e:
            raise Exception(f"保存文件时出错: {str(e)}")
 
def main():
    app = wx.App()
    frame = CompressorFrame()
    frame.Show()
    app.MainLoop()
 
if __name__ == '__main__':
    main()

环境准备

在开始之前,我们需要安装以下Python库:

  • wxPython:用于创建图形用户界面
  • python-pptx:用于处理PPTX文件
  • Pillow:用于图片压缩

安装命令:

1
pip install wxPython python-pptx Pillow

代码结构

代码主要包括以下几个部分:

  • 图形界面设计
  • 文件选择与压缩功能
  • 图片压缩逻辑

代码实现

导入必要模块

1
2
3
4
5
import wx
import os
from pptx import Presentation
from PIL import Image
import io

创建主窗口

主窗口CompressorFrame继承自wx.Frame,用于展示UI组件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class CompressorFrame(wx.Frame):
    def __init__(self):
        super().__init__(parent=None, title='PPTX压缩工具')
        self.panel = wx.Panel(self)
        self.create_ui()
         
    def create_ui(self):
        vbox = wx.BoxSizer(wx.VERTICAL)
         
        # 文件选择部分
        hbox1 = wx.BoxSizer(wx.HORIZONTAL)
        self.file_path = wx.TextCtrl(self.panel, size=(300, -1))
        browse_btn = wx.Button(self.panel, label='选择文件')
        browse_btn.Bind(wx.EVT_BUTTON, self.on_browse)
        hbox1.Add(self.file_path, proportion=1, flag=wx.EXPAND|wx.ALL, border=5)
        hbox1.Add(browse_btn, flag=wx.ALL, border=5)
         
        # 压缩按钮
        compress_btn = wx.Button(self.panel, label='开始压缩')
        compress_btn.Bind(wx.EVT_BUTTON, self.on_compress)
         
        # 进度条
        self.progress = wx.Gauge(self.panel, range=100, size=(400, 25))
         
        # 状态文本
        self.status_text = wx.StaticText(self.panel, label="")
         
        vbox.Add(hbox1, flag=wx.EXPAND|wx.ALL, border=5)
        vbox.Add(compress_btn, flag=wx.ALIGN_CENTER|wx.ALL, border=5)
        vbox.Add(self.progress, flag=wx.EXPAND|wx.ALL, border=5)
        vbox.Add(self.status_text, flag=wx.EXPAND|wx.ALL, border=5)
         
        self.panel.SetSizer(vbox)
        self.Fit()

文件选择功能

通过文件对话框让用户选择PPTX文件。

1
2
3
4
5
6
7
def on_browse(self, event):
    with wx.FileDialog(self, "选择PPTX文件", wildcard="PowerPoint files (*.pptx)|*.pptx",
                     style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST) as fileDialog:
        if fileDialog.ShowModal() == wx.ID_CANCEL:
            return
        path = fileDialog.GetPath()
        self.file_path.SetValue(os.path.normpath(path.strip('"')))

压缩功能实现

压缩图片逻辑:

  • 使用Pillow库压缩PPT中的图片,将其转换为JPEG格式,并降低质量以减少文件大小。
  • 限制图片的最大尺寸,保持图片的可视质量。

更新进度条与状态:

使用wx.Gauge展示处理进度。

实时更新处理状态。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
def _compress_pptx(self, input_path, output_path):
    prs = Presentation(input_path)
    total_slides = len(prs.slides)
    processed_images = 0
    skipped_images = 0
     
    for i, slide in enumerate(prs.slides):
        self.update_status(f"正在处理第 {i+1}/{total_slides} 张幻灯片")
         
        shapes_with_images = [shape for shape in slide.shapes if hasattr(shape, "image")]
         
        for shape in shapes_with_images:
            try:
                image_bytes = shape.image.blob
                with Image.open(io.BytesIO(image_bytes)) as img:
                    if img.mode == 'RGBA':
                        img = img.convert('RGB')
                    max_size = 800
                    if img.width > max_size or img.height > max_size:
                        ratio = min(max_size/img.width, max_size/img.height)
                        new_size = (int(img.width * ratio), int(img.height * ratio))
                        img = img.resize(new_size, Image.LANCZOS)
                    output_buffer = io.BytesIO()
                    img.save(output_buffer, format='JPEG', quality=10, optimize=True)
                    new_image = output_buffer.getvalue()
                    shape.image._blob = new_image
                processed_images += 1
            except Exception as e:
                print(f"处理图片时出错: {str(e)}")
                skipped_images += 1
        wx.CallAfter(self.progress.SetValue, int((i + 1) / total_slides * 100))
     
    self.update_status(f"完成!成功处理 {processed_images} 张图片,跳过 {skipped_images} 张图片")
    prs.save(output_path)

主函数

启动wxPython应用程序。

1
2
3
4
5
6
7
8
def main():
    app = wx.App()
    frame = CompressorFrame()
    frame.Show()
    app.MainLoop()
 
​​​​​​​if __name__ == '__main__':
    main()

运行结果

到此这篇关于基于Python开发PPTX压缩工具的文章就介绍到这了,更多相关Python PPTX压缩内容请搜索IT俱乐部以前的文章或继续浏览下面的相关文章希望大家以后多多支持IT俱乐部!

本文收集自网络,不代表IT俱乐部立场,转载请注明出处。https://www.2it.club/code/python/14966.html
上一篇
下一篇
联系我们

联系我们

在线咨询: QQ交谈

邮箱: 1120393934@qq.com

工作时间:周一至周五,9:00-17:30,节假日休息

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部