You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为同步倒计时计时器添加可恢复的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() 
问题原因
  1. 主线程阻塞:原start函数使用while循环+time.sleep(1),会完全占用Tkinter主线程,导致Pause按钮的点击事件无法被处理,程序陷入假死状态。
  2. 变量作用域错误:pause函数中修改pause_flag时未声明为全局变量,实际修改的是局部变量,无法控制倒计时循环。
  3. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 06:35:31