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

Tkinter中如何实现后台计时器与接硬币游戏逻辑的并行执行

解决Tkinter中计时器与游戏逻辑同时运行的问题

我明白你的困扰——现在计时器跑完才启动游戏,而且计时结束后也没法终止游戏循环。咱们一步步来修复这个问题,核心要解决线程创建错误、GUI操作必须在主线程以及线程间安全通信这几个关键点。

问题根源分析

  1. 线程创建方式错误:你写Thread(target=countdown(int(timeEntry.get())))时,会先执行countdown函数,等它完全跑完才会创建线程,这就是为什么游戏要等计时器结束才启动的原因。
  2. 子线程操作GUI:movingCoin里有大量Canvas的操作,Tkinter的GUI组件只能在主线程中修改,把它放到子线程会导致崩溃或异常。
  3. 死循环阻塞主线程:movingCoin里的while True会卡住Tkinter的事件循环,应该用Tkinter自带的after方法实现非阻塞的游戏循环。
  4. 线程通信不可靠:用全局字符串allowedTime判断计时结束,存在线程同步风险,用threading.Event来标记"时间到"的状态更安全。

修改后的完整代码

import tkinter
import random
import time
from threading import Thread, Event

window = tkinter.Tk()
window.title("Dukaty")
canvas = tkinter.Canvas(window, height=400, width=800)
canvas.pack()

timeLabel = tkinter.Label(text="Time:", font="Helvetica 15")
timeLabel.pack()
timeEntry = tkinter.Entry(bd=1)
timeEntry.pack()

mistakesLabel = tkinter.Label(text="Allowed mistakes:", font="Helvetica 15")
mistakesLabel.pack()
mistakesEntry = tkinter.Entry(bd=1)
mistakesEntry.pack()

timeEntry.insert(0, "0")
mistakesEntry.insert(0, "0")

speed = 250
score = 0
mistakes = 0
time_up_event = Event()  # 用于标记计时结束的事件
current_coin = None
# 先创建碗的图形(你可以替换成自己的图片)
bowl = canvas.create_rectangle(300, 360, 380, 380, fill="brown")
# 假设你有coinImg,这里先注释掉,实际使用时替换
# coinImg = tkinter.PhotoImage(file="coin.png")

def countdown(t):
    global time_up_event
    while t and not time_up_event.is_set():
        mins, secs = divmod(t, 60)
        timer = '{:02d}:{:02d}'.format(mins, secs)
        print(f"Remaining time: {timer}")
        time.sleep(1)
        t -= 1
    time_up_event.set()  # 设置时间到事件
    window.after(0, end)  # 确保在主线程执行结束操作

def update_score_and_mistakes():
    # 统一更新分数和错误数,避免重复代码
    canvas.delete("score")
    canvas.create_text(700, 50, text=str(score), font="Helvetica 20", fill="green", tags="score")
    canvas.delete("mistakes")
    canvas.create_text(700, 80, text=str(mistakes), font="Helvetica 20", fill="red", tags="mistakes")

def move_coin():
    global speed, score, mistakes, current_coin
    # 检查终止条件:时间到、错误数达标
    if time_up_event.is_set():
        return
    allowed_mistakes = int(mistakesEntry.get())
    if allowed_mistakes != 0 and mistakes >= allowed_mistakes:
        end()
        return

    if current_coin is None:
        # 创建新硬币(如果有图片就替换成image=coinImg)
        current_coin = canvas.create_image(random.randint(15, 585), 18, tags="coin")
        update_score_and_mistakes()

    # 移动硬币
    y_pos = canvas.coords(current_coin)[1]
    if y_pos < 360:
        canvas.move(current_coin, 0, 5)
        window.after(speed, move_coin)  # 递归调用实现循环
    else:
        # 判断是否接住硬币
        bowl_x = canvas.coords(bowl)[0]
        coin_x = canvas.coords(current_coin)[0]
        if (bowl_x - 40) < coin_x < (bowl_x + 40):
            score += 1
        else:
            mistakes += 1
        # 删除旧硬币,准备新的
        canvas.delete(current_coin)
        current_coin = None
        speed -= 10  # 加快速度
        window.after(0, move_coin)  # 立即创建新硬币

def end():
    global time_up_event
    time_up_event.set()
    print(f"Game Over! Score: {score}, Mistakes: {mistakes}")
    window.destroy()

def tap():
    global time_up_event
    time_up_event.clear()  # 重置时间到事件
    # 启动计时器线程(daemon=True让线程随主线程退出)
    set_time = int(timeEntry.get())
    if set_time > 0:
        Thread(target=countdown, args=(set_time,), daemon=True).start()
    # 在主线程启动游戏循环
    move_coin()

endButton = tkinter.Button(text="End", command=end)
endButton.pack(side="right")
startButton = tkinter.Button(text="Start", command=tap)
startButton.pack(side="left")

window.mainloop()

关键改动说明

  1. 用threading.Event实现线程通信:time_up_event是线程安全的,计时器结束时调用set(),游戏循环里检查is_set()来判断是否终止。
  2. 修正线程创建:Thread(target=countdown, args=(set_time,), daemon=True).start(),这里把函数名传给target,参数用args传递,不会提前执行函数;daemon=True确保主线程退出时子线程也跟着结束。
  3. 非阻塞游戏循环:用window.after(speed, move_coin)递归调用代替死循环,不会阻塞Tkinter的事件循环,GUI能正常响应操作。
  4. GUI操作统一在主线程:所有Canvas的修改都在主线程中执行,避免子线程操作GUI的风险。
  5. 代码复用:提取update_score_and_mistakes函数,减少重复代码,让逻辑更清晰。

内容的提问来源于stack exchange,提问作者Marek Maďar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 09:17:42