_tkinter.TclError: invalid command name in Python

95 Views Asked by At

This is my debut on this site, so I will start short with introducing myself. I’m Thomas from Denmark, I hold a bachelor's degree in construction engineering and I’m trying to learn to program in Python in my spare time. So, it’s fair to say that I’m a bit of a dumbass when it comes to programming, so I hope you guys can help me.

I have attached two images of the program so it’s easier to imagine what’s going on.

My issue with the program is that when I set any one of the entry fields to an empty string, I get an TclError. But this only happens if it’s the second time I have created that frame “ForsideMenuFrame” or “LastarrangementMenuFrame”. So, I think it has something to do with when I destroy the frames. I do it to “clean up” after myself so the program performs better, or at least that is what I think. The message I get in the output is:

Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Users\Thoma\AppData\Local\Programs\Python\Python311\Lib\tkinter\__init__.py", line 1948, in __call__
    return self.func(*args)
           ^^^^^^^^^^^^^^^^
  File "C:\Users\Thoma\AppData\Local\Programs\Python\Python311\Lib\site-packages\customtkinter\windows\widgets\ctk_entry.py", line 121, in _textvariable_callback
    self._activate_placeholder()
  File "C:\Users\Thoma\AppData\Local\Programs\Python\Python311\Lib\site-packages\customtkinter\windows\widgets\ctk_entry.py", line 299, in _activate_placeholder
    if self._entry.get() == "" and self._placeholder_text is not None and (self._textvariable is None or self._textvariable == ""):
       ^^^^^^^^^^^^^^^^^
  File "C:\Users\Thoma\AppData\Local\Programs\Python\Python311\Lib\tkinter\__init__.py", line 3109, in get
    return self.tk.call(self._w, 'get')
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_tkinter.TclError: invalid command name ".!lastarrangementmenuframe.!ctkentry.!entry"

The program does not crash, it still functions the way I want it to, it just bugs me, and I can’t figure out how to prevent it.

Below you see a copy of my code, and the images should be attached in the buttom.

import customtkinter as ctk
import settings, forside, lastarrangement, tagudformning

