Question

I'm trying to get a progress bar to appear on a Toplevel widget, then incrementally increase every few seconds until complete.

When I click "Start", there is delay of a few seconds before the progressbar widget appears. When it does appear, the progress bar does not increment at all.

Here is what I've tried so far:

class MainUI:
    def __init__(self, parent):
        self.parent = parent
        self.counter = IntVar()
        self.main_container = Frame(self.parent)
        self.main_container.pack()
        self.btn_start = Button(self.main_container, command=self.btn_start_click)
        self.btn_start.configure(
            text="Start", background="Grey",
            padx=50
            )
        self.btn_start.pack(side=LEFT)

    def progress_bar(self):
        self.pbar_top = Toplevel(self.main_container)
        self.download_label = Label(
            self.pbar_top,
            text="Download Bar"
            )
        self.download_label.pack(side=TOP)

        self.download_bar = ttk.Progressbar(
            self.pbar_top, orient="horizontal",
            length=400, mode="determinate",
            variable=self.counter, maximum=5
            )
        self.download_bar.pack(side=TOP)

    def btn_start_click(self):    
        self.progress_bar()
        for i in range(4):
            self.counter = i
            time.sleep(1)

root = Tk()
root.title("Progress Bar Test")
main_ui = MainUI(root)
root.mainloop()

I found that commenting out the for loop inside btn_start_click causes the progress bar to appear immediately after clicking "Start". However, as before the actual bar does not increment.

Could someone please point out what I'm doing wrong?

Was it helpful?

Solution

The problem is that you are calling time.sleep(1) in the same thread than your Tkinter code. It makes your GUI unresponsive until the task (in this case, the call to btn_start_click) finishes. To solve this, you can start a new thread which executes that function, and update the progress bar in the GUI thread by using a synchronized object like Queue. This is a working example I wrote for a similar question.

Besides, you should call self.counter.set(i) instead of self.counter = i to update the value of the IntVar. Another solution more explicit is self.download_bar.step() with the appropiate increment.

OTHER TIPS

from tkinter import *
from tkinter import ttk

class MainUI:
    def __init__(self, parent):
        self.parent = parent
        self.counter = IntVar()
        self.main_container = Frame(self.parent)
        self.main_container.pack()
        self.btn_start = Button(self.main_container, command=self.startThread)
        self.btn_start.configure(
            text="Start", background="Grey",
            padx=50
            )
        self.btn_start.pack(side=LEFT)

    def progress_bar(self):
        self.pbar_top = Toplevel(self.main_container)
        self.download_label = Label(
            self.pbar_top,
            text="Download Bar"
            )
        self.download_label.pack(side=TOP)

        self.download_bar = ttk.Progressbar(
            self.pbar_top, orient="horizontal",
            length=400, mode="determinate",
            variable=self.counter, maximum=5
            )
        self.download_bar.pack(side=TOP)

    def startThread(self):
        import threading
        def btn_start_click():
            self.progress_bar()
            for i in range(6):
                self.counter.set(i)
                import time
                time.sleep(1)
        t = threading.Thread(None, btn_start_click, ())
        t.start()


root = Tk()
root.title("Progress Bar Test")
main_ui = MainUI(root)
root.mainloop()
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top