Python Tkinter闹钟GUI实现控制台倒计时展示到界面问题
问题原因
Tkinter的UI渲染、事件响应逻辑全部运行在主线程,你直接在按钮回调中执行带time.sleep的死循环,会完全阻塞主线程,所有UI更新操作都会被暂停,因此你尝试修改Label内容的操作不会生效。
解决思路
- 将闹钟计时逻辑放到独立子线程运行,避免阻塞主线程UI
- 新增绑定
StringVar的Label控件,专门用来展示倒计时信息 - 子线程中计算当前时间和设定时间的差值,实时更新到绑定的Label变量上即可
- 子窗口改用
Toplevel()而非新建Tk()实例,避免多主事件循环导致的逻辑异常
修改后完整代码
# Import Required Library from tkinter import * import datetime import time import winsound from threading import * from tkinter import messagebox def submit(): # 子窗口改用Toplevel,不要新建Tk实例 root = Toplevel() root.geometry("500x400") root.config(bg="#447c84") root.title('MathAlarm') # 倒计时显示变量 countdown_var = StringVar() countdown_var.set("请设置闹钟时间后点击启动") def alarm(): # Infinite Loop while True: # Set Alarm set_alarm_time = f"{hour.get()}:{minute.get()}:{second.get()}" # Wait for one seconds time.sleep(1) # Get current time current_time = datetime.datetime.now().strftime("%H:%M:%S") print(current_time,set_alarm_time) # 计算剩余时间 try: set_time_obj = datetime.datetime.strptime(set_alarm_time, "%H:%M:%S") current_time_obj = datetime.datetime.strptime(current_time, "%H:%M:%S") remaining = set_time_obj - current_time_obj # 设定时间已过当日的话自动算到次日 if remaining.total_seconds() < 0: remaining += datetime.timedelta(days=1) # 更新倒计时显示,去掉微秒部分 countdown_var.set(f"剩余时间:{str(remaining).split('.')[0]}\n当前时间:{current_time} | 设定时间:{set_alarm_time}") except: pass # Check whether set alarm is equal to current time or not if current_time == set_alarm_time: print("Time to Wake up") countdown_var.set("时间到!起床啦!") # Playing sound winsound.PlaySound("sound.wav",winsound.SND_ASYNC) messagebox.showinfo(title="ALARM", message="Alarm is going off, its time to wake up!") break # Add Labels, Frame, Button, Optionmenus Label(root,text="Alarm Clock",font=("Helvetica 20 bold"),fg="Black",bg="#447c84").pack(pady=10) Label(root,text="Set Time",font=("Helvetica 15 bold"),bg="#447c84").pack() frame = Frame(root,bg="#447c84") frame.pack() hour = StringVar(root) hours = ('00', '01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23' ) hour.set(hours[0]) hrs = OptionMenu(frame, hour, *hours) hrs.pack(side=LEFT) minute = StringVar(root) minutes = ('00', '01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31', '32', '33', '34', '35', '36', '37', '38', '39', '40', '41', '42', '43', '44', '45', '46', '47', '48', '49', '50', '51', '52', '53', '54', '55', '56', '57', '58', '59') minute.set(minutes[0]) mins = OptionMenu(frame, minute, *minutes) mins.pack(side=LEFT) second = StringVar(root) seconds = ('00', '01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31', '32', '33', '34', '35', '36', '37', '38', '39', '40', '41', '42', '43', '44', '45', '46', '47', '48', '49', '50', '51', '52', '53', '54', '55', '56', '57', '58', '59') second.set(seconds[0]) secs = OptionMenu(frame, second, *seconds) secs.pack(side=LEFT) # 倒计时显示Label Label(root, textvariable=countdown_var, font=("Helvetica 15 bold"), fg="red", bg="#447c84", justify=CENTER).pack(pady=20) # 按钮回调改为启动子线程运行alarm逻辑 Button(root,text="Set Alarm",font=("Helvetica 15"),command=lambda: Thread(target=alarm, daemon=True).start()).pack(pady=10) Button(root,text="Exit",font=("Helvetica 15"), command=lambda:root.destroy()).pack(pady=10) root = Tk() root.title('MathAlarm') root.geometry('347x400') root.config(bg="#447c84") welcomelabel = Label(root, text="Welcome to Math Alarm", font=("Times", "24", "bold"), bg="#447c84") welcomelabel.pack(pady=30) ext = Button(root, text="Exit", padx=20, pady=10, relief=SOLID, font=("Times", "14", "bold"), command=lambda:root.destroy()) reg = Button(root, text="Create new Alarm", padx=20, pady=10, relief=SOLID, font=("Times", "14", "bold"), command=submit) reg.pack(pady=20) ext.pack(pady=20) # Execute Tkinter root.mainloop()
补充说明
- 代码中修正了原来小时、分钟选项里包含24、60的无效值问题
- 增加了跨零点的时间计算逻辑,设定时间晚于当前时间时会自动计算到次日的剩余时间
- 子线程设置为daemon守护线程,关闭窗口时会自动终止计时逻辑
内容的提问来源于stack exchange,提问作者ChrisBoss
相关产品推荐
相关产品推荐

