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

Tkinter开发打字速度App如何暂停循环等待用户按指定键

问题核心原因

你现在用for循环+time.sleep()的写法本身就会阻塞Tkinter的主事件循环。Tkinter是事件驱动模型,长循环跑在主线程里会直接卡住界面,没法响应键盘输入,之前用按钮+wait_variable体验差本质也是没顺着事件驱动的思路设计逻辑。

最优实现思路

完全抛弃一次性跑完的for循环逻辑,改用状态指针+键盘事件绑定的方案:

  • 维护一个当前高亮单词的索引指针,初始值为0
  • 单独写一个高亮当前单词的方法,每次调用只高亮指针对应的单词
  • 给输入框绑定空格键按下事件,触发时依次完成:校验当前输入的单词是否正确、清除上一个单词的高亮、指针+1、调用高亮方法渲染下一个单词,直到指针走到单词列表末尾结束流程
  • 不需要额外的等待逻辑、不需要阻塞循环,所有流程靠用户的键盘输入事件驱动,完全符合GUI程序的运行逻辑,也不会出现界面卡顿的问题。
可直接复用的修改代码

删掉原来type_and_check里的for循环,修改对应类方法即可,不需要额外引入第三方键盘监听库:

import tkinter
from tkinter import *
import time

class TypingSpeedApp:
    def __init__(self):
        self.typing_speed_app = Tk()
        self.typing_speed_app.title("Typing Speed Application")
        self.typing_speed_app.minsize(1200, 700)
        self.typing_speed_app.resizable(False, False)
        # define background image
        self.background_image = PhotoImage(file="background_image.PNG")
        # create canvas
        self.canvas = Canvas(self.typing_speed_app, width=1200, height=700)
        self.canvas.pack(fill="both", expand=True)
        # set image in canvas
        self.canvas.create_image(0, 0, image=self.background_image, anchor="nw")
        # add a label
        self.canvas.create_text(590, 50, text="Welcome to typing speed application", font=("Helvetica", 30),
                                fill="white")
        # add a label
        self.canvas.create_text(130, 90, text="Base text:", font=("Helvetica", 20), fill="white", state=DISABLED)
        # Define Entry Boxes
        self.base_text = Text(self.typing_speed_app, font=("Helvetica", 24), width=60, height=2, bd=0,
                              wrap=tkinter.WORD)
        self.base_text_window = self.canvas.create_window(70, 120, anchor="nw", window=self.base_text)
        # add a label
        self.canvas.create_text(130, 220, text="Type here:", font=("Helvetica", 20), fill="white")
        # Define Entry Boxes
        self.type_text = Text(self.typing_speed_app, font=("Helvetica", 24), width=20, height=1, bd=0,
                              wrap=tkinter.WORD)
        self.type_text_window = self.canvas.create_window(70, 250, anchor="nw", window=self.type_text)
        # add a label
        self.canvas.create_text(510, 220, text="Timer:", font=("Helvetica", 20), fill="white")
        # Define Entry Boxes
        self.time_text = Text(self.typing_speed_app, font=("Helvetica", 24), width=5, height=1, bd=0,
                              background="green")
        self.time_text_window = self.canvas.create_window(470, 250, anchor="nw", window=self.time_text)
        # text get from base_text
        self.base_text_get_test = ""
        # this list contain indexes of spaces
        self.space_indexes = ["1.0"]
        # 新增:当前高亮单词的指针
        self.current_word_idx = 0
        # 新增:提前拆分好基准文本的单词列表,方便校验
        self.word_list = []
        # 给输入框绑定空格键事件,仅输入框聚焦时触发,避免全局快捷键冲突
        self.type_text.bind('<space>', self.on_space_press)

    # show gui
    def main_loop(self):
        self.typing_speed_app.mainloop()

    def read_text_file(self):
        with open('text_1.txt') as text_file:
            lines = str(text_file.readlines())
        content = lines[2:-2] + " "
        self.base_text.insert(tkinter.END, content)
        self.base_text.configure(state=DISABLED)
        self.base_text_get_test = self.base_text.get("1.0", tkinter.END).strip()
        # 拆分单词列表,过滤空字符串
        self.word_list = [w for w in self.base_text_get_test.split(" ") if w]

    def find_space_intervals(self):
        idx = '1.0'
        while 1:
            idx = self.base_text.search(" ", idx, stopindex=END)
            if not idx:
                break
            last_idx = '%s+%dc' % (idx, 1)
            self.space_indexes.append(idx)
            self.space_indexes.append(last_idx)
            idx = last_idx

    def highlight_current_word(self):
        # 所有单词遍历完成直接结束
        if self.current_word_idx * 2 >= len(self.space_indexes) -1:
            # 此处可添加打字完成后的结算逻辑,比如计算WPM、弹出结果提示
            return
        start = self.space_indexes[self.current_word_idx * 2]
        end = self.space_indexes[self.current_word_idx * 2 + 1]
        self.base_text.tag_add("select", start, end)
        self.base_text.tag_configure("select", background="gray")
        self.base_text.see(start)

    def on_space_press(self, event):
        # 获取用户当前输入的内容,去掉首尾空格
        input_content = self.type_text.get("1.0", END).strip()
        # 清空输入框,准备下一个单词输入
        self.type_text.delete("1.0", END)
        # 移除上一个单词的高亮
        prev_start = self.space_indexes[self.current_word_idx *2]
        prev_end = self.space_indexes[self.current_word_idx *2 +1]
        self.base_text.tag_remove("select", prev_start, prev_end)
        
        # 此处可添加输入校验逻辑:对比input_content和self.word_list[self.current_word_idx],输入错误可做标红提示
        
        # 指针后移,高亮下一个单词
        self.current_word_idx +=1
        self.highlight_current_word()
        # 返回break可阻止空格输入到文本框,不需要可以删除
        return "break"

    def type_and_check(self):
        # 初始化时只高亮第一个单词即可
        self.highlight_current_word()

注意:原有main.py的调用逻辑无需修改,保持原有执行顺序即可正常运行。

额外优化点
  • 去掉第三方keyboard库的全局监听逻辑,直接绑定Tkinter组件的<space>事件即可,不会误触其他软件的快捷键
  • 可以在校验逻辑里增加对错反馈:输入正确单词高亮绿色,错误高亮红色,交互体验更好
  • 计时逻辑可以绑定在第一个单词输入时自动启动,不需要额外点击按钮触发
  • 原有读取文件的逻辑直接转str(readlines())会带列表括号和转义字符,建议改成逐行拼接后再处理,避免特殊字符混进待打字文本中

内容的提问来源于stack exchange,提问作者waxier358

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 13:01:41