乐于分享
好东西不私藏

Python圆形印章生成工具 使用文档

Python圆形印章生成工具 使用文档

圆形印章生成工具 使用文档

学在坚持公众号 出品

工具简介

圆形印章生成工具提供两种使用方式:

  1. Python 桌面版圆形印章生成工具.py):基于 Tkinter + Pillow,支持 4 倍超采样高清渲染
  2. H5 网页版圆形印章生成工具.html):纯前端 Canvas 实现,双击即可在浏览器中使用,无需安装任何依赖

两个版本功能一致:可视化制作中文/中英文圆形公章,支持全参数自定义、各元素可拖动调整位置、实时预览、一键下载透明底印章图片。


版本对比

特性
Python 桌面版
H5 网页版
运行方式
python 圆形印章生成工具.py
双击 HTML 文件
依赖
Python 3.7+ / Pillow
无(现代浏览器即可)
渲染质量
4倍超采样抗锯齿
Canvas 原生渲染
字体支持
系统字体(宋体/黑体/楷体等)
浏览器字体
元素拖动
✅ 支持
✅ 支持
实时预览
✅ 200ms 防抖
✅ 100ms 防抖
参数无上限
下载格式
PNG 透明底
PNG 透明底
老化效果

快速开始

Python 桌面版

pip install Pillowpython 圆形印章生成工具.py

H5 网页版

直接双击 圆形印章生成工具.html 文件,在浏览器中打开即可使用。


界面布局

两个版本均采用左右分栏布局:

  • 左侧(40%):参数配置面板,带滚动条
  • 右侧(60%):印章实时预览区,支持鼠标拖动各元素

拖动功能说明

右侧预览区的印章各元素可以直接用鼠标拖动调整位置:

区域
对应元素
拖动效果
上半外圈
公司名称
上下拖动调整字边距(文字离边框远近)
中心区域
五角星/中心内容
上下左右自由拖动
中下区域
章名
上下左右自由拖动
下半外圈
防伪码
上下拖动调整字边距

拖动时左侧对应参数值会实时同步更新。点击”重置拖动位置”可恢复默认。


参数配置详解

1. 公司名称(外圈弧形文字)

沿印章外圈弧形排列的主体文字。

参数
默认值
说明
内容
学在坚持圆形印章科技有限公司
外圈显示的文字
字号
20
文字大小(无上限)
字间距
0
字符之间的额外间距
字边距
0
文字与边框的距离(可拖动调整)

2. 章名(下方居中文字)

参数
默认值
说明
内容
专用章
章名文字
字号
16
文字大小
上下距离
-16
垂直偏移(可拖动调整)
左右距离
0
水平偏移(可拖动调整)

3. 中心内容(印章中心图案)

参数
默认值
说明
内容
中心字符/图案
字号
50
图案大小
上下距离
0
垂直偏移(可拖动调整)
左右距离
0
水平偏移(可拖动调整)

4. 防伪码(底部弧形文字)

参数
默认值
说明
内容
1234567890123
防伪编码
字号
10
文字大小
字间距
0
字符间距
矫正
0
垂直位置微调
字边距
0
与边框距离(可拖动调整)

5. 边框设置

参数
默认值
说明
外边线粗细
2
外圈边框线宽
内边线粗细
1
内圈边框线宽
内边线显示
开启
是否显示内圈边线

6. 外观与尺寸

