TomSchimansky / CustomTkinter

A modern and customizable python UI-library based on Tkinter
MIT License
11.33k stars 1.07k forks source link

Error message after tkinter window is destroyed #2283

Closed LJDrakeley closed 6 months ago

LJDrakeley commented 7 months ago

When closing my custom tkinter window, I receive the following error message:

invalid command name "2462417235584update" while executing "2462417235584update" ("after" script) invalid command name "2462162051648check_dpi_scaling" while executing "2462162051648check_dpi_scaling" ("after" script)

I do not use any custom methods that check for dpi scaling, "update" or any "after" methods. Is this an issue with the package? Thank you in advance.

Anonymous6598 commented 7 months ago

Show full code, please

LJDrakeley commented 7 months ago

Hi Anonymous6598, unfortunately I cannot show the code (and the size of the project is more than 2500 lines long anyway). However, that is the full extent of the error message that occurs when closing the window. I do not have any custom closing method if that helps. If this cannot be resolved without the code it's okay but I'm curious for a solution nonetheless. Thanks!

JanPanthera commented 7 months ago

Without code its almost impossible to say what's wrong, anyway, the only thing i recommend is, to make sure that your window desrutruction is don properly. Means, if you destroy the window and afterwards try to access something from customtkinter it can lead to an error like this. Hope you find a solution 😸

Anonymous6598 commented 7 months ago

Thanks @JanPanthera for support. Yes, without code it's impossible to understand issue.

VillainousSsnake commented 6 months ago

I have this exact issue and I also need help with this, here is an example:

import customtkinter as ctk

root = ctk.CTk()

root.mainloop()

root = ctk.CTk()

root.mainloop()

In the example above, to get the error you have to destroy the window. Then it will try to create a new root and it fails.

The event occurs when the user destroys the window and creates a new root window in any way, (eg. it happens when root.destroy() is called before the last root definition through a button or something.)

JanPanthera commented 6 months ago

Have you tried to delay the second creation, maybe the internal code is still cleaning up while you are trying to create a new one?

VillainousSsnake commented 6 months ago

Have you tried to delay the second creation, maybe the internal code is still cleaning up while you are trying to create a new one?

Okay, here is the new code then:

import customtkinter as ctk
from time import sleep

root = ctk.CTk()

root.mainloop()

sleep(5)

root = ctk.CTk()

root.mainloop()

It still raises this error:

invalid command name "2212570062528update"
    while executing
"2212570062528update"
    ("after" script)
JanPanthera commented 6 months ago

yeah thats odd, sorry i don't know, this is something for the actual dev then

Anonymous6598 commented 6 months ago

here is solution to your problem:

import customtkinter as ctk
from time import sleep

root_1 = ctk.CTk()

root_1.protocol("WM_DELETE_WINDOW", root_1.destroy())

root_1.mainloop()

sleep(5)

root_2 = ctk.CTk()

root_2.protocol("WM_DELETE_WINDOW", root_2.destroy())

root_2.mainloop()
VillainousSsnake commented 6 months ago
```python
import customtkinter as ctk
from time import sleep

root_1 = ctk.CTk()

root_1.protocol("WM_DELETE_WINDOW", root_1.destroy())

root_1.mainloop()

sleep(5)

root_2 = ctk.CTk()

root_2.protocol("WM_DELETE_WINDOW", root_2.destroy())

root_2.mainloop()

That doesn't work, this is the error it produces now:

Traceback (most recent call last):
  File "C:\Users\username\PycharmProjects\project\TEST.py", line 6, in <module>
    root_1.protocol("WM_DELETE_WINDOW", root_1.destroy())
  File "C:\Users\username\AppData\Local\Programs\Python\Python311\Lib\tkinter\__init__.py", line 2253, in wm_protocol
    return self.tk.call(
           ^^^^^^^^^^^^^
_tkinter.TclError: can't invoke "wm" command: application has been destroyed
FaheemM1020 commented 6 months ago
```python
import customtkinter as ctk
from time import sleep

root_1 = ctk.CTk()

root_1.protocol("WM_DELETE_WINDOW", root_1.destroy())

root_1.mainloop()

sleep(5)

root_2 = ctk.CTk()

root_2.protocol("WM_DELETE_WINDOW", root_2.destroy())

root_2.mainloop()

That doesn't work, this is the error it produces now:

