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

Tkinter打字测速程序等级切换功能故障求助

Tkinter打字测速程序切换文本级别失效问题解决

问题描述

用Tkinter开发了带GUI的打字测速程序,包含待打文本框和输入框,能计算CPS、CPM、WPS、WPM。点击Level 1/2/3按钮想切换加载texts1.txt、texts2.txt、texts3.txt作为待打文本,但用全局变量levelsel实现时完全无效。

错误原因分析

  1. 全局变量使用错误:level1方法缺少self参数,修改全局变量时未声明global,导致赋值的是局部变量,根本没改动全局的levelsel;且用全局变量管理状态不符合面向对象设计思路。
  2. 初始化条件判断笔误:所有elif判断的都是levelsel == "1",应分别对应"2"、"3",且初始化时levelsel被设为整数0,后续判断用字符串"1",类型不匹配。
  3. 切换级别后未更新文本:即便改了levelsel,也没有重新加载对应文本文件,更没更新显示的待打标签内容。
  4. Reset方法错误:重新创建sample_label但未调用grid布局,新标签无法显示,应直接修改现有标签的文本。

修正后的完整代码

import tkinter as tk 
import time 
import threading 
import random 

class TypeSpeedGUI: 
    def __init__(self):
        self.root = tk.Tk()
        self.root.title("Type Speed App")
        self.root.geometry("800x600")

        # 用实例变量代替全局变量,管理当前级别
        self.levelsel = 0
        # 加载初始文本
        self.load_text()

        self.frame = tk.Frame(self.root)

        self.sample_label = tk.Label(self.frame, text=random.choice(self.texty), font=("Helvetica", 18))
        self.sample_label.grid(row=1, column=3, columnspan=2, padx=5, pady=5)

        self.input_entry = tk.Entry(self.frame, width=40, font=("Helvetica", 24))
        self.input_entry.grid(row=2, column=3, columnspan=2, padx=5, pady=10)
        self.input_entry.bind("<KeyPress>", self.start)

        self.speed_label = tk.Label(self.frame, text ="Speed: \n0.00 CPS\n0.00 CPM\n0.00 WPS\n0.00 WPM", font=("Helvetica", 18))
        self.speed_label.grid(row=4, column=3, columnspan=2, padx=5, pady=10)
        
        self.reset_button = tk.Button(self.frame, text="Reset", command=self.reset)
        self.reset_button.grid(row=5, column=3, columnspan=2, padx=5, pady=10)

        self.level1_button = tk.Button(self.frame, text="Level 1", command=lambda: self.switch_level(1))
        self.level1_button.grid(row=0, column=2, padx=5, pady=10)

        self.level2_button = tk.Button(self.frame, text="Level 2", command=lambda: self.switch_level(2))
        self.level2_button.grid(row=0, column=3, padx=5, pady=10)

        self.level3_button = tk.Button(self.frame, text="Level 3", command=lambda: self.switch_level(3))
        self.level3_button.grid(row=0, column=4, padx=5, pady=10)

        self.frame.pack(expand=True)

        self.counter = 0 
        self.started = False

        self.root.mainloop()

    def start(self, event):
        if not self.started: 
            if event.keycode not in [16, 17, 18]: 
                self.started = True 
                t = threading.Thread(target=self.time_thread, daemon=True)  # 加daemon=True避免线程残留
                t.start()
        # 检查输入是否匹配开头
        input_text = self.input_entry.get()
        sample_text = self.sample_label.cget("text")
        if not sample_text.startswith(input_text):
            self.input_entry.config(fg="red")
        else:
            self.input_entry.config(fg="black")
        # 完全匹配时结束计时
        if input_text == sample_text:
            self.started = False 
            self.input_entry.config(fg="green")
                        

    def time_thread(self):
        while self.started:
            time.sleep(0.1)
            self.counter += 0.1 
            input_len = len(self.input_entry.get())
            cps = input_len / self.counter if self.counter > 0 else 0
            cpm = cps * 60
            word_len = len(self.input_entry.get().split())
            wps = word_len / self.counter if self.counter > 0 else 0
            wpm = wps * 60
            self.speed_label.config(text=f"Speed: \n{cps:.2f} CPS\n{cpm:.2f} CPM\n{wps:.2f} WPS\n{wpm:.2f} WPM")

    def switch_level(self, level):
        self.levelsel = level
        self.load_text()
        # 切换级别后直接更新待打文本
        self.sample_label.config(text=random.choice(self.texty))
        self.reset()  # 切换级别后重置状态

    def load_text(self):
        # 根据当前级别加载对应文本
        if self.levelsel == 1:
            self.texty = open("texts1.txt", "r").read().split("\n")
        elif self.levelsel == 2:
            self.texty = open("texts2.txt", "r").read().split("\n")
        elif self.levelsel == 3:
            self.texty = open("texts3.txt", "r").read().split("\n")
        else:
            self.texty = open("start.txt", "r").read().split("\n")
        # 过滤空行
        self.texty = [line.strip() for line in self.texty if line.strip()]

    def reset(self):
        self.started = False 
        self.counter = 0 
        self.speed_label.config(text="Speed: \n0.00 CPS\n0.00 CPM\n0.00 WPS\n0.00 WPM")
        self.sample_label.config(text=random.choice(self.texty))
        self.input_entry.delete(0, tk.END)
        self.input_entry.config(fg="black")

TypeSpeedGUI() 

关键修正点说明

  1. 用实例变量替代全局变量:把levelsel改成类的实例变量self.levelsel,避免全局变量混乱,符合面向对象编程规范。
  2. 统一级别切换方法:新增switch_level方法,通过lambda传递级别参数减少重复代码;切换级别后直接调用load_text加载对应文本,并更新待打标签。
  3. 修正初始化条件判断:把笔误(全是"1")改成对应级别,同时统一用整数判断,避免类型不匹配。
  4. 修复Reset方法:不再重新创建标签,直接修改现有sample_label的文本,确保界面更新。
  5. 线程优化:给计时线程加daemon=True,程序关闭时线程自动退出,避免残留。
  6. 文本过滤:加载文本时过滤空行,避免出现空白待打文本。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 01:45:54