python+Tkinter+多线程
界面和多线程一向是编程里比较难的地方,常见的做法一般是界面一个线程,后台新开一个工作线程,这两个线程进行通信,这样可以让界面不至于为响应。
在python中可以利用队列完成整体的架构设计。
直接给大家看代码吧,一个简单实例,非常好的例子。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | 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。
下例演示了如何更新。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | 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() |
一个进度条。设定最大进度为100。在子线程里每隔0.1秒更新一格。
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持IT俱乐部。