由于 tkinter 是单线程,你需要另一个线程来执行你的main
功能无需冻结 GUI。一种常见的方法是工作线程将消息放入同步对象(如Queue http://docs.python.org/2/library/queue.html),GUI 部分使用此消息,更新进度条。
以下代码基于完整详细的example http://code.activestate.com/recipes/82965/在活动状态上:
import tkinter as tk
from tkinter import ttk
import threading
import queue
import time
class App(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.queue = queue.Queue()
self.listbox = tk.Listbox(self, width=20, height=5)
self.progressbar = ttk.Progressbar(self, orient='horizontal',
length=300, mode='determinate')
self.button = tk.Button(self, text="Start", command=self.spawnthread)
self.listbox.pack(padx=10, pady=10)
self.progressbar.pack(padx=10, pady=10)
self.button.pack(padx=10, pady=10)
def spawnthread(self):
self.button.config(state="disabled")
self.thread = ThreadedClient(self.queue)
self.thread.start()
self.periodiccall()
def periodiccall(self):
self.checkqueue()
if self.thread.is_alive():
self.after(100, self.periodiccall)
else:
self.button.config(state="active")
def checkqueue(self):
while self.queue.qsize():
try:
msg = self.queue.get(0)
self.listbox.insert('end', msg)
self.progressbar.step(25)
except Queue.Empty:
pass
class ThreadedClient(threading.Thread):
def __init__(self, queue):
threading.Thread.__init__(self)
self.queue = queue
def run(self):
for x in range(1, 5):
time.sleep(2)
msg = "Function %s finished..." % x
self.queue.put(msg)
if __name__ == "__main__":
app = App()
app.mainloop()
自从原始示例 http://code.activestate.com/recipes/82965/在我看来,ActiveState 有点混乱(ThreadedClient
与GuiPart
,并且诸如控制从 GUI 生成线程的时刻之类的事情并不那么简单),我重构了它并添加了一个按钮来启动新线程。