Почему ttk Progressbar появляется после процесса в Tkinter
Я хочу создать большой текст на Tkinter
Команда меню и обеспечивает визуальную поддержку с помощью индикатора выполнения. Несмотря на то, что индикатор выполнения должен запускаться до следующего трудоемкого цикла, индикатор выполнения отображается только после создания и отображения большого текста.
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 ответа
Я думаю, что проблема в том, что длительный цикл предотвращает tkinter
цикл событий, mainloop()
от бега. Другими словами, когда ваша трудоемкая функция выполняется в том же потоке, что и графический интерфейс, она мешает ей, включая интерпретатор.
Чтобы предотвратить это, вы можете использовать вторичный поток для запуска вашей функции и запуска графического интерфейса и его индикатора выполнения в основном потоке. Чтобы дать вам представление о том, как это сделать, вот простой пример, который я извлек из кода в другом (не связанном) вопросе о прогресс-баре, чтобы показать, как легко можно сделать что-то подобное. Примечание. Обычно рекомендуется, чтобы вторичным потокам не был предоставлен прямой доступ к основным потокам. tkinter
объекты.
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()
Вот еще одно значительно более простое решение, которое не требует смешивания Tkinter и многопоточности. Для его использования требуется возможность вызова виджета индикатора выполнения update_idletasks()
метод несколько раз в течение трудоемкой функции.
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()