如何为同步倒计时计时器添加可恢复的Pause功能?
问题描述
现有一个基于Tkinter的3个同步倒计时计时器,Start和Stop功能运行正常,需要添加Pause按钮实现计时冻结,再次点击Start可恢复计时。尝试通过pause_flag变量实现,但点击Pause后程序无响应,相关代码如下:
import time from tkinter import * def first(): print("first new time is up \n") #stop module (1) def second(): print("second new time is up \n") #stop module (2) def third(): print("third new time is up \n") #stop module (3) #Create interface# root = Tk() root.geometry("1024x600") root.title("Countdown Timer") def modules(): if (clockTime[0] == 0): first() if(clockTime[1] == 0): second() if(clockTime[2] == 0): third() #initialize timers lists timers_number = 3 hrString=[0]*timers_number minString=[0]*timers_number secString=[0]*timers_number totalSeconds = [0]*timers_number totalMinutes = [0]*timers_number totalHours = [0]*timers_number for i in range(timers_number): hrString[i] = StringVar() hrString[i].set("00") for i in range(timers_number): minString[i] = StringVar() minString[i].set("00") for i in range(timers_number): secString[i] = StringVar() secString[i].set("00") #initialze list of each timer with zero seconds to store timers in seconds clockTime = [0]*timers_number #Get User Input hourTextBox1 = Entry(root, width=3, font=("Calibri", 20, ""),textvariable=hrString[0]).place(x=170, y=100) minuteTextBox1 = Entry(root, width=3, font=("Calibri", 20, ""),textvariable=minString[0]).place(x=220, y=100) secondTextBox1 = Entry(root, width=3, font=("Calibri", 20, ""),textvariable=secString[0]).place(x=270, y=100) hourTextBox2 = Entry(root, width=3, font=("Calibri", 20, ""), textvariable=hrString[1]).place(x=170, y=180) minuteTextBox2 = Entry(root, width=3, font=("Calibri", 20, ""), textvariable=minString[1]).place(x=220, y=180) secondTextBox2 = Entry(root, width=3, font=("Calibri", 20, ""), textvariable=secString[1]).place(x=270, y=180) hourTextBox3 = Entry(root, width=3, font=("Calibri", 20, ""), textvariable=hrString[2]).place(x=170, y=260) minuteTextBox3 = Entry(root, width=3, font=("Calibri", 20, ""), textvariable=minString[2]).place(x=220, y=260) secondTextBox3 = Entry(root, width=3, font=("Calibri", 20, ""), textvariable=secString[2]).place(x=270, y=260) pause_flag = 0 def start(): try: for i in range (timers_number): clockTime[i] = int(hrString[i].get())*3600 + int(minString[i].get())*60 + int(secString[i].get()) except: print("Incorrect values") while (clockTime[0] > -1 or clockTime[1] > -1 or clockTime[2] > -1): for i in range(timers_number): if(clockTime[i] > -1): totalMinutes[i], totalSeconds[i] = divmod(clockTime[i], 60) if(totalMinutes[i]>60): totalHours[i], totalMinutes[i] = divmod(totalMinutes[i], 60) hrString[i].set("{0:2d}".format(totalHours[i])) minString[i].set("{0:2d}".format(totalMinutes[i])) secString[i].set("{0:2d}".format(totalSeconds[i])) #time expires if(clockTime[i] == 0): hrString[i].set("00") minString[i].set("00") secString[i].set("00") for i in range(timers_number): if(clockTime[i] == 0): modules() #while (pause_flag == 0): clockTime[i] -= 1 time.sleep(1) root.update() def stop(): for i in range (timers_number): clockTime[i] = 0 def pause(): if pause_flag == 0: pause_flag = 1 first() second() third() setTimeButton = Button(root, text='START', bd='5', command=start).place(x=200, y=500) setTimeButton = Button(root, text='STOP', bd='5', command=stop).place(x=350, y=500) setTimeButton = Button(root, text='PAUSE', bd='5', command=pause).place(x=500, y=500) root.mainloop()
问题原因
- 主线程阻塞:原
start函数使用while循环+time.sleep(1),会完全占用Tkinter主线程,导致Pause按钮的点击事件无法被处理,程序陷入假死状态。 - 变量作用域错误:
pause函数中修改pause_flag时未声明为全局变量,实际修改的是局部变量,无法控制倒计时循环。 - Pause逻辑错误:原
pause函数错误调用了first()、second()、third()这些计时结束的回调函数,不符合Pause功能的预期。
修改后的代码
import time from tkinter import * def first(): print("first new time is up \n") #stop module (1) def second(): print("second new time is up \n") #stop module (2) def third(): print("third new time is up \n") #stop module (3) #Create interface# root = Tk() root.geometry("1024x600") root.title("Countdown Timer") def modules(): if (clockTime[0] == 0): first() if(clockTime[1] == 0): second() if(clockTime[2] == 0): third() #initialize timers lists timers_number = 3 hrString=[0]*timers_number minString=[0]*timers_number secString=[0]*timers_number totalSeconds = [0]*timers_number totalMinutes = [0]*timers_number totalHours = [0]*timers_number for i in range(timers_number): hrString[i] = StringVar() hrString[i].set("00") for i in range(timers_number): minString[i] = StringVar() minString[i].set("00") for i in range(timers_number): secString[i] = StringVar() secString[i].set("00") #initialze list of each timer with zero seconds to store timers in seconds clockTime = [0]*timers_number pause_flag = False # 用布尔值更直观 timer_running = False # 标记计时器是否在运行 #Get User Input hourTextBox1 = Entry(root, width=3, font=("Calibri", 20, ""),textvariable=hrString[0]).place(x=170, y=100) minuteTextBox1 = Entry(root, width=3, font=("Calibri", 20, ""),textvariable=minString[0]).place(x=220, y=100) secondTextBox1 = Entry(root, width=3, font=("Calibri", 20, ""),textvariable=secString[0]).place(x=270, y=100) hourTextBox2 = Entry(root, width=3, font=("Calibri", 20, ""), textvariable=hrString[1]).place(x=170, y=180) minuteTextBox2 = Entry(root, width=3, font=("Calibri", 20, ""), textvariable=minString[1]).place(x=220, y=180) secondTextBox2 = Entry(root, width=3, font=("Calibri", 20, ""), textvariable=secString[1]).place(x=270, y=180) hourTextBox3 = Entry(root, width=3, font=("Calibri", 20, ""), textvariable=hrString[2]).place(x=170, y=260) minuteTextBox3 = Entry(root, width=3, font=("Calibri", 20, ""), textvariable=minString[2]).place(x=220, y=260) secondTextBox3 = Entry(root, width=3, font=("Calibri", 20, ""), textvariable=secString[2]).place(x=270, y=260) def update_timer(): global timer_running if not pause_flag and timer_running: # 更新所有计时器显示 for i in range(timers_number): if clockTime[i] > -1: totalMinutes[i], totalSeconds[i] = divmod(clockTime[i], 60) totalHours[i], totalMinutes[i] = divmod(totalMinutes[i], 60) hrString[i].set("{0:02d}".format(totalHours[i])) minString[i].set("{0:02d}".format(totalMinutes[i])) secString[i].set("{0:02d}".format(totalSeconds[i])) if clockTime[i] == 0: hrString[i].set("00") minString[i].set("00") secString[i].set("00") modules() # 倒计时减1 for i in range(timers_number): if clockTime[i] > 0: clockTime[i] -= 1 # 检查是否所有计时器都结束 if all(t <= 0 for t in clockTime): timer_running = False return # 1秒后再次调用,替代while循环和sleep root.after(1000, update_timer) def start(): global pause_flag, timer_running # 如果是暂停状态,恢复计时 if pause_flag: pause_flag = False if not timer_running: timer_running = True update_timer() return # 首次启动或停止后重新启动 try: for i in range (timers_number): clockTime[i] = int(hrString[i].get())*3600 + int(minString[i].get())*60 + int(secString[i].get()) except: print("Incorrect values") return timer_running = True pause_flag = False update_timer() def stop(): global timer_running, pause_flag timer_running = False pause_flag = False for i in range (timers_number): clockTime[i] = 0 hrString[i].set("00") minString[i].set("00") secString[i].set("00") def pause(): global pause_flag pause_flag = not pause_flag # 切换暂停状态 setTimeButton = Button(root, text='START', bd='5', command=start).place(x=200, y=500) setTimeButton = Button(root, text='STOP', bd='5', command=stop).place(x=350, y=500) setTimeButton = Button(root, text='PAUSE', bd='5', command=pause).place(x=500, y=500) root.mainloop()
关键修改点
- 替换阻塞逻辑:用Tkinter的
after()方法替代while循环和time.sleep(),让主线程能处理UI事件,避免假死。 - 修复变量作用域:在修改全局变量的函数中添加
global声明,确保变量能被正确修改。 - 优化Pause功能:
pause()函数仅切换pause_flag的状态,不再错误调用计时结束回调。 - 增加状态控制:添加
timer_running标记,避免重复启动计时器,同时在所有计时器结束后自动停止。 - 规范时间显示:用
{0:02d}格式化输出,确保时间始终显示为两位数字,补零更美观。
内容的提问来源于stack exchange,提问作者Alaa
相关产品推荐
相关产品推荐