class App(ctk.CTk):
    def __init__(self):
        # Setup
        super().__init__(fg_color = settings.BACKGROUND_COLOR)
        self.title('Snelaster')
        self.geometry('1400x850+50+3')
        self._set_appearance_mode('dark')
        self.iconbitmap('Snow_flake.ico')
        self.resizable(False, False)

        # Layout
        self.rowconfigure(0, weight = 1, uniform = 'a')
        self.rowconfigure(1, weight = 24, uniform = 'a')
        self.columnconfigure(0, weight = 1, uniform = 'a')
        self.columnconfigure(1, weight = 3, uniform = 'a')

        # Dictionary for frames
        self.frames = {
            'forside_menu': None,
            'forside_screen': None,
            'forside_header': None,
            'lastarrangement_menu': None,
            'lastarrangement_screen': None,
            'lastarrangement_header': None,
            'pulttag_menu': None,
            'pulttag_screen': None,
            'pulttag_header': None,
            'sadeltag_menu': None,
            'sadeltag_screen': None,
            'sadeltag_header': None}
        
        # Dictionary for StringVars
        self.string_vars = {
            'entry_sag': ctk.StringVar(),
            'entry_sagsnr': ctk.StringVar(),
            'entry_initialer': ctk.StringVar(),
            'entry_dato': ctk.StringVar()}
        
        # Dictionary for StringVars with tracing
        self.traced_string_vars01 = {
            'entry_C_t': ctk.StringVar(value = '1.0'),
            'combobox_C_top': ctk.StringVar(value = 'Normal'),
            'label_C_top': ctk.StringVar(value = 'C_top\t=\t1.00'),
            'entry_l_1': ctk.StringVar(value = ''),
            'entry_l_2': ctk.StringVar(value = ''),
            'entry_h': ctk.StringVar(value = ''),
            'label_C_s': ctk.StringVar(value = 'C_s\t=\t1.00'),
            'label_C_e': ctk.StringVar(value = 'C_e\t=\t1.00')}
        
        # Start tracing of StringVars
        self.start_trace_string_vars01()

        # Instance of ButtonFrame
        self.button_frame = ButtonFrame(self)
        self.forside_button_click()

        # Run
        self.mainloop()

    # Button commands
    def forside_button_click(self):
        # Change button colors
        self.button_frame.forside_button.configure(fg_color = settings.FORSIDEHEADER_FRAME_COLOR_DARK)
        self.button_frame.lastarrangement_button.configure(fg_color = settings.LASTARRANGEMENTHEADER_FRAME_COLOR)
        self.button_frame.formfaktor_button.configure(fg_color = settings.FORMFAKTORHEADER_FRAME_COLOR)

        # Destroying exsisting frames
        self.destroy_frames()

        # Creating Forside Menu Frame   
        self.frames['forside_menu'] = forside.ForsideMenuFrame(self,
                                                               self.string_vars['entry_sag'],
                                                               self.string_vars['entry_sagsnr'],
                                                               self.string_vars['entry_initialer'],
                                                               self.string_vars['entry_dato'])
        
        # Creating Forside Screen Frame
        self.frames['forside_screen'] = forside.ForsideScreenFrame(self)

        # Creating Forside Header Frame
        self.frames['forside_header'] = forside.ForsideHeaderFrame(self)

    def lastarrangement_button_click(self):
        # Change button colors
        self.button_frame.forside_button.configure(fg_color = settings.FORSIDEHEADER_FRAME_COLOR)
        self.button_frame.lastarrangement_button.configure(fg_color = settings.LASTARRANGEMENTHEADER_FRAME_COLOR_DARK)
        self.button_frame.formfaktor_button.configure(fg_color = settings.FORMFAKTORHEADER_FRAME_COLOR)

        # Destroying exsisting frames
        self.destroy_frames()

        # Creating Lastarrangement Menu Frame
        self.frames['lastarrangement_menu'] = lastarrangement.LastarrangementMenuFrame(self,
                                                                                       self.traced_string_vars01['entry_C_t'],
                                                                                       self.traced_string_vars01['combobox_C_top'],
                                                                                       self.traced_string_vars01['label_C_top'],
                                                                                       self.traced_string_vars01['entry_l_1'],
                                                                                       self.traced_string_vars01['entry_l_2'],
                                                                                       self.traced_string_vars01['entry_h'],
                                                                                       self.traced_string_vars01['label_C_s'],
                                                                                       self.traced_string_vars01['label_C_e'])

        # Creating Lastarrangement Screen Frame
        self.frames['lastarrangement_screen'] = lastarrangement.LastarrangementScreenFrame(self)

        # Creating Lastarrangement Header Frame
        self.frames['lastarrangement_header'] = lastarrangement.LastarrangementHeaderFrame(self)

    def formfaktor_button_click(self):
        # Change button colors
        self.button_frame.forside_button.configure(fg_color = settings.FORSIDEHEADER_FRAME_COLOR)
        self.button_frame.lastarrangement_button.configure(fg_color = settings.LASTARRANGEMENTHEADER_FRAME_COLOR)
        self.button_frame.formfaktor_button.configure(fg_color = settings.FORMFAKTORHEADER_FRAME_COLOR_DARK)

        # Destroying exsisting frames
        self.destroy_frames()

        # Creating Formfaktor Menu Frame
        self.frames['pulttag_menu'] = tagudformning.PulttagMenuFrame(self)

        # Creating Formfaktor Screen Frame
        self.frames['pulttag_screen'] = tagudformning.PulttagScreenFrame(self)

        # Creating Formfaktor Header Frame
        self.frames['pulttag_header'] = tagudformning.PulttagHeaderFrame(self)

    def destroy_frames(self):
        for key, value in self.frames.items():
            if value is not None:
                # Destroying the frame
                self.frames[key].destroy()
                self.frames[key] = None

    def start_trace_string_vars01(self):
        for value in self.traced_string_vars01.values():
            value.trace('w', self.trace_string_vars01)

    def trace_string_vars01(self, *args):
        # entry_C_t
        C_t = self.traced_string_vars01['entry_C_t'].get()
        if len(C_t) == 2 and C_t[0] == '0' and C_t[1] != '.' or len(C_t) > 0 and C_t[0] == '.':
            self.traced_string_vars01['entry_C_t'].set('0')
        try:
            C_t = float(C_t)
            if C_t > 1:
                self.traced_string_vars01['entry_C_t'].set('1.0')
        except ValueError:
            if C_t == '':
                pass
            else:
                self.traced_string_vars01['entry_C_t'].set('')

        # label_C_top
        topografi = self.traced_string_vars01['combobox_C_top'].get()
        C_top = 1.00
        if topografi == 'Afskærmet':
            C_top = 1.25
        elif topografi == 'Normal':
            C_top = 1.00
        elif topografi == 'Vindblæst':
            C_top = 0.80
        self.traced_string_vars01['label_C_top'].set(f'C_top\t=\t{C_top:.2f}')

        # entry l_1
        l_1 = self.traced_string_vars01['entry_l_1'].get()
        if len(l_1) == 2 and l_1[0] == '0' and l_1[1] != '.' or len(l_1) > 0 and l_1[0] == '.':
            self.traced_string_vars01['entry_l_1'].set('')
        try:
            l_1 = float(l_1)
        except ValueError:
            if l_1 == '':
                pass
            else:
                self.traced_string_vars01['entry_l_1'].set('')

        # entry l_2
        l_2 = self.traced_string_vars01['entry_l_2'].get()
        if len(l_2) == 2 and l_2[0] == '0' and l_2[1] != '.' or len(l_2) > 0 and l_2[0] == '.':
            self.traced_string_vars01['entry_l_2'].set('')
        try:
            l_2 = float(l_2)
        except ValueError:
            if l_2 == '':
                pass
            else:
                self.traced_string_vars01['entry_l_2'].set('')

        # entry h
        h = self.traced_string_vars01['entry_h'].get()
        if len(h) == 2 and h[0] == '0' and h[1] != '.' or len(h) > 0 and h[0] == '.':
            self.traced_string_vars01['entry_h'].set('')
        try:
            h = float(h)
        except ValueError:
            if h == '':
                pass
            else:
                self.traced_string_vars01['entry_h'].set('')

        # label C_s
        try:
            C_s = 1.00
            if topografi == 'Afskærmet':
                C_s = 1.00
            elif 2 * h > l_1:
                C_s = 1.00
            else:
                if l_2 <= 10 * h:
                    C_s = 1.00
                elif l_2 >= 20 * h:
                    C_s = 1.25
                else:
                    C_s = 1 + 0.025 * ((l_2 - 10 * h) / h)
            self.traced_string_vars01['label_C_s'].set(f'C_s\t=\t{C_s:.2f}')
        except TypeError:
            pass

        # label C_e
        C_e = C_top * C_s
        self.traced_string_vars01['label_C_e'].set(f'C_e\t=\t{C_e:.2f}')
    