参数
默认值
说明
印章颜色
红色 (#FF0000)
支持任意颜色
印章尺寸
240px
输出图片尺寸(无上限)
老化效果
关闭
模拟印章磨损做旧

操作流程

1. 打开工具(Python版运行py / H5版双击html)   ↓2. 左侧面板调整参数(或右侧直接拖动元素)   ↓3. 实时预览自动更新   ↓4. 满意后点击"下载印章"保存 PNG 文件

输出格式

属性
说明
格式
PNG
背景
透明
色彩模式
RGBA
默认尺寸
240×240 像素(可自定义)

功能按钮

按钮
说明
生成中文印章
按当前配置生成纯中文圆形印章
生成中英文印章
支持制作包含中英文内容的印章
下载印章
导出当前预览的透明底印章图片
重置拖动位置
将所有拖动偏移恢复为默认值

项目文件

圆形印章生成工具.py     # Python 桌面版(Tkinter GUI)圆形印章生成工具.html   # H5 网页版(纯前端 Canvas)圆形印章生成工具.md     # 本文档

Python 版代码架构

类名
职责
SealGenerator
印章图片生成核心引擎(Pillow 渲染)
DraggablePreview
右侧预览区拖动交互逻辑
SealApp
GUI 界面与参数管理

H5 版技术栈

  • 纯 HTML + CSS + JavaScript
  • Canvas 2D API 绘制印章
  • 鼠标事件实现拖动
  • toDataURL() 导出 PNG

常见问题

Q: Python 版字体显示不正确?

A: 确保 Windows 系统已安装宋体、黑体、楷体等字体(系统自带)。

Q: H5 版字体和 Python 版不一样?

A: H5 版使用浏览器字体渲染,效果可能略有差异,但功能一致。

Q: 文字重叠怎么办?

A: 调整字间距、字边距或字号,或直接拖动元素调整位置。

Q: 如何去掉防伪码?

A: 将防伪码的”内容”清空即可。

Q: 参数有上限吗?

A: 没有最大上限,可以自由输入任意数值。


注意事项

  1. 本工具仅供学习和演示用途,请勿用于制作伪造公章等违法行为
  2. 生成的印章为模拟效果,不具有法律效力
  3. Python 版建议在 Windows 系统下使用以获得最佳字体支持
  4. H5 版建议使用 Chrome / Edge / Firefox 等现代浏览器

版本信息

  • 版本:2.0.0
  • 作者:学在坚持公众号
  • Python 版:Tkinter + Pillow
  • H5 版:HTML5 Canvas
  • 许可证:MIT License
"""圆形印章生成工具 - 学在坚持公众号功能:可视化制作中文/中英文圆形公章,支持全参数自定义,一键生成透明底印章图片并下载"""import tkinter as tkfrom tkinter import ttk, colorchooser, filedialog, messageboxfrom PIL import Image, ImageDraw, ImageFontimport mathimport osimport randomclass SealGenerator:"""圆形印章生成器核心类"""def __init__(self):    self.size = 240    self.color = (25500)    self.image = Nonedef create_seal(self, params):    """根据参数生成印章图片"""    self.size = params['seal_size']    self.color = params['seal_color']    scale = 4    img_size = self.size * scale    img = Image.new('RGBA', (img_size, img_size), (2552552550))    draw = ImageDraw.Draw(img)    center = img_size // 2    radius = (img_size - 20 * scale) // 2    border_width = params['outer_border'] * scale    draw.ellipse(        [center - radius, center - radius, center + radius, center + radius],        outline=self.color + (255,), width=border_width    )    if params['inner_border_show']:        inner_border_width = params['inner_border'] * scale        inner_radius = radius - (border_width + 2 * scale)        draw.ellipse(            [center - inner_radius, center - inner_radius,             center + inner_radius, center + inner_radius],            outline=self.color + (255,), width=inner_border_width        )    self._draw_arc_text_top(img, draw, center, radius, params, scale)    self._draw_center_content(img, draw, center, params, scale)    self._draw_bottom_text(img, draw, center, params, scale)    self._draw_arc_text_bottom(img, draw, center, radius, params, scale)    if params.get('aging_effect'False):        img = self._apply_aging(img, scale)    img = img.resize((self.size, self.size), Image.LANCZOS)    self.image = img    return imgdef _get_font(self, font_name, size, bold=False):    """获取字体"""    font_map = {        '宋体''simsun.ttc',        '黑体''simhei.ttf',        '楷体''simkai.ttf',        '仿宋''simfang.ttf',        '微软雅黑''msyh.ttc',        'Arial''arial.ttf',    }    font_file = font_map.get(font_name, 'simsun.ttc')    paths = [        f"C:/Windows/Fonts/{font_file}",        f"/usr/share/fonts/truetype/{font_file}",        font_file    ]    for path in paths:        if os.path.exists(path):            try:                return ImageFont.truetype(path, size)            except:                pass    try:        return ImageFont.truetype("C:/Windows/Fonts/simsun.ttc", size)    except:        return ImageFont.load_default()def _draw_arc_text_top(self, img, draw, center, radius, params, scale):    """绘制顶部弧形文字"""    text = params['company_name']    if not text:        return    font_size = params['company_font_size'] * scale    font = self._get_font(params['company_font'], font_size, params['company_bold'])    spacing = params['company_spacing'] * scale    margin = params['company_margin'] * scale    text_radius = radius - margin - font_size // 2 - 10 * scale    char_angles = []    total_angle = 0    for char in text:        bbox = font.getbbox(char)        char_width = bbox[2] - bbox[0]        angle = (char_width + spacing) / text_radius        char_angles.append(angle)        total_angle += angle    start_angle = -math.pi / 2 - total_angle / 2    current_angle = start_angle    for i, char in enumerate(text):        angle = current_angle + char_angles[i] / 2        x = center + text_radius * math.cos(angle)        y = center + text_radius * math.sin(angle)        char_img = Image.new('RGBA', (font_size * 2, font_size * 2), (0000))        char_draw = ImageDraw.Draw(char_img)        bbox = font.getbbox(char)        cw = bbox[2] - bbox[0]        ch = bbox[3] - bbox[1]        char_draw.text(            (font_size - cw // 2, font_size - ch // 2),            char, font=font, fill=self.color + (255,)        )        rotation = -math.degrees(angle + math.pi / 2)        char_img = char_img.rotate(rotation, resample=Image.BICUBIC, expand=False)        img.paste(char_img, (int(x - font_size), int(y - font_size)), char_img)        current_angle += char_angles[i]def _draw_center_content(self, img, draw, center, params, scale):    """绘制中心内容"""    text = params['center_content']    if not text:        return    font_size = params['center_font_size'] * scale    font = self._get_font(params['center_font'], font_size, params['center_bold'])    offset_y = params['center_offset_y'] * scale    offset_x = params['center_offset_x'] * scale    bbox = font.getbbox(text)    tw = bbox[2] - bbox[0]    th = bbox[3] - bbox[1]    x = center - tw // 2 + offset_x    y = center - th // 2 + offset_y    draw.text((x, y), text, font=font, fill=self.color + (255,))def _draw_bottom_text(self, img, draw, center, params, scale):    """绘制下方章名"""    text = params['seal_name']    if not text:        return    font_size = params['name_font_size'] * scale    font = self._get_font(params['name_font'], font_size, params['name_bold'])    offset_y = params['name_offset_y'] * scale    offset_x = params['name_offset_x'] * scale    bbox = font.getbbox(text)    tw = bbox[2] - bbox[0]    x = center - tw // 2 + offset_x    y = center + font_size + offset_y    draw.text((x, y), text, font=font, fill=self.color + (255,))def _draw_arc_text_bottom(self, img, draw, center, radius, params, scale):    """绘制底部弧形防伪码"""    text = params['code_text']    if not text:        return    font_size = params['code_font_size'] * scale    font = self._get_font(params['code_font'], font_size, params['code_bold'])    spacing = params['code_spacing'] * scale    margin = params['code_margin'] * scale    correction = params['code_correction'] * scale    text_radius = radius - margin - font_size // 2 - 10 * scale    char_angles = []    total_angle = 0    for char in text:        bbox = font.getbbox(char)        char_width = bbox[2] - bbox[0]        angle = (char_width + spacing) / text_radius        char_angles.append(angle)        total_angle += angle    start_angle = math.pi / 2 - total_angle / 2    current_angle = start_angle    for i, char in enumerate(text):        angle = current_angle + char_angles[i] / 2        x = center + text_radius * math.cos(angle)        y = center + text_radius * math.sin(angle) + correction        char_img = Image.new('RGBA', (font_size * 2, font_size * 2), (0000))        char_draw = ImageDraw.Draw(char_img)        bbox = font.getbbox(char)        cw = bbox[2] - bbox[0]        ch = bbox[3] - bbox[1]        char_draw.text(            (font_size - cw // 2, font_size - ch // 2),            char, font=font, fill=self.color + (255,)        )        rotation = -math.degrees(angle - math.pi / 2)        char_img = char_img.rotate(rotation, resample=Image.BICUBIC, expand=False)        img.paste(char_img, (int(x - font_size), int(y - font_size)), char_img)        current_angle += char_angles[i]def _apply_aging(self, img, scale):    """应用老化效果"""    pixels = img.load()    width, height = img.size    for _ in range(int(width * height * 0.03)):        x = random.randint(0, width - 1)        y = random.randint(0, height - 1)        if pixels[x, y][3] > 0:            pixels[x, y] = (0000)    for _ in range(int(width * height * 0.005)):        x = random.randint(0, width - 1)        y = random.randint(0, height - 1)        if pixels[x, y][3] == 0:            r, g, b = self.color            pixels[x, y] = (r, g, b, random.randint(30100))    return imgdef save_image(self, filepath):    """保存印章图片"""    if self.image:        self.image.save(filepath, 'PNG')        return True    return Falseclass DraggablePreview:"""可拖动预览画布 - 支持各元素独立拖动"""def __init__(self, canvas, app):    self.canvas = canvas    self.app = app    self.canvas_size = 420    self.preview_size = 380    self._dragging = None    self._drag_start = None    self.canvas.bind('<ButtonPress-1>'self._on_press)    self.canvas.bind('<B1-Motion>'self._on_drag)    self.canvas.bind('<ButtonRelease-1>'self._on_release)    self.canvas.bind('<Motion>'self._on_hover)def _get_element_at(self, x, y):    """判断鼠标位置对应哪个可拖动元素"""    cx = self.canvas_size // 2    cy = self.canvas_size // 2    rx = x - cx    ry = y - cy    radius = self.preview_size // 2    dist = math.sqrt(rx * rx + ry * ry)    if dist > radius:        return None    if dist < radius * 0.25:        return 'center'    if radius * 0.25 <= dist < radius * 0.55 and ry > 0:        return 'name'    if dist >= radius * 0.55 and ry < 0:        return 'company'    if dist >= radius * 0.55 and ry > 0:        return 'code'    return 'center'def _on_hover(self, event):    element = self._get_element_at(event.x, event.y)    self.canvas.config(cursor='fleur' if element else '')def _on_press(self, event):    self._dragging = self._get_element_at(event.x, event.y)    if self._dragging:        self._drag_start = (event.x, event.y)def _on_drag(self, event):    if not self._dragging or not self._drag_start:        return    dx = event.x - self._drag_start[0]    dy = event.y - self._drag_start[1]    self._drag_start = (event.x, event.y)    seal_size = self.app.seal_size.get()    scale = seal_size / self.preview_size    if self._dragging == 'center':        self.app.center_offset_x.set(self.app.center_offset_x.get() + int(dx * scale))        self.app.center_offset_y.set(self.app.center_offset_y.get() + int(dy * scale))    elif self._dragging == 'name':        self.app.name_offset_x.set(self.app.name_offset_x.get() + int(dx * scale))        self.app.name_offset_y.set(self.app.name_offset_y.get() + int(dy * scale))    elif self._dragging == 'company':        self.app.company_margin.set(self.app.company_margin.get() + int(dy * scale))    elif self._dragging == 'code':        self.app.code_margin.set(self.app.code_margin.get() - int(dy * scale))def _on_release(self, event):    self._dragging = None    self._drag_start = Noneclass SealApp:"""印章生成工具GUI界面 - 学在坚持公众号"""def __init__(self):    self.root = tk.Tk()    self.root.title("圆形印章生成工具 - 学在坚持公众号")    self.root.geometry("1100x750")    self.root.resizable(TrueTrue)    self.generator = SealGenerator()    self.seal_color = (25500)    self.preview_image = None    self._create_ui()    self._generate_preview()def _create_ui(self):    """创建界面 - 左右grid对半布局,右侧更大"""    main_frame = ttk.Frame(self.root, padding=10)    main_frame.pack(fill=tk.BOTH, expand=True)    # 左侧40%,右侧60%    main_frame.columnconfigure(0, weight=2)    main_frame.columnconfigure(1, weight=3)    main_frame.rowconfigure(0, weight=1)    # 左侧参数面板    left_frame = ttk.LabelFrame(main_frame, text="参数配置", padding=5)    left_frame.grid(row=0, column=0, sticky='nsew', padx=(05))    canvas = tk.Canvas(left_frame)    scrollbar = ttk.Scrollbar(left_frame, orient="vertical", command=canvas.yview)    self.params_frame = ttk.Frame(canvas)    self.params_frame.bind(        "<Configure>",        lambda e: canvas.configure(scrollregion=canvas.bbox("all"))    )    canvas.create_window((00), window=self.params_frame, anchor="nw")    canvas.configure(yscrollcommand=scrollbar.set)    canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)    scrollbar.pack(side=tk.RIGHT, fill=tk.Y)    def _on_mousewheel(event):        canvas.yview_scroll(int(-1 * (event.delta / 120)), "units")    canvas.bind_all("<MouseWheel>", _on_mousewheel)    # 右侧预览面板(更大,可拖动)    right_frame = ttk.LabelFrame(main_frame, text="印章预览 - 学在坚持公众号(可拖动各元素调整位置)", padding=10)    right_frame.grid(row=0, column=1, sticky='nsew', padx=(50))    self.preview_canvas = tk.Canvas(right_frame, width=420, height=420, bg='white',                                     highlightthickness=1, highlightbackground='#ccc')    self.preview_canvas.pack(pady=10, anchor='center')    # 拖动提示    ttk.Label(right_frame,              text="拖动:上半外圈=公司名称 | 中心=五角星 | 中下=章名 | 下半外圈=防伪码",              foreground='#666').pack(pady=5)    # 按钮区域    btn_frame = ttk.Frame(right_frame)    btn_frame.pack(fill=tk.X, pady=10, padx=40)    ttk.Button(btn_frame, text="生成中文印章", command=self._generate_chinese).pack(fill=tk.X, pady=4)    ttk.Button(btn_frame, text="生成中英文印章", command=self._generate_bilingual).pack(fill=tk.X, pady=4)    ttk.Button(btn_frame, text="下载印章", command=self._download_seal).pack(fill=tk.X, pady=4)    ttk.Button(btn_frame, text="重置拖动位置", command=self._reset_drag).pack(fill=tk.X, pady=4)    self._create_params()    # 初始化拖动功能    self.draggable = DraggablePreview(self.preview_canvas, self)def _reset_drag(self):    """重置拖动位置"""    self.center_offset_x.set(0)    self.center_offset_y.set(0)    self.name_offset_x.set(0)    self.name_offset_y.set(-16)    self.company_margin.set(0)    self.code_margin.set(0)def _create_params(self):    """创建参数配置区域"""    row = 0    row = self._create_section_header("1. 公司名称(外圈弧形文字)", row)    self.company_name = tk.StringVar(value="学在坚持圆形印章科技有限公司")    row = self._create_entry("内容:"self.company_name, row)    self.company_font = tk.StringVar(value="宋体")    row = self._create_combo("字体:"self.company_font, ['宋体''黑体''楷体''仿宋''微软雅黑'], row)    self.company_font_size = tk.IntVar(value=20)    row = self._create_spinbox("字号:"self.company_font_size, 899999, row)    self.company_bold = tk.BooleanVar(value=True)    row = self._create_check("加粗:"self.company_bold, row)    self.company_spacing = tk.IntVar(value=0)    row = self._create_spinbox("字间距:"self.company_spacing, -10099999, row)    self.company_margin = tk.IntVar(value=0)    row = self._create_spinbox("字边距:"self.company_margin, -10099999, row)    row = self._create_section_header("2. 章名(下方居中文字)", row)    self.seal_name = tk.StringVar(value="专用章")    row = self._create_entry("内容:"self.seal_name, row)    self.name_font = tk.StringVar(value="宋体")    row = self._create_combo("字体:"self.name_font, ['宋体''黑体''楷体''仿宋''微软雅黑'], row)    self.name_font_size = tk.IntVar(value=16)    row = self._create_spinbox("字号:"self.name_font_size, 899999, row)    self.name_bold = tk.BooleanVar(value=True)    row = self._create_check("加粗:"self.name_bold, row)    self.name_offset_y = tk.IntVar(value=-16)    row = self._create_spinbox("上下距离:"self.name_offset_y, -9999999999, row)    self.name_offset_x = tk.IntVar(value=0)    row = self._create_spinbox("左右距离:"self.name_offset_x, -9999999999, row)    row = self._create_section_header("3. 中心内容(印章中心图案)", row)    self.center_content = tk.StringVar(value="★")    row = self._create_entry("内容:"self.center_content, row)    self.center_font = tk.StringVar(value="宋体")    row = self._create_combo("字体:"self.center_font, ['宋体''黑体''楷体''仿宋''微软雅黑'], row)    self.center_font_size = tk.IntVar(value=50)    row = self._create_spinbox("字号:"self.center_font_size, 1099999, row)    self.center_bold = tk.BooleanVar(value=True)    row = self._create_check("加粗:"self.center_bold, row)    self.center_offset_y = tk.IntVar(value=0)    row = self._create_spinbox("上下距离:"self.center_offset_y, -9999999999, row)    self.center_offset_x = tk.IntVar(value=0)    row = self._create_spinbox("左右距离:"self.center_offset_x, -9999999999, row)    row = self._create_section_header("4. 防伪码(底部弧形文字)", row)    self.code_text = tk.StringVar(value="1234567890123")    row = self._create_entry("内容:"self.code_text, row)    self.code_font = tk.StringVar(value="楷体")    row = self._create_combo("字体:"self.code_font, ['宋体''黑体''楷体''仿宋''微软雅黑''Arial'], row)    self.code_font_size = tk.IntVar(value=10)    row = self._create_spinbox("字号:"self.code_font_size, 699999, row)    self.code_bold = tk.BooleanVar(value=True)    row = self._create_check("加粗:"self.code_bold, row)    self.code_spacing = tk.IntVar(value=0)    row = self._create_spinbox("字间距:"self.code_spacing, -10099999, row)    self.code_correction = tk.IntVar(value=0)    row = self._create_spinbox("矫正:"self.code_correction, -9999999999, row)    self.code_margin = tk.IntVar(value=0)    row = self._create_spinbox("字边距:"self.code_margin, -10099999, row)    row = self._create_section_header("5. 边框设置", row)    self.outer_border = tk.IntVar(value=2)    row = self._create_spinbox("外边线粗细:"self.outer_border, 199999, row)    self.inner_border = tk.IntVar(value=1)    row = self._create_spinbox("内边线粗细:"self.inner_border, 199999, row)    self.inner_border_show = tk.BooleanVar(value=True)    row = self._create_check("内边线显示:"self.inner_border_show, row)    row = self._create_section_header("6. 外观与尺寸", row)    color_frame = ttk.Frame(self.params_frame)    color_frame.grid(row=row, column=0, columnspan=3, sticky='w', pady=2)    ttk.Label(color_frame, text="印章颜色:").pack(side=tk.LEFT)    self.color_btn = tk.Button(color_frame, text="  ", bg='#FF0000', width=4, command=self._choose_color)    self.color_btn.pack(side=tk.LEFT, padx=5)    self.color_label = ttk.Label(color_frame, text="#FF0000")    self.color_label.pack(side=tk.LEFT)    row += 1    self.seal_size = tk.IntVar(value=240)    row = self._create_spinbox("印章尺寸(px):"self.seal_size, 5099999, row)    self.aging_effect = tk.BooleanVar(value=False)    row = self._create_check("印章老化效果:"self.aging_effect, row)def _create_section_header(self, text, row):    ttk.Separator(self.params_frame, orient='horizontal').grid(row=row, column=0, columnspan=3, sticky='ew', pady=(105))    row += 1    ttk.Label(self.params_frame, text=text, font=(''10'bold')).grid(row=row, column=0, columnspan=3, sticky='w', pady=(05))    row += 1    return rowdef _create_entry(self, label, var, row):    ttk.Label(self.params_frame, text=label).grid(row=row, column=0, sticky='w', pady=2)    ttk.Entry(self.params_frame, textvariable=var, width=30).grid(row=row, column=1, columnspan=2, sticky='w', pady=2)    var.trace_add('write'lambda *args: self._on_param_change())    return row + 1def _create_combo(self, label, var, values, row):    ttk.Label(self.params_frame, text=label).grid(row=row, column=0, sticky='w', pady=2)    ttk.Combobox(self.params_frame, textvariable=var, values=values, width=12, state='readonly').grid(row=row, column=1, sticky='w', pady=2)    var.trace_add('write'lambda *args: self._on_param_change())    return row + 1def _create_spinbox(self, label, var, from_, to, row):    ttk.Label(self.params_frame, text=label).grid(row=row, column=0, sticky='w', pady=2)    ttk.Spinbox(self.params_frame, textvariable=var, from_=from_, to=to, width=8).grid(row=row, column=1, sticky='w', pady=2)    var.trace_add('write'lambda *args: self._on_param_change())    return row + 1def _create_check(self, label, var, row):    ttk.Label(self.params_frame, text=label).grid(row=row, column=0, sticky='w', pady=2)    ttk.Checkbutton(self.params_frame, variable=var).grid(row=row, column=1, sticky='w', pady=2)    var.trace_add('write'lambda *args: self._on_param_change())    return row + 1def _choose_color(self):    color = colorchooser.askcolor(initialcolor='#%02x%02x%02x' % self.seal_color, title="选择印章颜色")    if color[0]:        self.seal_color = tuple(int(c) for c in color[0])        self.color_btn.configure(bg=color[1])        self.color_label.configure(text=color[1].upper())        self._on_param_change()def _on_param_change(self, *args):    if hasattr(self'_render_job'):        self.root.after_cancel(self._render_job)    self._render_job = self.root.after(200self._do_render)def _do_render(self):    try:        params = self._get_params()        img = self.generator.create_seal(params)        self._update_preview(img)    except:        passdef _get_params(self):    return {        'company_name'self.company_name.get(),        'company_font'self.company_font.get(),        'company_font_size'self.company_font_size.get(),        'company_bold'self.company_bold.get(),        'company_spacing'self.company_spacing.get(),        'company_margin'self.company_margin.get(),        'seal_name'self.seal_name.get(),        'name_font'self.name_font.get(),        'name_font_size'self.name_font_size.get(),        'name_bold'self.name_bold.get(),        'name_offset_y'self.name_offset_y.get(),        'name_offset_x'self.name_offset_x.get(),        'center_content'self.center_content.get(),        'center_font'self.center_font.get(),        'center_font_size'self.center_font_size.get(),        'center_bold'self.center_bold.get(),        'center_offset_y'self.center_offset_y.get(),        'center_offset_x'self.center_offset_x.get(),        'code_text'self.code_text.get(),        'code_font'self.code_font.get(),        'code_font_size'self.code_font_size.get(),        'code_bold'self.code_bold.get(),        'code_spacing'self.code_spacing.get(),        'code_correction'self.code_correction.get(),        'code_margin'self.code_margin.get(),        'outer_border'self.outer_border.get(),        'inner_border'self.inner_border.get(),        'inner_border_show'self.inner_border_show.get(),        'seal_color'self.seal_color,        'seal_size'self.seal_size.get(),        'aging_effect'self.aging_effect.get(),    }def _generate_preview(self):    try:        params = self._get_params()        img = self.generator.create_seal(params)        self._update_preview(img)    except Exception as e:        messagebox.showerror("错误"f"生成印章失败:{str(e)}")def _generate_chinese(self):    self._generate_preview()    messagebox.showinfo("成功""中文印章已生成,可点击[下载印章]保存")def _generate_bilingual(self):    self._generate_preview()    messagebox.showinfo("成功""中英文印章已生成,可点击[下载印章]保存")def _update_preview(self, img):    from PIL import ImageTk    preview_size = 380    img_resized = img.resize((preview_size, preview_size), Image.LANCZOS)    bg = Image.new('RGBA', (preview_size, preview_size), (255255255255))    bg.paste(img_resized, (00), img_resized)    self.preview_image = ImageTk.PhotoImage(bg)    self.preview_canvas.delete("all")    self.preview_canvas.create_image(210210, image=self.preview_image)def _download_seal(self):    if not self.generator.image:        self._do_render()    filepath = filedialog.asksaveasfilename(        title="保存印章", defaultextension=".png",        filetypes=[("PNG图片""*.png"), ("所有文件""*.*")],        initialfile="印章.png"    )    if filepath:        if self.generator.save_image(filepath):            messagebox.showinfo("成功"f"印章已保存到:\n{filepath}")        else:            messagebox.showerror("错误""保存失败")def run(self):    self.root.mainloop()if name == 'main':app = SealApp()app.run()