python+Tkinter+多线程的实例
Python+Tkinter+多线程
界面和多线程一向是编程里比较难的地方,常见的做法一般是界面一个线程,后台新开一个工作线程,这两个线程进行通信,这样可以让界面不至于为响应。
在python中可以利用队列完成整体的架构设计。
直接给大家看代码吧,一个简单实例,非常好的例子。
import Tkinter,time,threading,random,Queue
class GuiPart():
def __init__(self,master,queue,endCommand):
self.queue=queue
Tkinter.Button(master,text='Done',command=endCommand).pack()
def processIncoming(self):
while self.queue.qsize():
try:
msg=self.queue.get(0)
print msg
except Queue.Empty:
pass
class ThreadedClient():
def __init__(self,master):
self.master=master
self.queue=Queue.Queue()
self.gui=GuiPart(master,self.queue,self.endApplication)
self.running=True
self.thread1=threading.Thread(target=self.workerThread1)
self.thread1.start()
self.periodicCall()
def periodicCall(self):
self.master.after(200,self.periodicCall)
self.gui.processIncoming()
if not self.running:
self.master.destroy()
def workerThread1(self):
#self.ott=Tkinter.Tk()
#self.ott.mainloop()
while self.running:
time.sleep(rand.random()*1.5)
msg=rand.random()
self.queue.put(msg)
def endApplication(self):
self.running=False
rand=random.Random()
root=Tkinter.Tk()
client=ThreadedClient(root)
root.mainloop()
tkinter与多线程问题
长时间执行后台任务,UI会处于无响应状态。在子线程里更新UI状态,听说是不允许的。在哪个线程里调用了tk.mainloop(),就只能在哪个线程里更新UI。
下例演示了如何更新。
import Tkinter as tk
from ttk import *
import time
import Queue, threading
class MainWindow:
def __init__(self):
self.root = tk.Tk()
self.root.title('Demo')
def show(self):
self.progress = tk.IntVar()
self.progress_max = 100
self.progressbar = Progressbar(self.root, mode='determinate', orient=tk.HORIZONTAL, variable=self.progress, maximum=self.progress_max)
self.progressbar.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
self.progress.set(0)
btn = tk.Button(self.root, text='start', command=self.start)
btn.pack(fill=tk.BOTH, expand=True, padx=15, pady=5)
self.btn = btn
self.root.mainloop()
def start(self):
self.progress.set(0)
self.btn.config(state=tk.DISABLED)
self.thread_queue = Queue.Queue() # used to communicate between main thread (UI) and worker thread
new_thread = threading.Thread(target=self.run_loop, kwargs={'param1':100, 'param2':20})
new_thread.start()
# schedule a time-task to check UI
# it's in main thread, because it's called by self.root
self.root.after(100, self.listen_for_result)
def run_loop(self, param1, param2):
progress = 0
for entry in range(self.progress_max):
time.sleep(0.1)
progress = progress + 1
self.thread_queue.put(progress)
def listen_for_result(self):
'''
Check if there is something in the queue.
Must be invoked by self.root to be sure it's running in main thread
'''
try:
progress = self.thread_queue.get(False)
self.progress.set(progress)
except Queue.Empty: # must exist to avoid trace-back
pass
finally:
if self.progress.get() < self.progressbar['maximum']:
self.root.after(100, self.listen_for_result)
else:
self.btn.config(state=tk.NORMAL)
if __name__ == '__main__':
win = MainWindow()
win.show()
一个进度条。设定最大进度为100。在子线程里每隔0.1秒更新一格。
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持。
相关文章