[Python] 纯文本查看 复制代码import tkinter as tk
from tkinter import ttk, filedialog, messagebox, scrolledtext
from pptx import Presentation
from pptx.text.text import _Run
import os
import re
import win32com.client
import pythoncom
import win32gui
import win32con
class PPTTextReplacer:
def __init__(self, root):
self.root = root
self.root.title("PPT文本替换工具")
self.root.geometry("600x500") # 调整窗口大小
# 配置网格权重
self.root.grid_rowconfigure(0, weight=1)
self.root.grid_columnconfigure(0, weight=1)
# 创建主框架
self.main_frame = ttk.Frame(root, padding="5")
self.main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
# 配置主框架的网格权重
self.main_frame.grid_rowconfigure(4, weight=1)
self.main_frame.grid_columnconfigure(1, weight=1)
# 创建控件
self.create_widgets()
def create_widgets(self):
# 目录选择
ttk.Label(self.main_frame, text="PPT文件目录:").grid(row=0, column=0, sticky=tk.W, pady=2)
self.dir_path = tk.StringVar()
dir_entry = ttk.Entry(self.main_frame, textvariable=self.dir_path)
dir_entry.grid(row=0, column=1, sticky=(tk.W, tk.E), pady=2)
ttk.Button(self.main_frame, text="浏览", command=self.browse_directory, width=8).grid(row=0, column=2, padx=5, pady=2)
# 替换规则文件选择
ttk.Label(self.main_frame, text="替换规则文件:").grid(row=1, column=0, sticky=tk.W, pady=2)
self.rule_file = tk.StringVar()
rule_entry = ttk.Entry(self.main_frame, textvariable=self.rule_file)
rule_entry.grid(row=1, column=1, sticky=(tk.W, tk.E), pady=2)
ttk.Button(self.main_frame, text="浏览", command=self.browse_rule_file, width=8).grid(row=1, column=2, padx=5, pady=2)
# 开始替换按钮
ttk.Button(self.main_frame, text="开始替换", command=self.start_replacement, width=15).grid(row=2, column=0, columnspan=3, pady=10)
# 日志显示区域
ttk.Label(self.main_frame, text="替换进度:").grid(row=3, column=0, sticky=tk.W, pady=2)
self.log_text = scrolledtext.ScrolledText(self.main_frame, width=50, height=20)
self.log_text.grid(row=4, column=0, columnspan=3, sticky=(tk.W, tk.E, tk.N, tk.S), pady=2)
def browse_directory(self):
directory = filedialog.askdirectory()
if directory:
self.dir_path.set(directory)
def browse_rule_file(self):
file_path = filedialog.askopenfilename(filetypes=[("Text files", "*.txt")])
if file_path:
self.rule_file.set(file_path)
def log_message(self, message):
self.log_text.insert(tk.END, message + "\n")
self.log_text.see(tk.END)
def load_replacement_rules(self):
rules = {}
try:
with open(self.rule_file.get(), 'r', encoding='utf-8') as f:
for line in f:
if '=' in line:
old_text, new_text = line.strip().split('=', 1)
rules[old_text] = new_text
return rules
except Exception as e:
self.log_message(f"错误:无法读取替换规则文件 - {str(e)}")
return None
def minimize_powerpoint_window(self):
"""最小化PowerPoint窗口"""
try:
# 查找PowerPoint窗口
def callback(hwnd, extra):
if win32gui.IsWindowVisible(hwnd):
title = win32gui.GetWindowText(hwnd)
if "PowerPoint" in title:
# 最小化窗口
win32gui.ShowWindow(hwnd, win32con.SW_MINIMIZE)
win32gui.EnumWindows(callback, None)
except Exception:
pass
def convert_ppt_in_memory(self, ppt_path):
"""在内存中将旧版PPT转换为PPTX格式"""
try:
# 初始化COM
pythoncom.CoInitialize()
# 创建PowerPoint应用程序对象
powerpoint = win32com.client.Dispatch("PowerPoint.Application")
powerpoint.Visible = True
# 最小化PowerPoint窗口
self.minimize_powerpoint_window()
# 打开PPT文件
presentation = powerpoint.Presentations.Open(ppt_path)
# 创建临时文件路径
temp_pptx = os.path.join(os.path.dirname(ppt_path), f"temp_{os.path.basename(ppt_path)}.pptx")
# 保存为PPTX格式
presentation.SaveAs(temp_pptx, 24) # 24 表示PPTX格式
# 加载到python-pptx中
prs = Presentation(temp_pptx)
# 关闭并删除临时文件
presentation.Close()
powerpoint.Quit()
os.remove(temp_pptx)
return prs
except Exception as e:
self.log_message(f"转换文件 {os.path.basename(ppt_path)} 时出错: {str(e)}")
try:
if 'temp_pptx' in locals() and os.path.exists(temp_pptx):
os.remove(temp_pptx)
except:
pass
return None
finally:
# 清理COM
pythoncom.CoUninitialize()
def replace_text_in_paragraph(self, paragraph, rules):
"""替换段落中的文本,保持原有格式"""
for old_text, new_text in rules.items():
if old_text in paragraph.text:
# 获取段落中的所有run
runs = paragraph.runs
text = paragraph.text
# 找到所有匹配的位置
matches = [(m.start(), m.end()) for m in re.finditer(re.escape(old_text), text)]
if matches:
# 从后向前替换,避免索引变化
for start, end in reversed(matches):
# 计算在哪个run中
run_start = 0
for run in runs:
run_end = run_start + len(run.text)
if run_start {new_text}")
break
run_start = run_end
def replace_text_in_shape(self, shape, rules):
"""替换形状中的文本,保持原有格式"""
if hasattr(shape, "text_frame"):
for paragraph in shape.text_frame.paragraphs:
self.replace_text_in_paragraph(paragraph, rules)
if shape.has_table:
for row in shape.table.rows:
for cell in row.cells:
for paragraph in cell.text_frame.paragraphs:
self.replace_text_in_paragraph(paragraph, rules)
def start_replacement(self):
try:
if not self.dir_path.get() or not self.rule_file.get():
messagebox.showerror("错误", "请选择PPT文件目录和替换规则文件")
return
rules = self.load_replacement_rules()
if not rules:
return
ppt_files = [f for f in os.listdir(self.dir_path.get()) if f.lower().endswith(('.ppt', '.pptx'))]
if not ppt_files:
messagebox.showerror("错误", "所选目录中没有PPT文件")
return
for ppt_file in ppt_files:
try:
ppt_path = os.path.join(self.dir_path.get(), ppt_file)
self.log_message(f"\n处理文件: {ppt_file}")
# 获取文件扩展名
file_ext = os.path.splitext(ppt_file)[1].lower()
# 如果是旧版PPT格式,先转换为PPTX
if file_ext == '.ppt':
prs = self.convert_ppt_in_memory(ppt_path) # 修改这里
if not prs:
continue
file_ext = '.pptx'
else:
prs = Presentation(ppt_path)
for slide in prs.slides:
for shape in slide.shapes:
self.replace_text_in_shape(shape, rules)
# 保存修改后的文件,添加日期前缀
import datetime
date_str = datetime.datetime.now().strftime("%Y%m%d")
new_filename = f"modified_{date_str}_{os.path.splitext(ppt_file)[0]}{file_ext}"
new_path = os.path.join(self.dir_path.get(), new_filename)
prs.save(new_path)
self.log_message(f"成功保存修改后的文件: {new_filename}")
except Exception as e:
self.log_message(f"处理文件 {ppt_file} 时出错: {str(e)}")
messagebox.showinfo("完成", "所有文件处理完成!")
except KeyboardInterrupt:
self.log_message("操作被用户中断")
except Exception as e:
self.log_message(f"发生未预期的错误: {str(e)}")
if __name__ == "__main__":
root = tk.Tk()
app = PPTTextReplacer(root)
root.mainloop()