Why ttk Progressbar appears after process in Tkinter

匿名 (未验证) 提交于 2019-12-03 01:08:02

问题:

I want to create a large text upon Tkinter menu command and provide visual support by a progress bar. Although the progress bar is meant to start before the subsequent time-consuming loop, the progress bar shows up only after the large text was created and displayed.

def menu_bar(self):     self.create_menu.add_command(label="Create large file",      command=self.create_large_file)  def create_large_file(self):     self.progressbar = ttk.Progressbar(self.master, mode='indeterminate')     self.progressbar.pack()     self.progressbar.start()     self.text.delete(1.0, 'end')     self.file_content = []  i = 0 while i 

回答1:

I think the problem is that the time-consuming loop is preventing the tkinter event loop, mainloop(), from running. In other words, when your work intensive function runs in the same thread as the GUI, it interferes with it by hogging the interpreter.

To prevent this you can use a secondary Thread to run your function and run the GUI and its progressbar in the main thread. To give you and idea of how to do this, here's a simple example I derived from code in a another (unrelated) progressbar question to show how easily something like that can be done. Note: It's generally recommended that secondary threads not be given direct access to the main thread's tkinter objects.

from Tkinter import * import ttk  import time import threading  def foo():     time.sleep(5) # simulate some work  def start_foo_thread(event):     global foo_thread     foo_thread = threading.Thread(target=foo)     foo_thread.daemon = True     progressbar.start()     foo_thread.start()     root.after(20, check_foo_thread)  def check_foo_thread():     if foo_thread.is_alive():         root.after(20, check_foo_thread)     else:         progressbar.stop()  root = Tk() mainframe = ttk.Frame(root, padding="3 3 12 12") mainframe.grid(column=0, row=0, sticky=(N, W, E, S)) mainframe.columnconfigure(0, weight=1) mainframe.rowconfigure(0, weight=1) progressbar = ttk.Progressbar(mainframe, mode='indeterminate') progressbar.grid(column=1, row=100, sticky=W)  ttk.Button(mainframe, text="Check",            command=lambda:start_foo_thread(None)).grid(column=1, row=200,                                                        sticky=E)  for child in mainframe.winfo_children():     child.grid_configure(padx=5, pady=5) root.bind('', start_foo_thread)  root.mainloop()


回答2:

Here's another considerably simpler solution that doesn't require mixing Tkinter and multi-threading. To use it requires the ability to call the progressbar widget's update_idletasks() method multiple times during the time-consuming function.

from Tkinter import * import ttk  import time  def foo(progressbar):     progressbar.start()     for _ in range(50):         time.sleep(.1) # simulate some work         progressbar.step(10)         progressbar.update_idletasks()     progressbar.stop()  root = Tk()  mainframe = ttk.Frame(root, padding="3 3 12 12") mainframe.grid(column=0, row=0, sticky=(N, W, E, S)) mainframe.columnconfigure(0, weight=1) mainframe.rowconfigure(0, weight=1) progressbar = ttk.Progressbar(mainframe, mode='indeterminate') progressbar.grid(column=1, row=100, sticky=W)  ttk.Button(mainframe, text="Check",            command=lambda:foo(progressbar)).grid(column=1, row=200, sticky=E)  for child in mainframe.winfo_children():     child.grid_configure(padx=5, pady=5) root.bind('', lambda event:foo(progressbar))  root.mainloop()


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!