Search code examples
pythontkinter

Abort progress bar in tkinter


I build up a progress bar using tkinter, and I build an "abort" bottom to stop it. Here I used a global variable "isAbort" to control the function. However it fails QQ.

Does anyone know how to make it work? The code has been revised,thanksJRiggles's suggestion.

import tkinter as tk
from tkinter import ttk
import time

root = tk.Tk()
root.title("Test")
root.geometry("300x400")

isAbort = False

def ProgressBarRunning():
    global isAbort
    
    total_time = 30
    for i in range(total_time):
        if isAbort:
            bar['value']=0
            root.update()
            break
        else:
            bar['value'] = int(100*(i+1)/total_time)
            time.sleep(1)
            root.update()

def AbortProgress():
    global isAbort
    
    print("Abort process...")
    isAbort = True


bar = ttk.Progressbar(root, length=200, mode="determinate")
bar.grid(column=1, row=1, columnspan=2, sticky=("W","E"))

run_btn = tk.Button(root, text="Run", command=ProgressBarRunning)
run_btn.grid(column=1, row=2)

Abort_btn = tk.Button(root, text="Abort", command=AbortProgress)
Abort_btn.grid(column=2, row=2)

root.mainloop()

Solution

  • As commented by JRiggles- avoid using sleep in tkinter apps because it freezes the app. You can for example use the after() method of tkinter along with a recursive function instead of time.sleep():

    import tkinter as tk
    from tkinter import ttk
    
    root = tk.Tk()
    root.title("Test")
    root.geometry("300x400")
    
    isAbort = False
    
    def ProgressBarRunning(progress=0):
        global isAbort
    
        if isAbort:
            print("Progress aborted.")
            return
    
        total_time = 30
        bar['value'] = int(100 * progress / total_time)
        if progress < total_time:
            root.after(1000, ProgressBarRunning, progress + 1)
        else:
            print("Progress completed.")
    
    def AbortProgress():
        global isAbort
        isAbort = True
    
        # Optionally, reset progress bar on abort
        bar['value'] = 0
    
    bar = ttk.Progressbar(root, length=200, mode="determinate")
    bar.grid(column=1, row=1, columnspan=2, sticky=("W","E"))
    
    run_btn = tk.Button(root, text="Run", command=ProgressBarRunning)
    run_btn.grid(column=1, row=2)
    
    Abort_btn = tk.Button(root, text="Abort", command=AbortProgress)
    Abort_btn.grid(column=2, row=2)
    
    root.mainloop()
    

    The root.after() method schedules the ProgressBarRunning function to be called after 1000 milliseconds (1 second), allowing the GUI to remain responsive by not blocking the main tkinter event loop.