Search code examples
pythontkinterttk

How can I have a ttk.progressbar call a function when it is completed (Python)?


I am trying to have a ttk progressbar call a function that adds to a value that is displayed in the Tkinter window. I currently have a function that is called at the same time the progressbar begins:

def bar1_addVal():
     global userMoney
     userMoney += progBar1_values.value
     moneyLabel["text"] = ('$' + str(userMoney))
     canvas1.after((progBar1_values.duration*100), bar1_addVal)
     return

but I cannot seem to get the exact amount of time it takes for the progressbar to finish each iteration. Is there a way to have the progressbar call a function every time it completes?


Solution

  • You can use threading to check for the variable in a loop. Then you wont interrupt the main loop.
    I made a little example of this:

    import threading, time
    from ttk import Progressbar, Frame
    from Tkinter import IntVar, Tk
    
    
    root = Tk()
    
    
    class Progress:
    
        val = IntVar()
        ft = Frame()
        ft.pack(expand=True)
        kill_threads = False  # variable to see if threads should be killed
    
        def __init__(self):
            self.pb = Progressbar(self.ft, orient="horizontal", mode="determinate", variable=self.val)
            self.pb.pack(expand=True)
            self.pb.start(50)
    
            threading.Thread(target=self.check_progress).start()
    
    
        def check_progress(self):
            while True:
                if self.kill_threads:  # if window is closed
                    return             # return out of thread
                val = self.val.get()
                print(val)
                if val > 97:
                    self.finish()
                    return
                time.sleep(0.1)
    
        def finish(self):
            self.ft.pack_forget()
            print("Finish!")
    
    
    progressbar = Progress()
    
    
    def on_closing():       # function run when closing
        progressbar.kill_threads = True  # set the kill_thread attribute to tru
        time.sleep(0.1)  # wait to make sure that the loop reached the if statement
        root.destroy()   # then destroy the window
    
    root.protocol("WM_DELETE_WINDOW", on_closing) # bind a function to close button
    
    root.mainloop()
    

    Edit: Updated the answer, to end the thread before closing window.