我有一个程序(时间流逝制造商),它有两个更新wx.StaticBitmap
的线程。 当两个线程访问wx.StaticBitmap
时,它会因错误而崩溃
python:xcb_io.c:221:poll_for_event:断言`(((长) (event_sequence) - (长)(dpy->请求))< = 0)'失败。
我尝试了一个Google search的答案,我试图自己解决,但我仍然无法理解。
重现此错误的简单代码 (这不是实际程序):
#!/usr/bin/env python
import wx
import time,os.path,glob,threading
class MyFrame(wx.Frame):
def __init__(self, *args, **kwds):
kwds["style"] = wx.DEFAULT_FRAME_STYLE
wx.Frame.__init__(self, *args, **kwds)
self.bitmap_1 = wx.StaticBitmap(self, -1, wx.NullBitmap)
self.__set_properties()
self.__do_layout()
wx.CallAfter(self._img)
def __set_properties(self):
self.SetTitle("frame_1")
def __do_layout(self):
sizer_1 = wx.BoxSizer(wx.VERTICAL)
sizer_1.Add(self.bitmap_1, 0, 0, 0)
self.SetSizer(sizer_1)
sizer_1.Fit(self)
self.Layout()
def _img(self):
Thread1= threading.Thread(target=self._img1)
Thread1.start()
Thread2 = threading.Thread(target=self._img2)
Thread2.start()
def _img1(self):
frames = glob.glob("/path/to/pngs/*.png")
frames.sort()
for i in range(len(frames)):
if os.path.isfile(frames[i]) and i%2 == 0:
print frames[i]
wx.Yield()
##time.sleep(0.5)
wx.CallAfter(self.bitmap_1.SetBitmap,wx.Bitmap(frames[i], wx.BITMAP_TYPE_ANY))
wx.CallAfter(self.Update)
def _img2(self):
frames = glob.glob("/path/to/pngs/*.png")
frames.sort()
for i in range(len(frames)):
if os.path.isfile(frames[i]) and i%2 == 1:
print frames[i]
wx.Yield()
##time.sleep(0.5)
wx.CallAfter(self.bitmap_1.SetBitmap,wx.Bitmap(frames[i], wx.BITMAP_TYPE_ANY))
wx.CallAfter(self.Update)
if __name__ == "__main__":
app = wx.PySimpleApp(0)
wx.InitAllImageHandlers()
frame_1 = MyFrame(None, -1, "")
app.SetTopWindow(frame_1)
frame_1.Show()
app.MainLoop()
我用wx.PostEvent
解决了这个问题。请参阅我的回答。
答案 0 :(得分:4)
避免所有类型的崩溃和异常行为的最简单方法是确保只有主线程处理GUI。您可以尝试通过查找和锁定关键代码块来实现,但在我看来,这是一场失败的游戏。使用事件更容易使处理线程与主线程同步:
while run:
self.timer_evt.wait() # wait for main thread to unblock me
self.timer_evt.clear()
<process stuff, put results in queue or shared variables>
在处理线程中,
def tick(self):
if run:
<update GUI from queued data or shared variables>
self.timer_evt.set() # unblock processing thread
self.root.after(ms, self.tick) # reschedule the GUI update
在主线程中。
答案 1 :(得分:1)
我用wx.PostEvent
解决了它:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# generated by wxGlade 0.6.3 on Mon Oct 17 19:59:55 2011
import wx
import time,os.path,glob,threading
# begin wxGlade: extracode
# end wxGlade
ID_START = wx.NewId()
ID_STOP = wx.NewId()
# Define notification event for thread completion
EVT_RESULT_ID = wx.NewId()
def EVT_RESULT(win, func):
"""Define Result Event."""
win.Connect(-1, -1, EVT_RESULT_ID, func)
class ResultEvent(wx.PyEvent):
"""Simple event to carry arbitrary result data."""
def __init__(self, data):
"""Init Result Event."""
wx.PyEvent.__init__(self)
self.SetEventType(EVT_RESULT_ID)
self.data = data
class MyFrame(wx.Frame):
def __init__(self, *args, **kwds):
# begin wxGlade: MyFrame.__init__
kwds["style"] = wx.DEFAULT_FRAME_STYLE
wx.Frame.__init__(self, *args, **kwds)
self.bitmap_1 = wx.StaticBitmap(self, -1, wx.NullBitmap)
self.__set_properties()
self.__do_layout()
# end wxGlade
self.frames = ""
EVT_RESULT(self,self.OnResult)
wx.CallAfter(self._img)
def __set_properties(self):
# begin wxGlade: MyFrame.__set_properties
self.SetTitle("frame_1")
# end wxGlade
def __do_layout(self):
# begin wxGlade: MyFrame.__do_layout
sizer_1 = wx.BoxSizer(wx.VERTICAL)
sizer_1.Add(self.bitmap_1, 0, 0, 0)
self.SetSizer(sizer_1)
sizer_1.Fit(self)
self.Layout()
# end wxGlade
def OnResult(self, event):
"""Show Result status."""
if event.data is None:
pass
else:
self.bitmap_1.SetBitmap(wx.Bitmap(event.data, wx.BITMAP_TYPE_ANY))
# end of class MyFrame
def _img(self):
Thread1= threading.Thread(target=self._img1)
Thread1.start()
Thread2 = threading.Thread(target=self._img2)
Thread2.start()
def _img1(self):
frames = glob.glob("/home/mitch/Pictures/*.png")
frames.sort()
for i in range(len(frames)):
if os.path.isfile(frames[i]) and i%2 == 0:
print frames[i]
##wx.Yield()
##time.sleep(0.5)
wx.PostEvent(self, ResultEvent(frames[i]))
def _img2(self):
frames = glob.glob("/home/mitch/Pictures/*.png")
frames.sort()
for i in range(len(frames)):
if os.path.isfile(frames[i]) and i%2 == 1:
print frames[i]
##wx.Yield()
##time.sleep(0.5)
wx.PostEvent(self, ResultEvent(frames[i]))
if __name__ == "__main__":
app = wx.PySimpleApp(0)
wx.InitAllImageHandlers()
frame_1 = MyFrame(None, -1, "")
app.SetTopWindow(frame_1)
frame_1.Show()
app.MainLoop()