Updated: Mon Nov 15 12:42:48 2010
子スレッドは、threading モジュールの Thread クラスで定義する。
スレッドが実行する関数を run() メソッドを上書き定義するサブクラスを定義すると良い。
CPython インタプリタのマルチスレッドは OS のカーネルスレッドを利用するにもかかわらず
GIL (Giant Interpreter Lock) があるために Python コードを同時に実行できるスレッドは 1 つに限られる。
したがってマルチスレッドは並列性を持たす CPU 数に応じたスケーリング目的では使えないが、
メモリ空間を共有したままの並行処理には使え、またネットワークアクセスやディスクアクセスで
GUI がブロックされてしまうことの対処に使える。
WxPython の場合はメインスレッドを GUI の処理に使い、時間のかかる処理をワーカースレッドに分割する。 ワーカースレッドからメインスレッドには PostEvent() 関数でイベントを送れるので、それをメインスレッドで適宜処理すればよい。
イベントのタイプを作成する wx.PyEventBinder クラスの __init__() メソッドの第 3 (expectedIDs) 引数は、 __call__() メソッドに渡すべき ID の数を指定する。0, 1, または 2 が有効。 __call__() メソッドは後方互換性のために存在するので新規のプログラムでは使用しない。 その場合は expectedIDs はデフォルト (0) にする。
HTTP で画像を取得して表示するプログラム。
取得すべき URL をメインスレッドが Queue に入れ、
取得スレッドが Queue から取り出した URL を取得し、EVT_GOTURL イベントのパラメーターにしてメインスレッドに
PostEvent() する。
メインスレッドは EVT_GOTURL イベントを受け取ると取得したデータを取りだして
StaticBitmap としてフレーム imageFrame に追加する。
取得スレッドを setDaemon(True) でデーモンスレッドにすることで、
メインスレッドの終了時に取得スレッドの終了を待たずにプログアムを終了する。
import sys
from StringIO import StringIO
from urllib2 import urlopen, HTTPError, URLError
import wx
from threading import Thread
from Queue import Queue
EVT_TYPE_GOTURL = wx.NewEventType()
EVT_GOTURL = wx.PyEventBinder(EVT_TYPE_GOTURL)
class GotURLEvent(wx.PyCommandEvent):
def __init__(self, etype, eid, value = None):
wx.PyCommandEvent.__init__(self, etype, eid)
self.value = value
def GetValue(self):
return self.value
class GetterThread(Thread):
def __init__(self, parent):
Thread.__init__(self)
self.in_queue = Queue()
self.parent = parent
self.setDaemon(True)
self.start()
def request(self, url):
self.in_queue.put(url)
def run(self):
retry = 3
while retry > 0:
try:
url = self.in_queue.get()
except:
retry -= 1
continue
rc, hdr, body = get_url(url)
if rc == 0:
evt = GotURLEvent(EVT_TYPE_GOTURL, wx.ID_ANY, body)
wx.PostEvent(self.parent, evt)
def get_url(url):
try:
fd = urlopen(url)
rc = 0
hdr = fd.info().dict
segments = []
while True:
seg = fd.read()
if seg == '':
break
segments.append(seg)
body = ''.join(segments)
fd.close()
except HTTPError, val:
hdr = val.info().dict
rc = val.code
body = None
print 'HTTPError', rc
except URLError, val:
rc = -2
hdr = None
body = val
print 'URLError'
return rc, hdr, body
class imageFrame(wx.Frame):
def __init__(self, parent, imglist):
wx.Frame.__init__(self, parent, size = (300, 600), title = 'image viewer')
self.Bind(EVT_GOTURL, self.OnGotURL)
self.getter = GetterThread(self)
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.SetSizer(self.sizer)
for filename in imglist:
self.getter.request(filename)
def OnGotURL(self, event):
imgdata = event.GetValue()
stream = StringIO(imgdata)
image = wx.ImageFromStream(stream)
stream.close()
bitmap = image.ConvertToBitmap()
self.sizer.Add(wx.StaticBitmap(self, wx.ID_ANY, bitmap), 0, 0)
self.Layout()
self.Refresh()
app = wx.App(False)
frame = imageFrame(None, sys.argv[1:])
frame.Show()
app.MainLoop()