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

Python tkinter定时器首次结束后程序崩溃且提示音循环播放问题求助

问题原因分析
  • 根本问题出在setTimer()函数对window.after()的使用逻辑错误:window.after()是异步注册接口,调用后会立刻返回不会阻塞等待定时结束,所以你在for循环里批量调用after()的效果是一次性注册了9个定时器:4次循环共8个1分钟触发的定时器、1个30分钟触发的定时器,所有定时器的计时起点都是setTimer()被调用的瞬间。
  • 当1分钟时间到后,8个timerDone()回调会几乎同时触发,自然会出现提示音连续循环播放的情况,而playsound默认是同步阻塞执行,大量回调同时调用会直接卡死tkinter主事件循环,最终导致程序崩溃,后续逻辑自然无法执行。
  • 额外影响:playsound默认同步播放的逻辑会阻塞tkinter的界面刷新,哪怕只有单个定时器触发,也会出现界面假死的问题。
修复方案
  1. 放弃批量注册定时器的逻辑,改为链式触发:每次timerDone()执行完成后,再注册下一次需要的定时器,保证任务按顺序执行
  2. 调用playsound时添加block=False参数(需要playsound 1.3.0及以上版本),让音频异步播放,不阻塞主事件循环;如果版本不支持该参数,可以用子线程播放音频
  3. 新增定时器ID存储变量,重置/切换定时器时可以取消未触发的任务,避免重复注册
修正后的核心代码示例
import threading # 低版本playsound需要用线程播放时导入

# 新增变量存储定时器ID,方便取消
current_timer_id = None

def timerDone():
    global onBreak, isTimerDone, pomodoros, current_timer_id
    isTimerDone = True
    onBreak = not onBreak
    if onBreak:
        reminderTitle.config(text="Go on break!")
        # 休息时间到了之后触发下一轮工作计时,5分钟短休息可按需调整
        current_timer_id = window.after(5 * 60 * 1000, timerDone)
    else:
        pomodoros += 1
        reminderTitle.config(text="Get to work!")
        if pomodoros < 4:
            # 没到4轮番茄钟就继续25分钟工作计时
            current_timer_id = window.after(25 * 60 * 1000, timerDone)
        else:
            # 到4轮就触发30分钟长休息
            pomodoros = 0
            reminderTitle.config(text="Take a long break!")
            current_timer_id = window.after(30 * 60 * 1000, timerDone)
    timer.config(text="Completed")
    # 异步播放音频,二选一即可
    # 高版本playsound用这个:
    playsound(f'{os.getcwd()}/Audio/notification.mp3', block=False)
    # 低版本playsound用线程方案:
    # threading.Thread(target=playsound, args=(f'{os.getcwd()}/Audio/notification.mp3',), daemon=True).start()

def setTimer(mins=25):
    global pomodoros, onBreak, current_timer_id
    # 先取消之前的定时器,避免重复触发
    if current_timer_id is not None:
        window.after_cancel(current_timer_id)
    pomodoros = 0
    onBreak = False
    timer.config(text="In Progress")
    reminderTitle.config(text="Get to work!")
    # 只注册第一个定时器,后续链式触发
    current_timer_id = window.after(mins * 60 * 1000, timerDone)

def resetTimer():
    global current_timer_id, pomodoros, onBreak
    if current_timer_id is not None:
        window.after_cancel(current_timer_id)
        current_timer_id = None
    pomodoros = 0
    onBreak = False
    timer.config(text="Not Started")
    reminderTitle.config(text="There are no current reminders.")

内容的提问来源于stack exchange,提问作者Daniel Portas-Levy

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 13:27:04