Чтобы понять «замораживание», вам необходимо понять mainloop()
. Вызов этого метода запускает цикл событий tkinter. Основной поток отвечает за этот цикл. Поэтому, когда ваша интенсивная работа работает в основном потоке, она также мешает mainloop. Чтобы предотвратить это, вы можете использовать вторичный Thread
для запуска вашей функции. Рекомендуется, чтобы вторичные потоки не получали доступ к объектам tkinter. Аллен B.Taylor, автор mtTkinter, утверждает:
The problems stem from the fact that the _tkinter module attempts to gain control of the main thread via a polling technique when processing calls from other threads. If it succeeds, all is well. If it fails (i.e., after a timeout), the application receives an exception with the message: "RuntimeError: main thread is not in main loop".
Вы можете иметь вторичный поток поместить информацию в Queue
. Затем выполните функцию, которая проверяет Очередь каждые x миллисекунд, в пределах mainloop, с помощью метода after()
.
Прежде всего, решите, что вы хотите, чтобы значение Progressbar: maximum вариант быть.
Это максимальное значение индикатора Progressbar (сколько единиц требуется для заполнения Progressbar). Например, вы можете установить maximum=4
и затем поместить соответствующее значение индикатора в очередь после каждой из четырех функций. Затем основной поток может извлечь эти значения (из очереди), чтобы установить прогресс через tkinter.IntVar()
. (Обратите внимание, что если вы используете progbar.step()
, Progressbar сбрасывается до 0 (пустой) в конце, а не достигает 4 (полностью заполняется).)
Вот быстрый взгляд на то, как вы можете использовать tkinter.IntVar()
с ProgressBar:
int_var = tkinter.IntVar()
pb_instance = ttk.Progressbar(root, maximum=4)
pb_instance['variable'] = int_var
pb_instance.pack()
# completely fill the Progressbar
int_var.set(4)
# get the progress value
x = int_var.get()
Вот пример, основанный на своем собственном (переименована в «основную» функции «произвольный»):
import time
import threading
try: import tkinter
except ImportError:
import Tkinter as tkinter
import ttk
import Queue as queue
else:
from tkinter import ttk
import queue
class GUI_Core(object):
def __init__(self):
self.root = tkinter.Tk()
self.int_var = tkinter.IntVar()
progbar = ttk.Progressbar(self.root, maximum=4)
# associate self.int_var with the progress value
progbar['variable'] = self.int_var
progbar.pack()
self.label = ttk.Label(self.root, text='0/4')
self.label.pack()
self.b_start = ttk.Button(self.root, text='Start')
self.b_start['command'] = self.start_thread
self.b_start.pack()
def start_thread(self):
self.b_start['state'] = 'disable'
self.int_var.set(0) # empty the Progressbar
self.label['text'] = '0/4'
# create then start a secondary thread to run arbitrary()
self.secondary_thread = threading.Thread(target=arbitrary)
self.secondary_thread.start()
# check the Queue in 50ms
self.root.after(50, self.check_que)
def check_que(self):
while True:
try: x = que.get_nowait()
except queue.Empty:
self.root.after(25, self.check_que)
break
else: # continue from the try suite
self.label['text'] = '{}/4'.format(x)
self.int_var.set(x)
if x == 4:
self.b_start['state'] = 'normal'
break
def func_a():
time.sleep(1) # simulate some work
def func_b():
time.sleep(0.3)
def func_c():
time.sleep(0.9)
def func_d():
time.sleep(0.6)
def arbitrary():
func_a()
que.put(1)
func_b()
que.put(2)
func_c()
que.put(3)
func_d()
que.put(4)
que = queue.Queue()
gui = GUI_Core() # see GUI_Core's __init__ method
gui.root.mainloop()
Если вы хотите что-то, что указывает пользователю, что есть активность
, вы можете установить опцию Пробега mode на 'indeterminate'
.
Индикатор отскакивает назад и вперед в этом режиме (скорость относится к максимальной опции).
Затем вы можете вызвать метод Progressbar start()
непосредственно перед началом вторичного потока;
, а затем позвонить по телефону stop()
после secondary_thread.is_alive()
False.
Вот пример:
import time
import threading
try: import tkinter
except ImportError:
import Tkinter as tkinter
import ttk
else: from tkinter import ttk
class GUI_Core(object):
def __init__(self):
self.root = tkinter.Tk()
self.progbar = ttk.Progressbar(self.root)
self.progbar.config(maximum=4, mode='indeterminate')
self.progbar.pack()
self.b_start = ttk.Button(self.root, text='Start')
self.b_start['command'] = self.start_thread
self.b_start.pack()
def start_thread(self):
self.b_start['state'] = 'disable'
self.progbar.start()
self.secondary_thread = threading.Thread(target=arbitrary)
self.secondary_thread.start()
self.root.after(50, self.check_thread)
def check_thread(self):
if self.secondary_thread.is_alive():
self.root.after(50, self.check_thread)
else:
self.progbar.stop()
self.b_start['state'] = 'normal'
def func_a():
time.sleep(1) # simulate some work
def func_b():
time.sleep(0.3)
def func_c():
time.sleep(0.9)
def func_d():
time.sleep(0.6)
def arbitrary():
func_a()
func_b()
func_c()
func_d()
gui = GUI_Core()
gui.root.mainloop()
→ Progressbar reference
Aaaah я не понимаю. вы могли бы объяснить это, возможно, сделать более простой пример – user2148781
@ user2148781 Я добавил пример. Единственное отличие состоит в том, что у этого есть виджет индикатора выполнения, но концепция такая же. –
Но как я мог запустить workThread изнутри GuiPart, а не запускать его автоматически? – tatsuhirosatou