Tkinter progress bar how to correctly implement it in a model dialog box Tkinter progress bar how to correctly implement it in a model dialog box tkinter tkinter

Tkinter progress bar how to correctly implement it in a model dialog box


If you didn't want to use thread,maybe you could try asyncio.I don't know whether my code is correct,but it works fine on my PC.

Welcome to point out the fault in my code, I really don't know whether it is a good practice.

import tkinter as tkfrom tkinter import ttkimport asyncio, timeimport warningsclass App(tk.Tk):    def __init__(self):        super(App, self).__init__()        self.start_btn = ttk.Button(self, text="Start Test", command=self.test_start)        self.start_btn.pack(padx=10, pady=5, fill="both", expand=True)        self.stop_btn = ttk.Button(self, text="Stop Test", command=self.test_stop, state=tk.DISABLED)        self.stop_btn.pack(padx=10, pady=5, fill="both", expand=True)        self.test_window = tk.Toplevel()        self.test_window.progressbar = ttk.Progressbar(self.test_window, orient=tk.HORIZONTAL)        self.test_window.progressbar.grid(padx=10, pady=5, sticky=tk.NSEW, columnspan=2, column=0, row=0)        self.test_window.switch_btn = ttk.Button(self.test_window, text="Pause", command=self.switch)        self.test_window.switch_btn.grid(padx=10, pady=5, sticky=tk.NSEW, column=0, row=1)        self.test_window.cancel_btn = ttk.Button(self.test_window, text="Cancel", command=self.test_cancel)        self.test_window.cancel_btn.grid(padx=10, pady=5, sticky=tk.NSEW, column=1, row=1)        self.test_window.withdraw()    def test_start(self):        self.stop_btn['state'] = tk.NORMAL        self.test_window.deiconify()        self.test_window.after(0, self.work)    def work(self):        async def async_work(): # define a async task            try:                await asyncio.sleep(3)  # could be another async work.            except asyncio.CancelledError:                print("cancel or stop")                raise  # if don't raise the error ,it won't cancel        async def progressbar_add():            self.task = asyncio.create_task(async_work())            timeout = 0            while True: # wait the async task finish                done, pending = await asyncio.wait({self.task}, timeout=timeout)                self.test_window.update()                if self.task in done:                    self.test_window.progressbar['value'] += 10  # if finished, value += 10                    print(self.test_window.progressbar['value'])                    await self.task                    break        if self.test_window.progressbar['value'] >= 100:            return        asyncio.run(progressbar_add())        self.test_window.after(0, self.work)    def test_stop(self):        self.test_window.progressbar['value'] = 0        self.stop_btn['state'] = tk.DISABLED        try:            all_tasks = asyncio.Task.all_tasks()            for task in all_tasks:                task.cancel()        except RuntimeError:  # if you have cancel the task it will raise RuntimeError            pass    def switch(self):        if self.test_window.switch_btn['text'] == 'Pause':            self.test_window.switch_btn['text'] = 'Resume'            try:                all_tasks = asyncio.Task.all_tasks()                for task in all_tasks:                    task.cancel()            except RuntimeError:  # if you have cancel the task it will raise RuntimeError                pass        else:            self.test_window.switch_btn['text'] = 'Pause'            return self.work()    def test_cancel(self):        # self.test_window.progressbar['value'] = 0        print(self.test_window.progressbar['value'])        self.test_window.withdraw()        self.task.cancel()app = App()app.mainloop()

Below Python 3.7,you couldn't use asyncio.run(async).It was added in Python 3.7.Need to use get_event_loop() and run_until_complete().(Point out By @Saad)