CustomTkinter icon indicating copy to clipboard operation
CustomTkinter copied to clipboard

Progress Bar Freezing When Moving the Window During Update

Open DimaTepliakov opened this issue 1 year ago • 1 comments

Dear All,

Issue: The progress bar freezes when the user tries to move the window while the progress is being updated. Although the progress continues in the background (e.g., printed logs show updates), the UI becomes unresponsive. I'm looking for suggestions on how to allow the user to move the window without freezing the progress bar or the overall system.

Steps to Reproduce:

  1. Run the provided code.
  2. Start the progress by clicking "Start Progress."
  3. Attempt to move the window while the progress bar is updating.

Expected Behavior: The window should be movable without causing the progress bar to freeze or the UI to become unresponsive.

Actual Behavior: The progress bar freezes when the window is moved, even though progress updates continue in the terminal.

Code Example:

import time
import customtkinter as ctk

class App(ctk.CTk):
    def __init__(self):

        super().__init__()

        # configure window
        self.title("Freezing Progressbar")

        # create slider and progressbar frame
        self.slider_progressbar_frame = ctk.CTkFrame(self, fg_color="transparent")
        self.slider_progressbar_frame.grid(row=0, column=0, padx=(20, 0), pady=(20, 0), sticky="nsew")

        self.sidebar_button_1 = ctk.CTkButton(self.slider_progressbar_frame, text='Start Progress',  command=self.sidebar_button_event)
        self.sidebar_button_1.grid(row=0, column=0, padx=20, pady=10)
        
        self.progressbar_1 = ctk.CTkProgressBar(self.slider_progressbar_frame, height=15)
        self.progressbar_1.grid(row=1, column=0, padx=(20, 10), pady=(10, 10), sticky="ew")
        self.progressbar_1.set(0)

    def sidebar_button_event(self):
        self.test()
        print("sidebar_button click")

    def test(self):
        duration = 10
        iter_step = 1/duration
        progress_step = iter_step

        self.progressbar_1.start()
        for i in range(duration):
            print(progress_step)
            self.progressbar_1.set(progress_step)
            time.sleep(1)
            progress_step += iter_step
            self.update_idletasks()
        self.progressbar_1.stop()


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

Preview: https://github.com/user-attachments/assets/5b733b23-aa6d-43c5-b4aa-5a595f14514a

Environment: OS: Window 11 Python Version: 3.10.4 CustomTkinter Version: 5.2.2 Any ideas or recommendations would be appreciated!

DimaTepliakov avatar Sep 08 '24 07:09 DimaTepliakov

Since CustomTkinter does not inherently support threading within its callbacks, any long-running tasks will cause the entire interface—including window movements and other functionalities—to freeze or become temporarily unresponsive until the task completes. This is because such tasks block the GUI event loop. To handle operations that take longer than what the GUI loop can manage smoothly, it’s essential to offload them to a separate thread manually.

Also, in your provided sample code, there's a minor bug: when setting the progress bar's value incrementally, it's unnecessary to call start method on the progressbar. The corrected version of the code is given below:

import time
import customtkinter as ctk
from threading import Thread

class App(ctk.CTk):
    def __init__(self):

        super().__init__()

        # configure window
        self.title("Freezing Progressbar")

        # create slider and progressbar frame
        self.slider_progressbar_frame = ctk.CTkFrame(self, fg_color="transparent")
        self.slider_progressbar_frame.grid(row=0, column=0, padx=(20, 0), pady=(20, 0), sticky="nsew")

        self.sidebar_button_1 = ctk.CTkButton(self.slider_progressbar_frame, text='Start Progress',  command=self.sidebar_button_event)
        self.sidebar_button_1.grid(row=0, column=0, padx=20, pady=10)
        
        self.progressbar_1 = ctk.CTkProgressBar(self.slider_progressbar_frame, height=15)
        self.progressbar_1.grid(row=1, column=0, padx=(20, 10), pady=(10, 10), sticky="ew")
        self.progressbar_1.set(0)

    def sidebar_button_event(self):
        Thread(target=self.test, daemon=True).start()
        print("sidebar_button click")
        

    def test(self):
        duration = 10
        iter_step = 1/duration
        progress_step = iter_step

        for i in range(duration):
            print(progress_step)
            self.progressbar_1.set(progress_step)
            time.sleep(1)
            progress_step += iter_step
            self.update_idletasks()
                        
        self.progressbar_1.stop()


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

Output: pb_threading_in_ctk

Additionally, you could use .after() method for better handling such tasks in Tkinter or CustomTkinter. Hope the above information would be helpful to you.

Regards

dipeshSam avatar Sep 08 '24 09:09 dipeshSam