python thread crash
-
22-10-2019 - |
Question
I have a program (time lapse maker) which has two threads that updates a wx.StaticBitmap
. When the two threads access the wx.StaticBitmap
it crashes with the error
python: xcb_io.c:221: poll_for_event: Assertion `(((long) (event_sequence) - (long) (dpy->request)) <= 0)' failed.
I tried a Google search for the answer and I tried to solve it myself but I still can't figure it out.
Simple piece of code that reproduces this error (this is not the actual program):
#!/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()
I solved it with wx.PostEvent
See my answer.
Solution 2
I solved it with 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()
OTHER TIPS
The easiest way to avoid crashes and anomalous behavior of all sorts is to ensure that only the main thread handles the GUI. You could try to do it by finding and locking critical code blocks, but in my opinion that's a losing game. Much easier to synchronize the processing thread(s) with the main thread using events:
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>
in the processing thread, and
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
in the main thread.