Deb*_*ski 11 python multithreading tkinter event-handling
我正在使用队列在后台线程和Tk GUI应用程序之间交换消息.目前,这是通过不时调用查询方法来完成的.
def read_queue(self):
try:
self.process(self.queue.get(False)) # non-blocking
except Queue.Empty:
pass
finally:
self.after(UPDATE_TIME, self.read_queue)
Run Code Online (Sandbox Code Playgroud)
这种方法的问题是,如果UPDATE_TIME
太大,应用程序将比可能的更慢地处理新项目.如果它太小,Tk花费大部分时间来检查队列,虽然它可以在此期间做其他事情.
有没有办法read_queue
在新项目到达队列时自动触发方法?(当后台线程填满队列时,我当然可以在Tk上调用一个方法,但我担心这会给我带来一些并发问题 - 这就是我毕竟使用队列的原因.)
小智 15
一个选项可能是mtTkinter http://tkinter.unpythonic.net/wiki/mtTkinter
以下是从后台线程使用event_generate的另一个示例:
##The only secure way I found to make Tkinter mix with threads is to never
##issue commands altering the graphical state of the application in another
##thread than the one where the mainloop was started. Not doing that often
##leads to random behaviour such as the one you have here. Fortunately, one
##of the commands that seems to work in secondary threads is event_generate,
##giving you a means to communicate between threads. If you have to pass
##information from one thread to another, you can use a Queue.
##
##This obviously complicates things a bit, but it may work far better.
##Please note that the 'when' option *must* be specified in the call to
##event_generate and *must not* be 'now'. If it's not specified or if it's
##'now', Tkinter may directly execute the binding in the secondary thread's
##context. (Eric Brunel)
import threading
import time
import Queue
from Tkinter import *
## Create main window
root = Tk()
## Communication queue
commQueue = Queue.Queue()
## Function run in thread
def timeThread():
curTime = 0
while 1:
## Each time the time increases, put the new value in the queue...
commQueue.put(curTime)
## ... and generate a custom event on the main window
try:
root.event_generate('<<TimeChanged>>', when='tail')
## If it failed, the window has been destoyed: over
except TclError:
break
## Next
time.sleep(1)
curTime += 1
## In the main thread, do usual stuff
timeVar = IntVar()
Label(root, textvariable=timeVar, width=8).pack()
## Use a binding on the custom event to get the new time value
## and change the variable to update the display
def timeChanged(event):
timeVar.set(commQueue.get())
root.bind('<<TimeChanged>>', timeChanged)
## Run the thread and the GUI main loop
th=threading.Thread(target=timeThread)
th.start()
root.mainloop()
Run Code Online (Sandbox Code Playgroud)
还提到以类似的方式使用after_idle.
即.root.after_idle(timeChanged)
小智 5
总结:我不会使用" noob oddy的示例代码 " - 这是一个根本上有缺陷的方法.
我不是python guru,但是" noob oddy "(在后台线程中调用root.event_generate(...))提供的示例代码似乎是一种"根本上有缺陷的方法".也就是说,互联网上有几篇文章声明"永远不要在'GUI线程'的上下文之外调用Tkinter函数/对象方法"(通常是主线程).他的示例"大部分时间都在工作",但如果您提高事件生成率,那么示例的"崩溃率"将会增加 - 但是,具体行为取决于事件生成率和平台的性能特征.
例如,使用他的代码与Python 2.7.3,如果你改变:
time.sleep(1)
Run Code Online (Sandbox Code Playgroud)
至:
time.sleep(0.01)
Run Code Online (Sandbox Code Playgroud)
然后脚本/应用程序通常会在'x'次迭代后崩溃.
经过多次搜索,如果你"必须使用Tkinter",那么从后台线程获取信息到GUI线程的最"防弹方法"就是使用'after()'widget方法来轮询一个线程安全的对象(例如'队列').例如,
################################################################################
import threading
import time
import Queue
import Tkinter as Tk
import Tkconstants as TkConst
from ScrolledText import ScrolledText
from tkFont import Font
global top
global dataQ
global scrText
def thread_proc():
x = -1
dataQ.put(x)
x = 0
for i in xrange(5):
for j in xrange(20):
dataQ.put(x)
time.sleep(0.1)
x += 1
time.sleep(0.5)
dataQ.put(x)
def on_after_elapsed():
while True:
try:
v = dataQ.get(timeout=0.1)
except:
break
scrText.insert(TkConst.END, "value=%d\n" % v)
scrText.see(TkConst.END)
scrText.update()
top.after(100, on_after_elapsed)
top = Tk.Tk()
dataQ = Queue.Queue(maxsize=0)
f = Font(family='Courier New', size=12)
scrText = ScrolledText(master=top, height=20, width=120, font=f)
scrText.pack(fill=TkConst.BOTH, side=TkConst.LEFT, padx=15, pady=15, expand=True)
th = threading.Thread(target=thread_proc)
th.start()
top.after(100, on_after_elapsed)
top.mainloop()
th.join()
## end of file #################################################################
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
9646 次 |
最近记录: |