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) 
4
demandé sur nbro 2013-05-06 18:08:26

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()
4
répondu martineau 2017-05-23 12:16:39

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()
2
répondu martineau 2015-10-05 15:49:36