Tkinter使用after循环时if语句内变量未更新异常
Tkinter 定时邮件调度重复发信问题修复
问题根因
- 调度函数
CleaningSchedule每次执行时,都会第一行执行starts = cal.get_date()从日期控件拉取最原始的起始日期,这个starts是函数内部的局部变量,if判断块内对starts的修改仅在当前函数执行周期内生效,函数运行结束就会被销毁,根本不会持久保存。 - 因为每次轮询都会重置
starts为初始值,重新计算出的target永远是第一次设定的触发日期,只要系统日期走到触发日,每次2秒轮询进来判断target == now都成立,就会无限触发发信逻辑。 - 没有做重复触发拦截,触发日当天每2秒轮询一次就会执行一次发信,没有记录当天是否已经完成发送。
修复方案
核心思路是把调度相关的日期状态从函数局部变量改成持久化的全局状态,不要每次轮询都从控件重置初始值,同时增加重复发信拦截,发信完成后同步更新控件显示。
修正后可运行代码
from datetime import date, timedelta from tkinter import * from tkcalendar import DateEntry master = Tk() master.geometry("600x700") # 日期选择控件 cal = DateEntry(master, selectmode='day', cursor='hand1', date_pattern="yyyy-m-d") cal.place(x=140, y=20) # 间隔天数输入框 date_count_field = Entry(master, width=5) date_count_field.place(y=20, x=335) # 初始化持久化调度状态 current_start = cal.get_date() last_send_date = None # 记录上次发信日期,避免同一天重复发送 cal.set_date(current_start) # -------------------------- # 替换为你实际的业务逻辑 # 收件人迭代器、邮件发送配置 emails = ["receiver1@example.com", "receiver2@example.com"] selector = iter(emails * 1000) # 循环迭代避免耗尽,按实际需求调整 def send_mail(receivers): # 这里放你原有的SMTP发信代码 print(f"[发信完成] 收件人:{receivers},发信时间:{date.today()}") # -------------------------- def starter(): CleaningSchedule() master.after(2000, starter) def CleaningSchedule(): global current_start, last_send_date, selector now = date.today() days_calc = int(date_count_field.get()) target = current_start + timedelta(days=days_calc) print(f"[调度日志] 当前日期:{now},下次触发日期:{target}") # 同时满足两个条件才发信:到达目标日期、当天未发过 if target == now and last_send_date != now: # 获取本轮收件人 receiver_email = (next(selector), next(selector)) send_mail(receiver_email) # 更新调度状态,滚动到下一个周期 current_start = target last_send_date = now # 同步更新日期控件显示 cal.set_date(current_start) # 启动按钮 Button(master, text="Start", command=starter, width=10).place(x=410, y=650) master.mainloop()
关键修改点说明
- 移除了调度函数开头每次从日期控件拉取初始值的逻辑,用全局变量
current_start持久化当前调度的基准日期,仅在初始化、发信完成后更新该值。 - 新增
last_send_date标记位,触发日当天仅执行一次发信逻辑,不会因为2秒一次的轮询重复发信。 - 发信完成后自动把新的基准日期同步回DateEntry控件,界面可以直观看到当前调度的基准时间。
- 简化了启动按钮的绑定逻辑,去掉了无意义的lambda包裹写法。
内容的提问来源于stack exchange,提问作者UGC61
相关产品推荐
相关产品推荐

