要了解“冻结”,您需要了解mainloop()
。调用此方法启动tkinter事件循环。主线程负责这个循环。因此,当您的工作密集型函数在主线程中运行时,它也会干扰主循环。为了防止这种情况,您可以使用辅助Thread
来运行您的功能。建议不要让辅助线程访问 tkinter 对象。mtTkinter 的作者 Allen B.Taylor说:
问题源于 _tkinter 模块在处理来自其他线程的调用时试图通过轮询技术获得对主线程的控制。如果成功,一切都很好。如果失败(即超时后),应用程序会收到异常消息:“RuntimeError: main thread is not in main loop”。
您可以让辅助线程将信息放入Queue
. 然后有一个函数,通过该方法在主循环中每x毫秒检查一次队列。after()
首先,确定您希望Progressbar的最大选项的值是什么。
这是进度条的最大指标值(填充进度条需要多少单位)。例如,您可以maximum=4
在四个函数中的每一个之后设置适当的指标值并将其放入队列中。然后主线程可以检索这些值(从队列中)以通过tkinter.IntVar()
. (请注意,如果您使用progbar.step()
,Progressbar 会在最后重置为 0(空),而不是达到 4(完全填充)。)
以下是如何将 atkinter.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()
如果您想要的只是向用户表明有活动的东西,
您可以将进度条的模式选项设置为'indeterminate'
.
指标在此模式下来回反弹(速度与最大选项有关)。
然后可以start()
在启动辅助线程之前直接调用Progressbar的方法;
然后调用stop()
aftersecondary_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()
→进度条参考