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)