pythontkintercustomtkinter

Maximum recursion depth exceeded while drawing windows in tkinter


I am trying to make an app with Tkinter and CustomTkinter and sometimes it fails saying that ir has reached the maximum recursion depth. The error usually happens when rescaling a view and less often when openning new windows. The following example fails when rescaling:

import tkinter as tk
from hdpitkinter import HdpiTk
import customtkinter as ctk
import ctypes

def rescale(event, root, frames, scrolls):
    scale_factor = ctypes.windll.shcore.GetScaleFactorForDevice(0)/100
    print(scale_factor)
    width = root.winfo_width()//scale_factor
    height = root.winfo_height()//scale_factor
    print("Width = ", width, "Height = ", height, "type", type(width))
    for i in range(3):
        frames[i].configure(width = width-10,height=(height//3)-30)
        scrolls[i].configure(width = (width/2)-40,height=(height//3)-50)
        scrolls[i+3].configure(width = (width/2)-40,height=(height//3)-50)
    # Updates window
    root.update_idletasks()

if __name__ == "__main__":
    root = ctk.CTk()
    root.geometry("1280x720")
    root.minsize(width=1280,height=720)
    root.title("Test")
    root.configure(fg_color = "#4B4B4B")
    height = 720
    width = 1280

    frames, scrolls, labels = [], [], []

    leftFont = ctk.CTkFont(family="Inter", size=20, weight="normal")
    rightFont = ctk.CTkFont(family="Inter", size=15, weight="normal")

    # Base frames creation
    for i in range(3):
        frames.append(ctk.CTkFrame(master=root,fg_color="#504F4F", corner_radius=10, width = width-10, height=height/3-46))
    # ScrollFrames creation
    for i in range(6):
        scrolls.append(ctk.CTkScrollableFrame(master=frames[i%3],fg_color="#504F4F", corner_radius= 10,width = ((width/2)-40)))
    # Buttons creation
    button1 = ctk.CTkButton(root, text="button1", font=leftFont, corner_radius=10,
                                fg_color="#1E1E1E", text_color="white", height=40)
    button2 = ctk.CTkButton(root, text="button2", font=leftFont, corner_radius=10,
                                fg_color="#1E1E1E", text_color="white", height=40)
    button3 = ctk.CTkButton(root, text="button3", font=leftFont, corner_radius=10,
                                fg_color="#1E1E1E", text_color="white", height=40)
    # Placing of buttons
    button1.grid(column = 0, row = 3,pady = 5, padx = 10, sticky="w")
    button2.grid(column = 1, row = 3,pady = 5, padx = 10, sticky="w")
    button3.grid(column = 1, row = 3,pady = 5, sticky="e")
    # Frames placing
    for i in range(3):
        frames[i].grid(column= 0, row = i, columnspan=2, padx=3, pady=3,)
    # Scrolls and labels placing
    for i in range(3):
        scrolls[i].grid(row = 0, column=0, padx = 5)
        scrolls[i+3].grid(row = 0, column=1, padx = 5)
    # Bind rescale method
    root.bind("<Configure>", lambda event, root = root, frames = frames, scrolls = scrolls: rescale(event, root, frames, scrolls))
    root.mainloop()

The complete error is this:

Traceback (most recent call last):
  File "d:\lib\tkinter\__init__.py", line 1921, in __call__
    return self.func(*args)
  File "D:\test.py", line 58, in <lambda>
    root.bind("<Configure>", lambda event, root = root, frames = frames, scrolls = scrolls: rescale(event, root, frames, scrolls))
  File "D:\test.py", line 13, in rescale
    frames[i].configure(width = width-10,height=(height//3)-30)
  File "d:\lib\site-packages\customtkinter\windows\widgets\ctk_frame.py", line 167, in configure
    super().configure(require_redraw=require_redraw, **kwargs)
  File "d:\lib\site-packages\customtkinter\windows\widgets\core_widget_classes\ctk_base_class.py", line 117, in configure
    self._set_dimensions(width=kwargs.pop("width"))
  File "d:\lib\site-packages\customtkinter\windows\widgets\ctk_frame.py", line 88, in _set_dimensions
    super()._set_dimensions(width, height)
  File "d:\lib\site-packages\customtkinter\windows\widgets\core_widget_classes\ctk_base_class.py", line 240, in _set_dimensions
    super().configure(width=self._apply_widget_scaling(self._desired_width),
RecursionError: maximum recursion depth exceeded while calling a Python object

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "D:\test.py", line 59, in <module>
    root.mainloop()
  File "d:\lib\site-packages\customtkinter\windows\ctk_tk.py", line 165, in mainloop
    super().mainloop(*args, **kwargs)
  File "d:\lib\tkinter\__init__.py", line 1458, in mainloop
    self.tk.mainloop(n)
  File "d:\lib\tkinter\__init__.py", line 1925, in __call__
    self.widget._report_exception()
  File "d:\lib\tkinter\__init__.py", line 1641, in _report_exception
    root.report_callback_exception(exc, val, tb)
  File "d:\lib\tkinter\__init__.py", line 2379, in report_callback_exception
    traceback.print_exception(exc, val, tb)
  File "d:\lib\traceback.py", line 119, in print_exception
    te = TracebackException(type(value), value, tb, limit=limit, compact=True)
  File "d:\lib\traceback.py", line 502, in __init__
    self.stack = StackSummary.extract(
  File "\lib\traceback.py", line 353, in extract
    limit = getattr(sys, 'tracebacklimit', None)
RecursionError: maximum recursion depth exceeded while calling a Python object

I found out that changing frames[i].configure(width = width-10,height=(height//3)-35) with frames[i].configure(width = width-10,height=(height//3)-30) does work, it takes a long time to update but works. Also the problem is not just with rescaling as such because in the project I have a main window that opens other windows and it also trhows the error when creating some of them.

Another thing I found is that if i create a new conda enviroment does solve the problem, it still takes a long time to load the window but it does it.

So my question, is there a way to make sure that this wont happen if I install it elsewhere or if I compile it into an exe?


Solution

  • As told in the comments above every time a widget is updated it generates an event, and since inside that event we update widgets this rescaling method is called infinitely.

    My solution has been to do the rescaling only if more than half a second has passed since last time it executed:

    def rescale(event, root, frames, scrolls):
        global updated
        if(time.time()- updated > 0.5 ):
            scale_factor = ctypes.windll.shcore.GetScaleFactorForDevice(0)/100
            print(scale_factor)
            width = root.winfo_width()//scale_factor
            height = root.winfo_height()//scale_factor
            print("Width = ", width, "Height = ", height, "type", type(width))
            for i in range(3):
                frames[i].configure(width = width-10,height=(height//3)-80)
                scrolls[i].configure(width = (width/2)-40,height=(height//3)-45)
                scrolls[i+3].configure(width = (width/2)-40,height=(height//3)-45)
            # Updates window
            #root.update_idletasks()
            updated = time.time()
    

    This works and is a lot faster than when the other attempt worked.