用 tkinter 实现一个对联生成器,核心功能包括:上传图片作为背景、输入汉字内容、点击生成对联(叠加在背景图上)、点击保存最终成品。
实现这个完整功能,用到 tkinter 做界面,PIL(Pillow)处理图片和文字叠加,步骤清晰且代码可直接运行。



首先安装需要的依赖库(处理图片必须用到 Pillow):
pip install pillowimport tkinter as tkfrom tkinter import filedialog, messageboxfrom PIL import Image, ImageTk, ImageDraw, ImageFontimport os# 对联生成器主类classCoupletGenerator:def__init__(self, root):# 初始化主窗口 self.root = root self.root.title("对联生成器(背景图+自定义文字)") self.root.geometry("800x900") # 窗口大小# 定义全局变量存储关键数据 self.bg_image_path = None# 背景图片路径 self.bg_image = None# 原始背景图片对象 self.result_image = None# 生成后的最终图片对象# 1. 创建界面组件 self._create_widgets()def_create_widgets(self):"""创建所有界面控件"""# 第一排:上传背景图片按钮 self.upload_btn = tk.Button( self.root, text="📷 上传背景图片", command=self.upload_background, font=("宋体", 12), width=20, height=2 ) self.upload_btn.pack(pady=10)# 第二排:背景图片预览区域 self.preview_label = tk.Label(self.root, text="背景图片预览(上传后显示)", font=("宋体", 10)) self.preview_label.pack(pady=5)# 限制预览区域大小,避免图片过大撑爆窗口 self.preview_canvas = tk.Canvas(self.root, width=600, height=400, bg="#f0f0f0", bd=1, relief="solid") self.preview_canvas.pack(pady=5)# 第三排:对联文字输入区域 self.frame_input = tk.Frame(self.root) self.frame_input.pack(pady=10)# 上联输入 tk.Label(self.frame_input, text="上联:", font=("宋体", 12)).grid(row=0, column=0, padx=5, pady=5) self.upper_couplet = tk.Entry(self.frame_input, font=("宋体", 14), width=30) self.upper_couplet.grid(row=0, column=1, padx=5, pady=5)# 下联输入 tk.Label(self.frame_input, text="下联:", font=("宋体", 12)).grid(row=1, column=0, padx=5, pady=5) self.lower_couplet = tk.Entry(self.frame_input, font=("宋体", 14), width=30) self.lower_couplet.grid(row=1, column=1, padx=5, pady=5)# 横批输入 tk.Label(self.frame_input, text="横批:", font=("宋体", 12)).grid(row=2, column=0, padx=5, pady=5) self.horizontal_couplet = tk.Entry(self.frame_input, font=("宋体", 14), width=30) self.horizontal_couplet.grid(row=2, column=1, padx=5, pady=5)# 第四排:功能按钮(生成+保存) self.frame_btn = tk.Frame(self.root) self.frame_btn.pack(pady=20) self.generate_btn = tk.Button( self.frame_btn, text="✨ 生成对联", command=self.generate_couplet, font=("宋体", 12), width=15, bg="#4CAF50", fg="white" ) self.generate_btn.grid(row=0, column=0, padx=20) self.save_btn = tk.Button( self.frame_btn, text="💾 保存成品", command=self.save_couplet, font=("宋体", 12), width=15, bg="#2196F3", fg="white" ) self.save_btn.grid(row=0, column=1, padx=20)defupload_background(self):"""上传背景图片并显示预览"""# 打开文件选择对话框,限定图片格式 file_path = filedialog.askopenfilename( title="选择背景图片", filetypes=[("图片文件", "*.jpg *.jpeg *.png *.bmp"), ("所有文件", "*.*")] )ifnot file_path: # 用户取消选择return# 存储背景图片路径 self.bg_image_path = file_pathtry:# 打开图片并缩放(适配预览画布大小,保持宽高比) self.bg_image = Image.open(file_path) self.bg_image.thumbnail((600, 400), Image.Resampling.LANCZOS) # 高质量缩放# 转换为tkinter可显示的格式 tk_image = ImageTk.PhotoImage(self.bg_image)# 更新预览画布 self.preview_canvas.delete("all") # 清空原有内容 self.preview_canvas.create_image(300, 200, image=tk_image) # 居中显示 self.preview_canvas.tk_image = tk_image # 保存引用,防止被垃圾回收# 更新预览标签文字 self.preview_label.config(text=f"已上传:{os.path.basename(file_path)}")except Exception as e: messagebox.showerror("错误", f"图片打开失败:{str(e)}") self.bg_image_path = None self.bg_image = Nonedefgenerate_couplet(self):"""生成对联(将文字叠加到背景图片上)"""# 校验前置条件ifnot self.bg_image_path ornot self.bg_image: messagebox.warning("提示", "请先上传背景图片!")return upper_text = self.upper_couplet.get().strip() lower_text = self.lower_couplet.get().strip() horizontal_text = self.horizontal_couplet.get().strip()ifnot (upper_text or lower_text or horizontal_text): messagebox.warning("提示", "请至少输入一项对联内容!")returntry:# 1. 重新打开原始背景图片(避免预览缩放影响最终成品) original_bg = Image.open(self.bg_image_path).convert("RGB") # 转为RGB格式,避免透明通道问题 draw = ImageDraw.Draw(original_bg) # 创建绘图对象# 2. 设置字体(关键:支持中文显示,这里使用系统自带宋体,可替换为自定义字体文件)# 如果你有自定义中文字体(如simhei.ttf),可以替换为字体文件路径try:# Windows 系统默认宋体路径 font = ImageFont.truetype("simsun.ttc", 48) # 48为字体大小except:try:# Mac 系统默认中文字体路径 font = ImageFont.truetype("STHeitiLight.ttc", 48)except:# 无自定义字体时,使用默认字体(可能不支持中文) font = ImageFont.load_default(size=48) messagebox.warning("提示", "未找到系统中文字体,生成的文字可能无法正常显示!")# 3. 获取背景图片尺寸,用于计算文字位置(居中/对称排列) bg_width, bg_height = original_bg.size# 4. 绘制上联(左侧垂直排列,可根据需求调整位置)if upper_text: upper_x = bg_width * 0.2# 上联x坐标(左侧20%位置) upper_y_step = 60# 每个字的垂直间距 start_upper_y = (bg_height - len(upper_text) * upper_y_step) / 2# 垂直居中for i, char in enumerate(upper_text): draw.text( (upper_x, start_upper_y + i * upper_y_step), char, font=font, fill=(255, 0, 0) # 文字颜色:红色 )# 5. 绘制下联(右侧垂直排列)if lower_text: lower_x = bg_width * 0.8# 下联x坐标(右侧80%位置) lower_y_step = 60# 每个字的垂直间距 start_lower_y = (bg_height - len(lower_text) * lower_y_step) / 2# 垂直居中for i, char in enumerate(lower_text): draw.text( (lower_x, start_lower_y + i * lower_y_step), char, font=font, fill=(255, 0, 0) # 文字颜色:红色 )# 6. 绘制横批(顶部水平居中排列)if horizontal_text:# 计算横批文字宽度,实现水平居中 text_bbox = draw.textbbox((0, 0), horizontal_text, font=font) text_width = text_bbox[2] - text_bbox[0] horizontal_x = (bg_width - text_width) / 2 horizontal_y = bg_height * 0.1# 横批y坐标(顶部10%位置) draw.text( (horizontal_x, horizontal_y), horizontal_text, font=font, fill=(255, 0, 0) # 文字颜色:红色 )# 7. 存储生成后的图片,并更新预览 self.result_image = original_bg# 缩放结果图用于预览 preview_result = self.result_image.copy() preview_result.thumbnail((600, 400), Image.Resampling.LANCZOS) tk_result = ImageTk.PhotoImage(preview_result) self.preview_canvas.delete("all") self.preview_canvas.create_image(300, 200, image=tk_result) self.preview_canvas.tk_image = tk_result messagebox.showinfo("成功", "对联生成完成!可点击保存按钮导出成品。")except Exception as e: messagebox.showerror("错误", f"对联生成失败:{str(e)}") self.result_image = Nonedefsave_couplet(self):"""保存生成后的对联图片"""ifnot self.result_image: messagebox.warning("提示", "请先生成对联!")return# 打开保存文件对话框,默认格式为PNG save_path = filedialog.asksaveasfilename( title="保存对联图片", defaultextension=".png", filetypes=[("PNG图片", "*.png"), ("JPG图片", "*.jpg"), ("所有文件", "*.*")] )ifnot save_path: # 用户取消保存returntry: self.result_image.save(save_path) messagebox.showinfo("成功", f"对联已保存至:\n{save_path}")except Exception as e: messagebox.showerror("错误", f"图片保存失败:{str(e)}")# 程序入口if __name__ == "__main__": root = tk.Tk() app = CoupletGenerator(root) root.mainloop()jpg/jpeg/png/bmp 格式图片,预览时自动缩放到合适大小,不撑爆窗口。PNG/JPG 格式,自定义保存路径。ImageFont.truetype() 加载系统中文字体(Windows 默认为 simsun.ttc,Mac 默认为 STHeitiLight.ttc),解决 tkinter + PIL 中文乱码问题。.ttf/.ttc 格式中文字体,将 ImageFont.truetype() 中的路径替换为你的字体文件路径即可。messagebox 给出清晰提示,避免程序崩溃。tkinter 构建界面,Pillow 处理图片与文字叠加,实现了对联生成器的核心需求,且代码可直接运行。import tkinter as tkfrom tkinter import filedialog, messageboxfrom PIL import Image, ImageTk, ImageDraw, ImageFontimport osimport random# 对联词库UPPER_LINES = ["春风入喜财入户","岁月更新福满门","一帆风顺年年好","万事如意步步高","红梅含苞傲冬雪"]LOWER_LINES = ["岁月更新福满门","春风入喜财入户","万事如意步步高","一帆风顺年年好","绿柳吐絮迎新春"]HORIZONTAL_LINES = ["新春大吉","万事如意","四季平安","五福临门","恭喜发财"]classCoupletGenerator:def__init__(self, root): self.root = root self.root.title("对联生成器(背景图+随机/自定义)") self.root.geometry("800x950") self.bg_image_path = None self.bg_image = None self.result_image = None self._create_widgets()def_create_widgets(self):# 上传背景图 self.upload_btn = tk.Button( self.root, text="📷 上传背景图片", command=self.upload_background, font=("宋体", 12), width=20, height=2 ) self.upload_btn.pack(pady=10)# 预览 self.preview_label = tk.Label(self.root, text="背景图片预览(上传后显示)", font=("宋体", 10)) self.preview_label.pack(pady=5) self.preview_canvas = tk.Canvas(self.root, width=600, height=400, bg="#f0f0f0", bd=1, relief="solid") self.preview_canvas.pack(pady=5)# 输入区 self.frame_input = tk.Frame(self.root) self.frame_input.pack(pady=10) tk.Label(self.frame_input, text="上联:", font=("宋体", 12)).grid(row=0, column=0, padx=5, pady=5) self.upper_couplet = tk.Entry(self.frame_input, font=("宋体", 14), width=30) self.upper_couplet.grid(row=0, column=1, padx=5, pady=5) tk.Label(self.frame_input, text="下联:", font=("宋体", 12)).grid(row=1, column=0, padx=5, pady=5) self.lower_couplet = tk.Entry(self.frame_input, font=("宋体", 14), width=30) self.lower_couplet.grid(row=1, column=1, padx=5, pady=5) tk.Label(self.frame_input, text="横批:", font=("宋体", 12)).grid(row=2, column=0, padx=5, pady=5) self.horizontal_couplet = tk.Entry(self.frame_input, font=("宋体", 14), width=30) self.horizontal_couplet.grid(row=2, column=1, padx=5, pady=5)# 随机对联按钮 self.random_btn = tk.Button( self.frame_input, text="🎲 随机生成对联", command=self.random_couplet, font=("宋体", 12), bg="#FF9800", fg="white" ) self.random_btn.grid(row=3, column=0, columnspan=2, pady=10)# 生成/保存 self.frame_btn = tk.Frame(self.root) self.frame_btn.pack(pady=20) self.generate_btn = tk.Button( self.frame_btn, text="✨ 生成对联", command=self.generate_couplet, font=("宋体", 12), width=15, bg="#4CAF50", fg="white" ) self.generate_btn.grid(row=0, column=0, padx=20) self.save_btn = tk.Button( self.frame_btn, text="💾 保存成品", command=self.save_couplet, font=("宋体", 12), width=15, bg="#2196F3", fg="white" ) self.save_btn.grid(row=0, column=1, padx=20)defupload_background(self): file_path = filedialog.askopenfilename( title="选择背景图片", filetypes=[("图片文件", "*.jpg *.jpeg *.png *.bmp"), ("所有文件", "*.*")] )ifnot file_path:return self.bg_image_path = file_pathtry: self.bg_image = Image.open(file_path) self.bg_image.thumbnail((600, 400), Image.Resampling.LANCZOS) tk_image = ImageTk.PhotoImage(self.bg_image) self.preview_canvas.delete("all") self.preview_canvas.create_image(300, 200, image=tk_image) self.preview_canvas.tk_image = tk_image self.preview_label.config(text=f"已上传:{os.path.basename(file_path)}")except Exception as e: messagebox.showerror("错误", f"图片打开失败:{str(e)}") self.bg_image_path = None self.bg_image = Nonedefrandom_couplet(self):"""随机生成对联并填入输入框""" upper = random.choice(UPPER_LINES) lower = random.choice(LOWER_LINES) horizontal = random.choice(HORIZONTAL_LINES)# 清空输入框并填入随机内容(确保完整覆盖) self.upper_couplet.delete(0, tk.END) self.upper_couplet.insert(0, upper) self.lower_couplet.delete(0, tk.END) self.lower_couplet.insert(0, lower) self.horizontal_couplet.delete(0, tk.END) self.horizontal_couplet.insert(0, horizontal)defgenerate_couplet(self):"""生成对联(核心修复:字体+文字坐标+调试信息)"""ifnot self.bg_image_path ornot self.bg_image: messagebox.showwarning("提示", "请先上传背景图片!")return upper_text = self.upper_couplet.get().strip() lower_text = self.lower_couplet.get().strip() horizontal_text = self.horizontal_couplet.get().strip()ifnot (upper_text or lower_text or horizontal_text): messagebox.showwarning("提示", "请至少输入一项对联内容!")returntry:# 1. 打开原始背景图(保证清晰度) original_bg = Image.open(self.bg_image_path).convert("RGB") draw = ImageDraw.Draw(original_bg) bg_w, bg_h = original_bg.size# 2. 修复字体问题(优先级:自定义字体 > 系统字体 > 默认字体,增加字体大小适配) font = None font_size = min(48, int(bg_w/20), int(bg_h/20)) # 自适应图片尺寸,避免文字过大try:# Windows 系统宋体(兼容大多数环境) font = ImageFont.truetype("simsun.ttc", font_size)except Exception as e1:try:# Mac 系统中文字体 font = ImageFont.truetype("STHeitiLight.ttc", font_size)except Exception as e2:try:# Linux 系统中文字体 font = ImageFont.truetype("wqy-microhei.ttc", font_size)except:# 最终兜底:默认字体(虽不支持中文,但提示用户) font = ImageFont.load_default(size=font_size) messagebox.showwarning("提示", f"未找到中文字体:{e1}/{e2},文字可能无法正常显示!")# 3. 修复文字坐标(避免超出图片范围,增加偏移量适配) text_color = (255, 0, 0) # 红色(醒目,确保可见) step = font_size + 12# 字间距(适配字体大小)# 上联(左竖排:调整x坐标,避免贴边)if upper_text: x = max(50, int(bg_w * 0.1)) # 左侧至少留50像素边距 start_y = (bg_h - len(upper_text) * step) / 2# 确保文字在图片内 start_y = max(50, min(start_y, bg_h - len(upper_text) * step - 50))for i, c in enumerate(upper_text): draw.text((x, start_y + i * step), c, font=font, fill=text_color, stroke_width=1, stroke_fill=(0,0,0))# 下联(右竖排:调整x坐标,避免贴边)if lower_text: x = min(bg_w - 50, int(bg_w * 0.9)) # 右侧至少留50像素边距 start_y = (bg_h - len(lower_text) * step) / 2 start_y = max(50, min(start_y, bg_h - len(lower_text) * step - 50))for i, c in enumerate(lower_text): draw.text((x, start_y + i * step), c, font=font, fill=text_color, stroke_width=1, stroke_fill=(0,0,0))# 横批(上中横排:居中对齐,增加描边更醒目)if horizontal_text:# 计算文字宽度,实现精准居中 bbox = draw.textbbox((0, 0), horizontal_text, font=font) text_w = bbox[2] - bbox[0] x = (bg_w - text_w) / 2 y = max(30, int(bg_h * 0.05)) # 顶部至少留30像素边距 draw.text((x, y), horizontal_text, font=font, fill=text_color, stroke_width=1, stroke_fill=(0,0,0))# 4. 保存结果并更新预览 self.result_image = original_bg# 缩放预览(适配画布) preview = self.result_image.copy() preview.thumbnail((600, 400), Image.Resampling.LANCZOS) tk_img = ImageTk.PhotoImage(preview) self.preview_canvas.delete("all") self.preview_canvas.create_image(300, 200, image=tk_img) self.preview_canvas.tk_image = tk_img # 保存引用,防止被回收 messagebox.showinfo("成功", f"对联生成完成!\n上联:{upper_text}\n下联:{lower_text}\n横批:{horizontal_text}")except Exception as e: messagebox.showerror("错误", f"生成失败:{str(e)}") self.result_image = Nonedefsave_couplet(self):ifnot self.result_image: messagebox.showwarning("提示", "请先生成对联!")return save_path = filedialog.asksaveasfilename( title="保存对联", defaultextension=".png", filetypes=[("PNG图片", "*.png"), ("JPG图片", "*.jpg"), ("所有文件", "*.*")] )ifnot save_path:returntry: self.result_image.save(save_path) messagebox.showinfo("成功", f"已保存至:\n{save_path}")except Exception as e: messagebox.showerror("错误", f"保存失败:{str(e)}")if __name__ == "__main__": root = tk.Tk() app = CoupletGenerator(root) root.mainloop()字体适配优化:
Linux 系统字体(wqy-microhei.ttc)支持,覆盖更多操作系统。stroke_width=1, stroke_fill=(0,0,0)),即使背景颜色较红,文字也能清晰可见。文字坐标安全校验:
50 像素边距,横批顶部留 30 像素边距。y 坐标,确保不会超出图片上下边界。step)适配字体大小,避免文字重叠。输入框内容确保完整:
delete(0, tk.END) 清空输入框,再 insert(0, 内容),确保内容完整传递。如果仍然无法显示文字,大概率是系统缺少对应的中文字体文件,解决方案:
simhei.ttf 黑体、simsun.ttc 宋体)。ImageFont.truetype() 的路径为:font = ImageFont.truetype("simhei.ttf", font_size)(替换为你的字体文件名)。PIL 能加载并绘制中文。如果你希望在原有对联生成器基础上,增加字体大小选择功能,可以自定义调整对联文字的大小,适配不同尺寸的背景图。
在修复版代码的基础上,添加字体大小下拉选择框(可选16-128号字体,步长8),并让文字间距、边距随字体大小自动适配,保证排版美观,以下是完整可运行的最终版代码。
import tkinter as tkfrom tkinter import filedialog, messageboxfrom PIL import Image, ImageTk, ImageDraw, ImageFontimport osimport random# 对联词库(可自行扩展)UPPER_LINES = ["春风入喜财入户", "岁月更新福满门", "一帆风顺年年好", "万事如意步步高","红梅含苞傲冬雪", "绿柳吐絮迎新春", "迎喜迎春迎富贵", "接财接福接平安"]LOWER_LINES = ["岁月更新福满门", "春风入喜财入户", "万事如意步步高", "一帆风顺年年好","绿柳吐絮迎新春", "红梅含苞傲冬雪", "接财接福接平安", "迎喜迎春迎富贵"]HORIZONTAL_LINES = ["新春大吉", "万事如意", "四季平安", "五福临门", "恭喜发财","财源广进", "吉星高照", "合家欢乐", "迎春接福"]classCoupletGenerator:def__init__(self, root): self.root = root self.root.title("对联生成器(背景图+随机/自定义+字体大小选择)") self.root.geometry("800x1000") # 微调窗口高度适配新控件# 核心变量 self.bg_image_path = None self.bg_image = None self.result_image = None self.default_font_size = 48# 默认字体大小 self._create_widgets()def_create_widgets(self):"""创建所有界面控件(新增字体大小选择)"""# 1. 上传背景图片按钮 self.upload_btn = tk.Button( self.root, text="📷 上传背景图片", command=self.upload_background, font=("宋体", 12), width=20, height=2 ) self.upload_btn.pack(pady=10)# 2. 背景图片预览区域 self.preview_label = tk.Label(self.root, text="背景图片预览(上传后显示)", font=("宋体", 10)) self.preview_label.pack(pady=5) self.preview_canvas = tk.Canvas(self.root, width=600, height=400, bg="#f0f0f0", bd=1, relief="solid") self.preview_canvas.pack(pady=5)# 3. 对联文字输入+字体大小选择区域 self.frame_input = tk.Frame(self.root) self.frame_input.pack(pady=10)# 3.1 对联输入(3行) tk.Label(self.frame_input, text="上联:", font=("宋体", 12)).grid(row=0, column=0, padx=5, pady=5) self.upper_couplet = tk.Entry(self.frame_input, font=("宋体", 14), width=25) self.upper_couplet.grid(row=0, column=1, padx=5, pady=5) tk.Label(self.frame_input, text="下联:", font=("宋体", 12)).grid(row=1, column=0, padx=5, pady=5) self.lower_couplet = tk.Entry(self.frame_input, font=("宋体", 14), width=25) self.lower_couplet.grid(row=1, column=1, padx=5, pady=5) tk.Label(self.frame_input, text="横批:", font=("宋体", 12)).grid(row=2, column=0, padx=5, pady=5) self.horizontal_couplet = tk.Entry(self.frame_input, font=("宋体", 14), width=25) self.horizontal_couplet.grid(row=2, column=1, padx=5, pady=5)# 3.2 字体大小选择(新增:下拉框+标签,和输入框同排) tk.Label(self.frame_input, text="字体大小:", font=("宋体", 12)).grid(row=1, column=2, padx=10, pady=5)# 字体大小选项:16-128,步长8,覆盖小/中/大尺寸需求 self.font_size_options = [16,24,32,40,48,56,64,72,80,88,96,104,112,128] self.font_size_var = tk.StringVar(value=str(self.default_font_size)) # 默认选中48 self.font_size_combobox = tk.OptionMenu( self.frame_input, self.font_size_var, *self.font_size_options ) self.font_size_combobox.config(font=("宋体", 12), width=6) self.font_size_combobox.grid(row=1, column=3, padx=5, pady=5)# 4. 随机生成对联按钮 self.random_btn = tk.Button( self.frame_input, text="🎲 随机生成对联", command=self.random_couplet, font=("宋体", 12), bg="#FF9800", fg="white" ) self.random_btn.grid(row=3, column=0, columnspan=4, pady=10)# 5. 生成+保存功能按钮 self.frame_btn = tk.Frame(self.root) self.frame_btn.pack(pady=20) self.generate_btn = tk.Button( self.frame_btn, text="✨ 生成对联", command=self.generate_couplet, font=("宋体", 12), width=15, bg="#4CAF50", fg="white" ) self.generate_btn.grid(row=0, column=0, padx=20) self.save_btn = tk.Button( self.frame_btn, text="💾 保存成品", command=self.save_couplet, font=("宋体", 12), width=15, bg="#2196F3", fg="white" ) self.save_btn.grid(row=0, column=1, padx=20)defupload_background(self):"""上传背景图片并显示预览""" file_path = filedialog.askopenfilename( title="选择背景图片", filetypes=[("图片文件", "*.jpg *.jpeg *.png *.bmp"), ("所有文件", "*.*")] )ifnot file_path:return self.bg_image_path = file_pathtry: self.bg_image = Image.open(file_path) self.bg_image.thumbnail((600, 400), Image.Resampling.LANCZOS) tk_image = ImageTk.PhotoImage(self.bg_image) self.preview_canvas.delete("all") self.preview_canvas.create_image(300, 200, image=tk_image) self.preview_canvas.tk_image = tk_image self.preview_label.config(text=f"已上传:{os.path.basename(file_path)}")except Exception as e: messagebox.showerror("错误", f"图片打开失败:{str(e)}") self.bg_image_path = None self.bg_image = Nonedefrandom_couplet(self):"""随机生成对联并填入输入框""" upper = random.choice(UPPER_LINES) lower = random.choice(LOWER_LINES) horizontal = random.choice(HORIZONTAL_LINES)# 清空并填入,确保无残留内容 self.upper_couplet.delete(0, tk.END) self.upper_couplet.insert(0, upper) self.lower_couplet.delete(0, tk.END) self.lower_couplet.insert(0, lower) self.horizontal_couplet.delete(0, tk.END) self.horizontal_couplet.insert(0, horizontal)defgenerate_couplet(self):"""生成对联(核心:读取选择的字体大小,自动适配排版)"""# 前置校验ifnot self.bg_image_path ornot self.bg_image: messagebox.showwarning("提示", "请先上传背景图片!")return upper_text = self.upper_couplet.get().strip() lower_text = self.lower_couplet.get().strip() horizontal_text = self.horizontal_couplet.get().strip()ifnot (upper_text or lower_text or horizontal_text): messagebox.showwarning("提示", "请至少输入一项对联内容!")returntry:# 1. 读取并校验选择的字体大小(防止非法输入)try: font_size = int(self.font_size_var.get())except: font_size = self.default_font_size messagebox.showwarning("提示", "字体大小选择异常,使用默认48号字体")# 2. 打开原始背景图,创建绘图对象 original_bg = Image.open(self.bg_image_path).convert("RGB") draw = ImageDraw.Draw(original_bg) bg_w, bg_h = original_bg.size# 3. 多系统字体兼容加载 font = Nonetry: font = ImageFont.truetype("simsun.ttc", font_size) # Windows 宋体except Exception as e1:try: font = ImageFont.truetype("STHeitiLight.ttc", font_size) # Mac 黑体except Exception as e2:try: font = ImageFont.truetype("wqy-microhei.ttc", font_size) # Linux 文泉驿except: font = ImageFont.load_default(size=font_size) messagebox.showwarning("提示", f"未找到中文字体:{e1}/{e2},文字可能显示异常!")# 4. 排版参数(随字体大小自动适配,无需手动调整) text_color = (255, 0, 0) # 对联经典红色 step = font_size + 8# 字间距:字体越大,间距越大 stroke_w = max(1, font_size // 24) # 描边宽度:随字体大小适配,更美观# 5. 绘制上联(左竖排,防超出边界)if upper_text: x = max(30, int(bg_w * 0.1)) # 左侧最小边距30 start_y = (bg_h - len(upper_text) * step) / 2 start_y = max(30, min(start_y, bg_h - len(upper_text) * step - 30)) # 上下防超出for i, c in enumerate(upper_text): draw.text((x, start_y + i * step), c, font=font, fill=text_color, stroke_width=stroke_w, stroke_fill=(0,0,0)) # 红底黑边,更醒目# 6. 绘制下联(右竖排,防超出边界)if lower_text: x = min(bg_w - 30, int(bg_w * 0.9)) # 右侧最小边距30 start_y = (bg_h - len(lower_text) * step) / 2 start_y = max(30, min(start_y, bg_h - len(lower_text) * step - 30))for i, c in enumerate(lower_text): draw.text((x, start_y + i * step), c, font=font, fill=text_color, stroke_width=stroke_w, stroke_fill=(0,0,0))# 7. 绘制横批(上中横排,精准居中)if horizontal_text: bbox = draw.textbbox((0, 0), horizontal_text, font=font) text_w = bbox[2] - bbox[0] x = (bg_w - text_w) / 2 y = max(20, int(bg_h * 0.05)) # 顶部最小边距20 draw.text((x, y), horizontal_text, font=font, fill=text_color, stroke_width=stroke_w, stroke_fill=(0,0,0))# 8. 保存结果并更新预览 self.result_image = original_bg preview = self.result_image.copy() preview.thumbnail((600, 400), Image.Resampling.LANCZOS) tk_img = ImageTk.PhotoImage(preview) self.preview_canvas.delete("all") self.preview_canvas.create_image(300, 200, image=tk_img) self.preview_canvas.tk_image = tk_img messagebox.showinfo("成功",f"对联生成完成!\n字体大小:{font_size}号\n"f"上联:{upper_text}\n下联:{lower_text}\n横批:{horizontal_text}" )except Exception as e: messagebox.showerror("错误", f"生成失败:{str(e)}") self.result_image = Nonedefsave_couplet(self):"""保存生成后的对联图片"""ifnot self.result_image: messagebox.showwarning("提示", "请先生成对联!")return save_path = filedialog.asksaveasfilename( title="保存对联图片", defaultextension=".png", filetypes=[("PNG图片", "*.png"), ("JPG图片", "*.jpg"), ("所有文件", "*.*")] )ifnot save_path:returntry: self.result_image.save(save_path) messagebox.showinfo("成功", f"对联已保存至:\n{save_path}")except Exception as e: messagebox.showerror("错误", f"保存失败:{str(e)}")if __name__ == "__main__": root = tk.Tk() app = CoupletGenerator(root) root.mainloop()16、24、32...128号字体(步长8),覆盖小尺寸背景图(小字体)、大尺寸背景图(大字体)的所有需求;如果想添加更多字体大小选项,只需修改代码中self.font_size_options列表,比如添加14、20、144等,下拉框会自动同步; 如果想调整字间距/描边宽度,只需修改step = font_size + 8和stroke_w = max(1, font_size // 24)中的数字即可(+8是字间距补偿,//24是描边适配比例)。