Pourquoi TTK Progressbar apparaît après le processus dans Tkinter
je veux créer un grand texte sur la commande de menu Tkinter
et fournir un support visuel par une barre de progression. Bien que la barre de progression soit censée commencer avant la boucle de temps suivante, la barre de progression apparaît seulement après que le grand texte a été créé et affiché.
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 < 2000000:
line = lfd.input_string
self.file_content.append(line + "n")
i += 1
self.file_content = ''.join(self.file_content)
self.text.insert(1.0, self.file_content)
2 réponses
je pense que le problème est que la boucle chronophage empêche la boucle d'événement tkinter
, mainloop()
, de fuir. En d'autres termes, lorsque votre fonction intensive de travail fonctionne dans le même fil que L'interface graphique, elle interfère avec elle en monopolisant l'interpréteur.
pour éviter cela, vous pouvez utiliser un Thread secondaire pour exécuter votre fonction et exécuter l'interface graphique et sa barre de progression dans le thread principal. Pour vous donner une idée de la façon de faire cela, Voici un exemple simple que j'ai dérivé de code dans un autre (sans rapport) question progressbar pour montrer comment facilement quelque chose comme cela peut être fait. Note: il est généralement recommandé de ne pas donner aux threads secondaires un accès direct aux objets tkinter
du thread principal.
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('<Return>', start_foo_thread)
root.mainloop()
Voici une autre solution beaucoup plus simple qui ne nécessite pas de mélange Tkinter et multi-filetage. Pour l'utiliser, il nécessite la possibilité d'appeler la méthode update_idletasks()
du widget progressbar plusieurs fois au cours de la fonction chronophage.
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('<Return>', lambda event:foo(progressbar))
root.mainloop()