import wx
import threading
def call_and_wait(target, *args, **kwargs):
if wx.Thread_IsMain():
return target(*args, **kwargs)
else:
context = {}
context['event'] = threading.Event()
wx.CallAfter(call_in_mainthread, context, target, *args, **kwargs)
context['event'].wait()
return context['result']
def call_in_mainthread(context, target, *args, **kwargs):
context['result'] = target(*args, **kwargs)
context['event'].set()
主要思想是在工作线程创建一个Event对象,然后利用CallAfter放在主线程中去调用,工作线程调用event.wait()开始等待,当主线程执行完调用之后,设置event的状态,这样工作线程开始继续运行。下面是使用这个方法的一个例子:
class WorkThread(Thread):
def run():
# ...
call_and_wait(ShowSomeDialog)
def ShowSomeDialog():
# display some dialog
dlg = UserPassDialg(None)
if dlg.ShowModel() == wx.ID_OK:
return (dlg.Username, dlg.Password)
else:
return None
call_and_wait调用很别扭,利用decorator我们可以取消该方法的调用,取而代之我们把它的调用放到decorator去做。
def mainthread(func, *args, **kwargs):
'''ensure func invoked in main thread'''
def _func():
return call_and_wait(func, *args, **kwargs)
return _func
这样我们可以将前面的例子改写成:
class WorkThread(Thread):
def run():
# ...
ShowSomeDialog()
@mainthread
def ShowSomeDialog():
# display some dialog
dlg = UserPassDialg(None)
if dlg.ShowModel() == wx.ID_OK:
return (dlg.Username, dlg.Password)
else:
return None
似乎上面的代码并没有简洁多少,但是如果碰到需要大量调用主线程的方法时候,使用decorator可以避免创建许多一次性的方法。例如,在应用使用sqlite的情况下,由于sqlite只能单线程使用,所以也不能在工作线程中调用sqlite方法,通常的方法就是将所有sqlite调用都放到主线程中去做。
@mainthread
def get_account(id):
#....
@mainthread
def save_account(account):
#...
# more sqlite methods
分享到:
2011-01-15 23:56
浏览 5721
评论