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

如何停止Tkinter定时器线程?解决AttributeError报错问题

问题分析与修复方案

核心问题点

  1. Timer类方法与属性命名冲突
    Timer类里同时定义了self.stop_threads(Event对象)和stop_threads()方法,实例化后stop_threads会被覆盖为Event对象,调用停止方法时会触发TypeError,因为Event对象不可调用。

  2. 停止操作未指向正确线程
    MainFrame的stop方法里新建了一个Timer实例执行停止,这和正在运行的计时线程完全无关,自然无法停止计时。必须把启动的线程保存为MainFrame的实例属性,才能在停止时引用它。

  3. Entry变量绑定错误
    计时显示的Entry组件,textvariable参数传了字符串"entry6_var",应该绑定已定义的self.entry1_var,否则变量无法同步更新显示内容。

  4. 初始值显示问题
    Timer类的entry1初始为None,第一次监控线程时会显示空值,建议初始化为"00:00"。

修复后的完整代码

#!/usr/bin/python3.9
   
import tkinter as tk
from tkinter import ttk
from threading import Thread
from threading import Event
import time

class Timer(Thread):
    def __init__(self, count):
        super().__init__()
        # 重命名Event属性,避免和方法名冲突
        self.stop_event = Event()
        # 初始化默认计时值
        self.entry1 = "00:00"
        self.count = count

    def run(self):
        while True:
            if self.stop_event.is_set():
                break
            mins, secs = divmod(self.count, 60)
            timeformat = '{:02d}:{:02d}'.format(mins, secs)
            self.entry1 = timeformat
            time.sleep(1)
            self.count += 1 

    # 修改方法名,避免和属性冲突
    def stop(self):
        self.stop_event.set()

class MainFrame(ttk.Frame):
    def __init__(self, container):
        super().__init__(container)
     
        ### TOP BUTTON ROW
        self.frame1=ttk.Frame(self, padding = (0, 50, 0, 30))
        self.frame1.pack(fill="x", ipadx=0, ipady=0, padx=0, pady=0, expand=False)

        ### TOP TEXT BOX
        self.frame2=ttk.Frame(self, padding = (0, 0, 0, 0))
        self.frame2.pack(fill="x", ipadx=0, ipady=0, padx=0, pady=0, expand=False)

        ### MID TEXT BOX
        self.frame3=ttk.Frame(self, padding = (40, 20, 40, 0))
        self.frame3.pack(fill="x", ipadx=0, ipady=0, padx=0, pady=0, expand=False)

        self.pack()

        ### START BUTTON
        self.button1=tk.Button(self.frame1, text='START')
        self.button1['command'] = self.delay
        self.button1.pack(expand=True, padx=0, pady=0, ipadx=0, ipady=0, side="left")

        ### STOP BUTTON
        self.button2=tk.Button(self.frame1, text='STOP')
        self.button2['command'] = self.stop
        self.button2.pack(expand=True, padx=0, pady=0, ipadx=0, ipady=0, side="right")

        ### TIMER DISPLAY LABEL
        self.entry1_label=tk.Label(self.frame2 , text='TIMER', bg="white", fg="blue")
        self.entry1_label.pack(padx=10, pady=0, ipadx=0, ipady=0, expand=False, side="left")

        ### TIMER DISPLAY
        self.entry1_var = tk.StringVar()
        # 修正textvariable绑定,使用已定义的变量
        self.entry1=tk.Entry(self.frame2, textvariable=self.entry1_var, width=10, justify="center")
        self.entry1.pack(padx=0, pady=0, ipadx=0, ipady=4, expand=False, side="left")

        ### MSG DISPLAY
        self.text1 = tk.Text(self.frame3, height=1)
        self.text1.pack(expand=False, padx=0, pady=0, ipadx=0, ipady=0, side="left")        

    def delay(self):
        self.button1['state'] = tk.DISABLED
        # 将线程保存为实例属性,方便stop方法调用
        self.timer_thread=Timer(0)
        self.timer_thread.start()
        self.monitor(self.timer_thread)  
 
    def stop(self):
        # 检查线程是否存在且存活,避免未启动时调用报错
        if hasattr(self, 'timer_thread') and self.timer_thread.is_alive():
            self.timer_thread.stop()
            self.text1.insert("end", "stopping\n")

    def monitor(self,thread):
        if thread.is_alive():
            # 直接更新StringVar,比手动删除插入更高效
            self.entry1_var.set(thread.entry1)
            self.after(100,lambda:self.monitor(thread))
        else:
            self.text1.insert("end", "thread is stopped\n")
            self.button1['state']=tk.NORMAL

class App(tk.Tk):
    def __init__(self):
        super().__init__()  

        # 配置主窗口
        self.geometry("600x300")
        self.option_add('*Font', 'Helvetica 9')
        self.configure(bg="white")
        self.resizable(False, False)

        self.title("TEST TIMER")

        # 配置Frame样式
        f = ttk.Style()
        f.configure('TFrame', background='white')

if __name__ == "__main__":
    app = App()
    frame = MainFrame(app)
    app.mainloop()

额外优化点

  • 用self.entry1_var.set()直接更新Entry内容,更符合Tkinter变量绑定的设计逻辑,比手动删除插入更高效。
  • 在stop方法里增加线程存在性检查,避免未启动计时器时点击停止按钮触发错误。
  • 给消息显示框的文本插入加上换行符,避免内容堆叠在一起。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 10:15:28