Kivy/Python邮件合并应用中MailMerge.merge_templates仅生效一次问题
解决Kivy邮件合并应用Save按钮重复点击不更新数据的问题
我看了你用Kivy+Python开发的邮件合并应用,遇到的问题确实挺头疼的——Save按钮第一次点击能正常生成填充后的文档,但修改输入框内容后再点,新文档还是老数据,必须重启应用才行。这问题其实出在你对MailMerge对象的复用逻辑上,咱们一步步来解决:
问题根源
你在选择Word模板时创建了全局的merge_doc对象,第一次调用merge_templates后,这个对象内部的文档结构已经被修改(合并域被替换成了第一次输入的数据),后续再调用merge_templates时,已经没有可替换的合并域了,自然不会更新内容。
解决方案
每次执行保存操作时,重新加载Word模板并创建新的MailMerge实例,这样每次都基于原始模板进行数据填充,就不会出现旧数据残留的问题了。同时,咱们也可以优化一下全局变量的使用,尽量减少不必要的全局状态。
修改后的代码
main.py
import tkinter as tk from tkinter import filedialog from kivy.app import App from kivy.base import Builder from kivy.uix.boxlayout import BoxLayout from kivy.uix.button import Button from docx import * from mailmerge import MailMerge from datetime import date from collections import OrderedDict from kivy.uix.screenmanager import ScreenManager, Screen from kivy.uix.popup import Popup from kivy.uix.scrollview import ScrollView from kivy.uix.label import Label from kivy.core.window import Window from kivy.uix.textinput import TextInput import os f_path = None merge_data = None merge_objects = None class doc_select(BoxLayout): def create_merge(self): global f_path, merge_data, merge_objects root = tk.Tk() root.withdraw() f_path = filedialog.askopenfilename() # 用临时文档获取合并域,用完即关闭 temp_doc = MailMerge(f_path) merge_fields = temp_doc.get_merge_fields() merge_data = dict.fromkeys(merge_fields) merge_objects = dict.fromkeys(merge_fields) temp_doc.close() self.show_popup() def show_popup(self): pop = Popup(title= 'Input Details', content = popup_content(), size_hint = (None,None), size = (Window.width,Window.height)) pop.open() class popup_content(BoxLayout): data_inputs = [] def __init__(self, **kwargs): super().__init__(**kwargs) def dynamic_widgets(self): for wid in merge_objects.keys(): self.ids.Mybox.add_widget(Label(text = wid, size_hint_y=None)) merge_objects[wid] = TextInput(size_hint_y = None) self.ids.Mybox.add_widget(merge_objects[wid]) def create_merge_data(self): for i in merge_objects.keys(): merge_data[i] = merge_objects[i].text def save_func(self): save_path = 'C:/Users/pavan m sunder/tutorials/Kivy/py/docx/populated_doc.docx' self.create_merge_data() # 每次保存都重新加载原始模板 with MailMerge(f_path) as merge_doc: merge_doc.merge_templates([merge_data], separator='page_break') merge_doc.write(save_path) os.startfile(save_path) def saveas_func(self): self.create_merge_data() # 同样基于原始模板生成新文档 with MailMerge(f_path) as merge_doc: merge_doc.merge_templates([merge_data], separator='page_break') save_path = filedialog.asksaveasfilename(defaultextension='.docx', filetypes=(('doc files', '*.doc*'), ('All files', '*.*'))) if save_path: # 处理用户取消保存的情况 merge_doc.write(save_path) class docsy(App): def build(self): return Builder.load_file('C:/Users/pavan m sunder/tutorials/Kivy/py/docx/sample.kv') if __name__ == '__main__': docsy().run()
关键修改点
- 移除了全局的
merge_doc变量,避免复用已被修改的文档对象 - 在选择模板时用临时
temp_doc获取合并域,用完立即关闭,不保留修改状态 save_func和saveas_func中,每次都通过with MailMerge(f_path)创建新实例,基于原始模板填充最新数据- 给
saveas_func增加了判断,防止用户取消保存对话框时抛出错误
sample.kv(无需修改)
doc_select: Button: text: 'Open' on_press: root.create_merge() <popup_content>: orientation: 'vertical' ScrollView: GridLayout: cols:2 id: Mybox orientation: 'vertical' size_hint: 1, self.minimum_height on_kv_post: root.dynamic_widgets() GridLayout: size_hint: 1, 0.1 cols: 2 Button: text: 'Save' on_press: root.save_func() Button: text: 'SaveAs' on_press: root.saveas_func()
Word模板内容(sample.docx)
Welcome «Name»!
«Date»
Hello «Name». This is word editing using python. Go ahead and unleash your python skill. «Wish»!!
这样修改后,不管你多少次修改输入框内容并点击Save/SaveAs,都会基于原始模板重新填充最新的数据,再也不用重启应用啦!
内容的提问来源于stack exchange,提问作者Pavan
相关产品推荐
相关产品推荐

