再导入PSD后点击嵌入水印一直显示操作失败:type object 'PsDImage' has no attribute 'load这个问题应该怎么修改哇,真的不懂啊
import os
import datetime
import tkinter as tk
from tkinter import filedialog, messagebox
from psd_tools import PSDImage
from xml.etree import ElementTree as ET
class LegacyPSDWatermarkApp:
def __init__(self, root):
self.root = root
root.title("PSD暗标工具 v1.5 (最终修正版)")
root.geometry("620x420")
# 修正后的XMP命名空间配置
self.XMP_NS = {
'xap': 'http://ns.adobe.com/xap/1.0/',
'mark': 'http://192.168.31.165/watermark/1.0/' # ← 修正后的正确域名格式
}
self.create_widgets()
def create_widgets(self):
"""创建界面组件"""
# 文件选择区
file_frame = tk.Frame(self.root, padx=10, pady=10)
file_frame.pack(fill=tk.X)
self.btn_open = tk.Button(file_frame, text="打开PSD", command=self.select_file,
bg="#37474F", fg="white", width=10)
self.btn_open.pack(side=tk.LEFT)
self.file_path = tk.StringVar()
self.entry_path = tk.Entry(file_frame, textvariable=self.file_path, width=50)
self.entry_path.pack(side=tk.LEFT, padx=10)
# 水印操作区
action_frame = tk.Frame(self.root, pady=15)
action_frame.pack()
self.qq_entry = tk.Entry(action_frame, width=20, font=('Arial', 12))
self.qq_entry.pack(side=tk.LEFT, padx=5)
self.btn_add = tk.Button(action_frame, text="添加水印", command=self.add_watermark,
bg="#00695C", fg="white", width=10)
self.btn_add.pack(side=tk.LEFT, padx=5)
self.btn_check = tk.Button(action_frame, text="检测水印", command=self.check_watermark,
bg="#BF360C", fg="white", width=10)
self.btn_check.pack(side=tk.LEFT, padx=5)
# 日志显示区
self.log = tk.Text(self.root, height=12, state=tk.DISABLED, wrap=tk.WORD,
font=('Consolas', 9), bg="#263238", fg="#ECEFF1")
self.log.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# ----------------- 核心功能修正 -----------------
def add_watermark(self):
"""添加水印(兼容旧版API)"""
qq = self.qq_entry.get().strip()
psd_path = self.file_path.get()
if not self._validate(qq, psd_path):
return
try:
self._log(f"🚀 开始处理: {os.path.basename(psd_path)}")
# === 关键修正:使用正确的加载方式 ===
with open(psd_path, 'rb') as f:
psd = PSDImage.load(f) # 旧版正确加载方式
# 生成XMP数据
xmp_data = self._generate_xmp(qq)
self._log("✅ XMP元数据生成成功")
# 更新资源数据
self._update_xmp_resource(psd, xmp_data)
# 保存文件
new_path = self._get_save_path(psd_path)
with open(new_path, 'wb') as f:
psd.save(f) # 旧版正确保存方式
self._log(f"💾 文件保存成功: {os.path.basename(new_path)}")
messagebox.showinfo("成功", "水印添加完成!")
except Exception as e:
self._show_error(f"❌ 操作失败:{str(e)}")
def check_watermark(self):
"""检测水印(兼容旧版API)"""
psd_path = self.file_path.get()
if not os.path.exists(psd_path):
self._show_error("文件不存在")
return
try:
self._log(f"🔍 正在检测: {os.path.basename(psd_path)}")
with open(psd_path, 'rb') as f:
psd = PSDImage.load(f) # 旧版加载方式
qq = self._extract_watermark(psd)
if qq:
self.qq_entry.delete(0, tk.END)
self.qq_entry.insert(0, qq)
self._log(f"🕵️ 检测到水印: {qq}")
messagebox.showinfo("结果", f"找到水印: {qq}")
else:
self._log("⚠️ 未检测到水印")
messagebox.showinfo("结果", "未找到水印")
except Exception as e:
self._show_error(f"❌ 检测失败: {str(e)}")
def _update_xmp_resource(self, psd, xmp_data):
"""更新XMP资源(兼容旧版方法)"""
# 查找XMP资源(资源ID 1033)
xmp_res_list = [res for res in psd.resources if res.resource_id == 1033]
if xmp_res_list:
# 更新现有资源
xmp_res_list[0].data = xmp_data
self._log("🔄 更新现有XMP资源")
else:
# 创建新资源(关键步骤)
from psd_tools.psd import Resource
new_res = Resource()
new_res.resource_id = 1033 # 官方XMP资源ID
new_res.name = "XMP Data"
new_res.data = xmp_data
psd.resources.append(new_res)
self._log("✨ 新建XMP资源")
# ----------------- 辅助方法修正 -----------------
def _generate_xmp(self, qq):
"""生成符合规范的XMP数据"""
root = ET.Element('{http://ns.adobe.com/xap/1.0/}xmpmeta', {
'xmlns:xap': self.XMP_NS['xap'],
'xmlns:mark': self.XMP_NS['mark'] # 使用修正后的域名
})
desc = ET.SubElement(root, '{http://ns.adobe.com/xap/1.0/}Description')
watermark = ET.SubElement(desc, '{http://192.168.31.165/watermark/1.0/}watermark')
ET.SubElement(watermark, 'qq').text = qq
ET.SubElement(watermark, 'timestamp').text = datetime.datetime.now().isoformat()
return ET.tostring(root, encoding='utf-8', xml_declaration=True)
def _extract_watermark(self, psd):
"""从资源块提取水印"""
try:
xmp_res_list = [res for res in psd.resources if res.resource_id == 1033]
if not xmp_res_list:
return None
root = ET.fromstring(xmp_res_list[0].data)
return root.findtext('.//{http://192.168.31.165/watermark/1.0/}qq') # 域名保持一致
except Exception as e:
self._log(f"⚠️ 解析错误: {str(e)}")
return None
# ----------------- 其他方法保持不变 -----------------
def _validate(self, qq, path):
"""输入验证"""
errors = []
if not qq.isdigit() or len(qq) <5:
errors.append("水印必须为5位以上数字")
if not path.lower().endswith('.psd'):
errors.append("请选择PSD格式文件")
if not os.path.exists(path):
errors.append("文件不存在")
if errors:
self._show_error("\n".join(errors))
return False
return True
def _get_save_path(self, original):
"""生成防重复文件名"""
base, ext = os.path.splitext(original)
counter = 1
while True:
new_path = f"{base}_安全版{counter}{ext}"
if not os.path.exists(new_path):
return new_path
counter += 1
def select_file(self):
"""选择文件"""
filename = filedialog.askopenfilename(
filetypes=[("PSD文件", "*.psd"), ("所有文件", "*.*")]
)
if filename:
self.file_path.set(filename)
self._log(f"📂 已选择文件: {os.path.basename(filename)}")
def _log(self, message):
"""日志记录"""
self.log.config(state=tk.NORMAL)
self.log.insert(tk.END, f"[{datetime.datetime.now().strftime('%H:%M:%S')}] {message}\n")
self.log.config(state=tk.DISABLED)
self.log.see(tk.END)
def _show_error(self, msg):
"""显示错误"""
messagebox.showerror("错误", msg)
self._log(f"❌ {msg}")
if __name__ == "__main__":
root = tk.Tk()
app = LegacyPSDWatermarkApp(root)
root.mainloop()