Traceback (most recent call last):
  File "C:\Users\username\PycharmProjects\project\TEST.py", line 6, in <module>
    root_1.protocol("WM_DELETE_WINDOW", root_1.destroy())
  File "C:\Users\username\AppData\Local\Programs\Python\Python311\Lib\tkinter\__init__.py", line 2253, in wm_protocol
    return self.tk.call(
           ^^^^^^^^^^^^^
_tkinter.TclError: can't invoke "wm" command: application has been destroyed

Try this :

import customtkinter as ctk
from time import sleep

root_1 = ctk.CTk()

root_1.protocol("WM_DELETE_WINDOW", root_1.destroy)

root_1.mainloop()

sleep(5)

root_2 = ctk.CTk()

root_2.protocol("WM_DELETE_WINDOW", root_2.destroy)

root_2.mainloop()
FaheemM1020 commented 6 months ago

Try catching it with try and except

Anonymous6598 commented 6 months ago
```python
import customtkinter as ctk
from time import sleep

root_1 = ctk.CTk()

root_1.protocol("WM_DELETE_WINDOW", root_1.destroy())

root_1.mainloop()

sleep(5)

root_2 = ctk.CTk()

root_2.protocol("WM_DELETE_WINDOW", root_2.destroy())

root_2.mainloop()

That doesn't work, this is the error it produces now:

Traceback (most recent call last):
  File "C:\Users\username\PycharmProjects\project\TEST.py", line 6, in <module>
    root_1.protocol("WM_DELETE_WINDOW", root_1.destroy())
  File "C:\Users\username\AppData\Local\Programs\Python\Python311\Lib\tkinter\__init__.py", line 2253, in wm_protocol
    return self.tk.call(
           ^^^^^^^^^^^^^
_tkinter.TclError: can't invoke "wm" command: application has been destroyed

Try this :

import customtkinter as ctk
from time import sleep

root_1 = ctk.CTk()

root_1.protocol("WM_DELETE_WINDOW", root_1.destroy)

root_1.mainloop()

sleep(5)

root_2 = ctk.CTk()

root_2.protocol("WM_DELETE_WINDOW", root_2.destroy)

root_2.mainloop()

You made mistake. destroy() is missing. Therefore function won't work.

Anonymous6598 commented 6 months ago

I forgot to put lambda here is solution:

import customtkinter as ctk
from time import sleep

root_1 = ctk.CTk()

root_1.protocol("WM_DELETE_WINDOW", lambda: root_1.destroy())

root_1.mainloop()

root_2 = ctk.CTk()

root_2.protocol("WM_DELETE_WINDOW", lambda: root_2.destroy())

root_2.mainloop()
VillainousSsnake commented 6 months ago

I forgot to put lambda here is solution:

import customtkinter as ctk
from time import sleep

root_1 = ctk.CTk()

root_1.protocol("WM_DELETE_WINDOW", lambda: root_1.destroy())

root_1.mainloop()

root_2 = ctk.CTk()

root_2.protocol("WM_DELETE_WINDOW", lambda: root_2.destroy())

root_2.mainloop()

That still produces this error:

invalid command name "2742723926720update"
    while executing
"2742723926720update"
    ("after" script)
VillainousSsnake commented 6 months ago

The numbers in the error change every time the program is run for some reason. (Maybe the CTk class has codes for each function??)

And because this is a simple example, the error is not long like OP showed.

Anonymous6598 commented 6 months ago

final solution:

import customtkinter as ctk, sys
from time import sleep

def exit():
    root_1.withdraw()

    root_2 = ctk.CTk()

    root_2.protocol("WM_DELETE_WINDOW", lambda: sys.exit())

    root_2.mainloop()

root_1 = ctk.CTk()

root_1.protocol("WM_DELETE_WINDOW", lambda: exit())

root_1.mainloop()
LJDrakeley commented 6 months ago

Without code its almost impossible to say what's wrong, anyway, the only thing i recommend is, to make sure that your window desrutruction is don properly. Means, if you destroy the window and afterwards try to access something from customtkinter it can lead to an error like this. Hope you find a solution 😸

Understandable! I apologize I cannot give the code, I was hoping it was a more widespread issue that others have commonly seen. Thank you all for your time and efforts. I hope VillainousSsnake finds a solution to their similar issue. It is odd that the window delete protocol must be explicitly stated for correct closing?

VillainousSsnake commented 6 months ago

final solution:

import customtkinter as ctk, sys
from time import sleep

def exit():
    root_1.withdraw()

    root_2 = ctk.CTk()

    root_2.protocol("WM_DELETE_WINDOW", lambda: sys.exit())

    root_2.mainloop()

root_1 = ctk.CTk()

root_1.protocol("WM_DELETE_WINDOW", lambda: exit())

root_1.mainloop()

That works! Thanks for helping me, I was about to put my entire app on hold because of this bug but now I can continue development!

Anonymous6598 commented 6 months ago

Your welcome dude

Anonymous6598 commented 6 months ago

Without code its almost impossible to say what's wrong, anyway, the only thing i recommend is, to make sure that your window desrutruction is don properly. Means, if you destroy the window and afterwards try to access something from customtkinter it can lead to an error like this. Hope you find a solution 😸

Understandable! I apologize I cannot give the code, I was hoping it was a more widespread issue that others have commonly seen. Thank you all for your time and efforts. I hope VillainousSsnake finds a solution to their similar issue. It is odd that the window delete protocol must be explicitly stated for correct closing?

Yes, it is needed.

PhilipNelson5 commented 6 months ago

@LJDrakeley we don't need your entire source code, that would actually be worse. What we need is a Minimal, Reproducible Example (MRE). If you can take your application, strip it down to the minimal set of statements that reproduce the bug, then people can use that to diagnose the issue. Without an MRE, any number of things could be causing it.

LJDrakeley commented 6 months ago

@PhilipNelson5 I have finally been able to isolate the problem. I just started a new project and I noticed that by creating a method that destroys some window, and then creates another, causes the error.

Here is an example:


import customtkinter as ctk

class WindowClass(ctk.CTk):
    def __init__(self, parent):
        super().__init__()

        self.parent = parent

        button = ctk.CTkButton(master=self, 
                               text='destroy', 
                               command=lambda: self.parent.open_new_window(self))
        button.pack()

class MainClass:
    def __init__(self):
        window = WindowClass(parent=self)
        window.mainloop()

    def open_new_window(self, some_window):
        some_window.destroy()

        new_window = WindowClass(parent=self)
        new_window.mainloop()

if __name__ == "__main__":
    app = MainClass()

Thanks in advance!

PhilipNelson5 commented 6 months ago

There is a print statement in \/Lib/tkinter/__init__.py:686 which is part of the destroy sequence. Uncommenting it provides some insight. The window is trying to execute commands that have been deleted.

- Tkinter: deleted command 2489637091072_on_enter
- Tkinter: deleted command 2489637091328_on_leave
- Tkinter: deleted command 2489637091584_clicked
- Tkinter: deleted command 2489615068352_on_enter
- Tkinter: deleted command 2489615068800_on_leave
- Tkinter: deleted command 2489615069120_clicked
- Tkinter: deleted command 2489615069632_clicked
- Tkinter: deleted command 2489637089344_update_dimensions_event
- Tkinter: deleted command 2489615088832_click_animation
- Tkinter: deleted command tkerror
- Tkinter: deleted command exit
- Tkinter: deleted command 2489614969856destroy
- Tkinter: deleted command 2489637086144_update_dimensions_event
- Tkinter: deleted command 2489637086400_focus_in_event
- Tkinter: deleted command 2489615069952check_dpi_scaling
- Tkinter: deleted command 2489615057856update
invalid command name "2489615057856update"
    while executing
"2489615057856update"
    ("after" script)
invalid command name "2489615069952check_dpi_scaling"
    while executing
"2489615069952check_dpi_scaling"
    ("after" script)
invalid command name "2489615088832_click_animation"
    while executing
"2489615088832_click_animation"
    ("after" script)
PhilipNelson5 commented 6 months ago

I think the problem is creating multiple Tk instances. From what I've read, it can be done, however it is tricky to manage. A simpler path is to create one Tk instance and use multiple TopWindows. Here is a working example:

from typing import Tuple

import customtkinter as CTk

class App(CTk.CTk):
    def __init__(self, fg_color: str | Tuple[str, str] | None = None, **kwargs):
        super().__init__(fg_color, **kwargs)
        self.withdraw()
        self.window = Window(master=self)

    def new_window(self):
        self.window.destroy()
        self.window = Window(master=self)

class Window(CTk.CTkToplevel):
    def __init__(self, master):
        super().__init__(master=master)
        self.geometry("200x80")

        button = CTk.CTkButton(
            master=self,
            text="destroy",
            command=lambda: self.master.new_window(),
        )
        button.pack(padx=25, pady=25)

        self.protocol("WM_DELETE_WINDOW", self.master.destroy)

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

The application immediately hides the man window which owns the eventloop. Then it creates a new TopWindow. self.protocol("WM_DELETE_WINDOW", self.master.destroy) destroys the main window when you press the X on one of the TopLevel windows to stop the eventloop.

LJDrakeley commented 6 months ago

Great solution!

Thank you, I will be implementing this method from now on.

PhilipNelson5 commented 6 months ago

Awesome, if that resolves your problem, you can close this issue.