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

连续运行多TK实例报错:_tkinter.TclError及无效命令名求助

问题:连续运行多个CustomTkinter GUI实例时出现TclError错误

问题场景

需要在another.py中循环调用GUI_file.py里的GUI类实例,实现自动连续运行无需人工干预,但启动新实例时触发_tkinter.TclError: can't invoke "destroy" command: application has been destroyed及一系列无效命令名错误,尝试替换self.destroy()为self.quit()等方法未解决。

相关代码

GUI_file.py

import os
import cv2
import sys
import tkinter as tk
import customtkinter
from pathlib import Path
from PIL import Image, ImageTk
from tkinter import Canvas, PhotoImage


class GUI(customtkinter.CTk):
    def __init__(self, id):
       super().__init__()
       self.title("Front-end UI")
       self.geometry(f"{1080}x{720}")

       # configure grid layout (4x4)
       self.grid_columnconfigure(1, weight=1)
       self.grid_columnconfigure((2, 3), weight=0)
       self.grid_rowconfigure((0, 1, 2), weight=1)
       self.image_window = customtkinter.CTkFrame(self)
       self.image_window.grid(row=0, column=1, rowspan=2, padx=5, pady=40, sticky="nsew")

       self.image_path = os.path.join(Path().absolute() / 'dependent_data', "poo.png")
       self.org_image = cv2.imread(self.image_path)
       self.canvas = Canvas(self.image_window, width=1280, height=720, background="white")
       self.canvas.pack()
       self.canvas_image = ImageTk.PhotoImage(Image.open(self.image_path))
       self.canvas.create_image(625, 350, image=self.canvas_image, anchor="center")
       # initialize and do something with id
       self.id = id
       self.exit = False
       self.after_id = None  # 新增:保存after任务ID
       self.after_method()

    def method1(self):
       self.id += 1
       # do something

    def method2(self, img):
       cv2.line(img, (self.id, self.id), (self.id, self.id), (0,0,255), 2)
       self.canvas_image = ImageTk.PhotoImage(Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)))
       self.canvas.create_image(625, 350, image=self.canvas_image, anchor="center")
       self.method1()
       # do something

    def after_method(self):
       if self.exit or self.id >= 100:
          # 新增:取消所有待执行的after任务
          if self.after_id:
              self.after_cancel(self.after_id)
          # 先退出主循环,再销毁窗口
          self.quit()
          self.destroy()
          return
       
       self.method2(self.org_image.copy())
       print(self.id)
       self.after_id = self.after(10, self.after_method)  # 保存after任务ID
       
    def method3(self):
       pass
       # do something


def run_instance(id):
    # 新增:重置CustomTkinter环境,确保新实例使用独立的Tcl解释器
    customtkinter.deactivate()
    customtkinter.activate()
    
    gui = GUI(id)
    # 修改窗口关闭回调:使用quit而非sys.exit,避免直接终止程序
    gui.protocol("WM_DELETE_WINDOW", lambda: gui.quit())
    gui.mainloop()
    
    # 确保窗口资源完全清理
    try:
        gui.destroy()
    except:
        pass
    print("Finished running instance....")

another.py

# necessary imports
from GUI_file import run_instance

# 示例ID列表
ids = [10, 20, 30]

for id in ids:
   run_instance(id)
   # process saved results from instance
   # pause(5)

错误信息

File "/home/user/project/gui/GUI_file.py", line 803, in update
    self.destroy()
  File "/home/user/.local/lib/python3.10/site-packages/customtkinter/windows/ctk_tk.py", line 85, in destroy
    tkinter.Tk.destroy(self)
  File "/usr/lib/python3.10/tkinter/__init__.py", line 2341, in destroy
    self.tk.call('destroy', self._w)
_tkinter.TclError: can't invoke "destroy" command: application has been destroyed
invalid command name "139702252461440update"
    while executing
"139702252461440update"
    ("after" script)
invalid command name "139702258075584<lambda>"
    while executing
"139702258075584<lambda>"
    ("after" script)
...
invalid command name "139702256197312<lambda>"
    while executing
"139702256197312<lambda>"
    ("after" script)
invalid command name "139702494947200check_dpi_scaling"
    while executing
"139702494947200check_dpi_scaling"
    ("after" script)

解决方案说明

  1. 问题根源:

    • 第一个实例调用destroy后,Tcl解释器被销毁,后续实例尝试使用已失效的解释器资源,触发错误。
    • after方法注册的任务在窗口销毁后仍可能被触发,导致调用已销毁对象的方法,出现"无效命令名"错误。
    • 使用sys.exit作为窗口关闭回调会直接终止整个程序,无法继续循环运行后续实例。
  2. 关键修改点:

    • 保存after任务的ID,在退出前调用self.after_cancel取消所有待执行的异步任务,避免窗口销毁后任务仍被触发。
    • 先调用self.quit()退出主循环,再执行self.destroy()销毁窗口,确保主循环正常结束后再清理资源。
    • 在run_instance中添加customtkinter.deactivate()和customtkinter.activate(),重置CustomTkinter环境,让每个新实例使用独立的Tcl解释器。
    • 将窗口关闭回调改为gui.quit(),避免直接终止程序,保证循环可以继续执行。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 11:30:25