class ButtonFrame(ctk.CTkFrame):
    def __init__(self, parent):
        # Setup
        super().__init__(master = parent, 
                         fg_color = settings.BUTTON_FRAME_COLOR)
        self.grid(row = 0, column = 0, sticky = 'nsew')

        # Create instance of parent (App)
        self.parent = parent

        # Layout
        self.rowconfigure(0, weight = 1, uniform = 'a')
        self.columnconfigure((0,1,2), weight = 1, uniform = 'a')

        # Button font
        font = ctk.CTkFont(family = settings.FONT_FAMILY,
                           size = settings.BUTTON_FONT_SIZE,
                           weight = 'bold')

        # Button widgets
        self.forside_button = ctk.CTkButton(self, 
                                       text = 'Forside', 
                                       font = font,
                                       text_color = 'white',
                                       fg_color = settings.FORSIDEHEADER_FRAME_COLOR,
                                       hover_color = settings.FORSIDEHEADER_FRAME_COLOR_DARK,
                                       corner_radius = settings.BUTTON_CORNER_RADIUS,
                                       command = self.parent.forside_button_click)
        self.forside_button.grid(row = 0, column = 0, 
                                 sticky = 'nsew')

        self.lastarrangement_button = ctk.CTkButton(self, 
                                               text = 'Lastarrangement', 
                                               font = font,
                                               text_color = 'white',
                                               fg_color = settings.LASTARRANGEMENTHEADER_FRAME_COLOR,
                                               hover_color = settings.LASTARRANGEMENTHEADER_FRAME_COLOR_DARK,
                                               corner_radius = settings.BUTTON_CORNER_RADIUS,
                                               command = self.parent.lastarrangement_button_click)
        self.lastarrangement_button.grid(row = 0, column = 1, 
                                         sticky = 'nsew')

        self.formfaktor_button = ctk.CTkButton(self, 
                                          text = 'Formfaktor', 
                                          font = font,
                                          text_color = 'white',
                                          fg_color = settings.FORMFAKTORHEADER_FRAME_COLOR,
                                          hover_color = settings.FORMFAKTORHEADER_FRAME_COLOR_DARK,
                                          corner_radius = settings.BUTTON_CORNER_RADIUS,
                                          command = self.parent.formfaktor_button_click)
        self.formfaktor_button.grid(row = 0, column = 2, 
                                    sticky = 'nsew')

if __name__ == '__main__':
    App()

Forside Lastarrangement

This is what I have tried, and it did not work. In the start of the program, I imported _tkinter.

import customtkinter as ctk
import settings, forside, lastarrangement, tagudformning
import _tkinter as _tk

And then in the trace_string_vars01 method of the App class I tried to except a TclError and then ignored it.

 # entry_C_t
        try:
            C_t = self.traced_string_vars01['entry_C_t'].get()
            if len(C_t) == 2 and C_t[0] == '0' and C_t[1] != '.' or len(C_t) > 0 and C_t[0] == '.':
                self.traced_string_vars01['entry_C_t'].set('0')
            try:
                C_t = float(C_t)
                if C_t > 1:
                    self.traced_string_vars01['entry_C_t'].set('1.0')
            except ValueError:
                if C_t == '':
                    pass
                else:
                    self.traced_string_vars01['entry_C_t'].set('')
        except _tk.TclError:
            pass
1

There are 1 best solutions below

5
Thingamabobs On

The error suggests it comes within the costumtkinter package and the source code give you multiple options where the error might happen.

Setting the variable to an empty string, by configure placeholder, FocusOut, delete method or by instantiating a Entry.

Further, your error refering to this Entry:
".!lastarrangementmenuframe.!ctkentry.!entry"
Even though you included a lot of code, it's not enough in this case, since lastarrangement.LastarrangementMenuFrame is defined in an imported module.

The error:

_tkinter.TclError: invalid command name ".!lastarrangementmenuframe.!ctkentry.!entry"

means that you/the StringVar or an event refering to an already dead widget.