Search code examples
pythontkintercustomtkinter

Why do I get a "RecursionError: maximum recursion depth exceeded" while sliding CTkSlider in customtkinter library?


I'm trying to pass the CTkSlider()'s real-time value from the top level to the main window via the seperate class Variables. I couldn't figure out what causes this issue. I'm open to any suggestion. What is wrong with this code, do you guys think? Thank you.

import customtkinter

class ToplevelWindow(customtkinter.CTkToplevel):
    
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        
        self.variables = Variables()

        self.slider = customtkinter.CTkSlider(self, height=29, from_=0, to=100, number_of_steps=10, progress_color="#19A7CE", button_color="#FB2576", command=self.get_value)
        self.slider.grid()

    def get_value(self, value):
        self.variables.variables_variable = value
        self.variables.update_value()

class App(customtkinter.CTk):

    def __init__(self):
        super().__init__()

        self.toplevel_window : customtkinter.CTkToplevel = None

        self.app_variable : int = 9
        self.app_variable_1 : tuple = None
        self.app_variable_2 : float = 1.5

        self.app_condition : float = 0.5

        self.button = customtkinter.CTkButton(master=self, command=self.open_toplevel)
        self.button.grid()

    def open_toplevel(self):
        if self.toplevel_window is None or not self.toplevel_window.winfo_exists():
            self.toplevel_window = ToplevelWindow(self)
        else:
            self.toplevel_window.focus()

    def value_setter(self, size=50):
        if self.app_condition < 1:
            self.app_variable_1 = (size, size * self.app_variable_2)
        else:
            self.app_variable_1 = (size * self.app_variable_2, size)

    def update_app_variable(self, size=50):
        self.value_setter(size)


class Variables(App):

    def __init__(self, *args, **kwargs):
        self.variables_variable : int

    def update_value(self):
        super().update_app_variable(self.variables_variable)


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

Traceback:

Exception in Tkinter callback
Traceback (most recent call last):
  File "D:\Python311\Lib\tkinter\__init__.py", line 1948, in __call__
    return self.func(*args)
           ^^^^^^^^^^^^^^^^
  File "D:\Python311\Lib\site-packages\customtkinter\windows\widgets\ctk_slider.py", line 324, in _clicked
    self._command(self._output_value)
  File "main.py", line 15, in get_value
    self.variables.update_value()
  File "main.py", line 55, in update_value
    super().update_app_variable(self.variables_variable)
  File "main.py", line 46, in update_app_variable
    self.value_setter(size)
  File "main.py", line 40, in value_setter
    if self.app_condition < 1:
       ^^^^^^^^^^^^^^^^^^
  File "D:\Python311\Lib\tkinter\__init__.py", line 2410, in __getattr__
    return getattr(self.tk, attr)
                   ^^^^^^^
  File "D:\Python311\Lib\tkinter\__init__.py", line 2410, in __getattr__
    return getattr(self.tk, attr)
                   ^^^^^^^
  File "D:\Python311\Lib\tkinter\__init__.py", line 2410, in __getattr__
    return getattr(self.tk, attr)
                   ^^^^^^^
  [Previous line repeated 987 more times]
RecursionError: maximum recursion depth exceeded

Solution

  • This error occurs because the Variables class inherits from the App class. The update_app_variable calls value_setter which calls update_app_variable creating an infinite loop.

    The following modifications to the code should resolve the issue by updating app_variable_1 without the value_setter method, instead using the self.variables_variable value when calling update_idletasks. This allows the GUI to update immediately.

    import customtkinter
    
    class ToplevelWindow(customtkinter.CTkToplevel):
        
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            
            self.variables = Variables()
    
            self.slider = customtkinter.CTkSlider(self, height=29, from_=0, to=100, number_of_steps=10, progress_color="#19A7CE", button_color="#FB2576", command=self.get_value)
            self.slider.grid()
    
        def get_value(self, value):
            self.variables.variables_variable = value
            self.variables.update_value(self.master)
    
    class App(customtkinter.CTk):
    
        def __init__(self):
            super().__init__()
    
            self.toplevel_window : customtkinter.CTkToplevel = None
    
            self.app_variable : int = 9
            self.app_variable_1 : tuple = None
            self.app_variable_2 : float = 1.5
    
            self.app_condition : float = 0.5
    
            self.button = customtkinter.CTkButton(master=self, command=self.open_toplevel)
            self.button.grid()
    
        def open_toplevel(self):
            if self.toplevel_window is None or not self.toplevel_window.winfo_exists():
                self.toplevel_window = ToplevelWindow(self)
            else:
                self.toplevel_window.focus()
    
        def value_setter(self, size=50):
            if self.app_condition < 1:
                self.app_variable_1 = (size, size * self.app_variable_2)
            else:
                self.app_variable_1 = (size * self.app_variable_2, size)
    
        def update_app_variable(self, size=50):
            self.value_setter(size)
    
    
    class Variables:
    
        def __init__(self):
            self.variables_variable : int
    
        def update_value(self, app_instance):
            app_instance.app_variable_1 = (self.variables_variable, self.variables_variable)
            app_instance.update_idletasks()
    
    
    if __name__ == "__main__":
        app = App()
        app.mainloop()