Strange wxMemoryDC Exception?

I'm getting strange errors in a custom panel that double-buffers its drawing routines. The basic routines, which are based on a wx double buffering demo, are:

     def OnSize(self, event):
         (w,h) = self.GetClientSize()
         self.bitmapbuffer = wx.EmptyBitmap(w, h)

     def OnPaint(self, event):
         if USE_BUFFERED_DC:
             dc = wx.BufferedPaintDC(self, self.bitmapbuffer)
         else:
             dc = wx.PaintDC(self)
             dc.DrawBitmap(self.bitmapbuffer, (0,0))

     def UpdateDrawing(self):
         (w,h) = self.GetClientSizeTuple()
         self.drawing_mutex.acquire()
         if USE_BUFFERED_DC:
             dc = wx.BufferedDC(wx.ClientDC(self), self.bitmapbuffer)
             self.DrawBoard(dc)
         else:
             # update the buffer
             dc = wx.MemoryDC()
             dc.SelectObject(self.bitmapbuffer)

             self.DrawBoard(dc)
             # update the screen
             wx.ClientDC(self).Blit((0, 0), (w, h), dc, (0, 0))
         self.drawing_mutex.release()

Notice the flag used by the demo to turn buffering on or off, yet the drawings (and errors) appear to be the same either way.

Since the actual application has several threads that could potentially call UpdateDrawing at any time, there's a mutex to regulate access. Yet despite the mutex, I get the following exception:

Traceback (most recent call last):
   File "boardcanvas.py", line 492, in OnPaint
     dc = wx.BufferedPaintDC(self, self.bitmapbuffer)
   File "C:\Python23\Lib\site-packages\wx\gdi.py", line 3079, in __init__
     newobj = _gdi.new_BufferedPaintDC(*args, **kwargs)
wx.core.PyAssertionError: C++ assertion "wxAssertFailure" failed in ..\..\src\ms
w\dcmemory.cpp(133): Couldn't select a bitmap into wxMemoryDC

Occasionally the exception will say recommend deleting the previous DC or using SelectObject(wx.NullBitmap).

Sometimes, the window will just freeze even with no exception. What could be causing this problem? Any help would be greatly appreciated.

First: please put this kind of thing in a small self-contained app that demonstrates the problem.

Alex Zeiger wrote:

I'm getting strange errors in a custom panel that double-buffers its drawing routines. The basic routines, which are based on a wx double buffering demo, are:

I wrote that, so hopefully I can help.

> Notice the flag used by the demo to turn buffering on or off, yet the
> drawings (and errors) appear to be the same either way.

actually, it doesn't turn buffering on or off, it switches between using the built in BufferedDC classes, and doing it by hand. That was for instructional purposes, and because there are time you might want to do it by hand. it looks like you have USE_BUFFERED_DC == True, so you can just dump the other code.

    def OnSize(self, event):
        (w,h) = self.GetClientSize()
        self.bitmapbuffer = wx.EmptyBitmap(w, h)

    def OnPaint(self, event):
        if USE_BUFFERED_DC:
            dc = wx.BufferedPaintDC(self, self.bitmapbuffer)
        else:
            dc = wx.PaintDC(self)
            dc.DrawBitmap(self.bitmapbuffer, (0,0))

    def UpdateDrawing(self):
        (w,h) = self.GetClientSizeTuple()
        self.drawing_mutex.acquire()
        if USE_BUFFERED_DC:
            dc = wx.BufferedDC(wx.ClientDC(self), self.bitmapbuffer)
            self.DrawBoard(dc)
        else:
            # update the buffer
            dc = wx.MemoryDC()
            dc.SelectObject(self.bitmapbuffer)

            self.DrawBoard(dc)
            # update the screen
            wx.ClientDC(self).Blit((0, 0), (w, h), dc, (0, 0))
        self.drawing_mutex.release()

Since the actual application has several threads that could potentially call UpdateDrawing at any time, there's a mutex to regulate access. Yet despite the mutex, I get the following exception:

I've never used a mutex, and a quick scan of the mutex docs in the library reference makes no mention of mutex.acquire(), so I'm at a loss. Have you subclassed or written your own mutex code?

  File "boardcanvas.py", line 492, in OnPaint
    dc = wx.BufferedPaintDC(self, self.bitmapbuffer)
  File "C:\Python23\Lib\site-packages\wx\gdi.py", line 3079, in __init__

this looks like you're running Windows. Windows is more sensitive that other platforms about having the same Bitmap Selected into more than one MemoryDC. Nonetheless, you should be able to get this to work.

I suspect your mutex isn't working right, but another thing you might try is to make USE_BUFFERED_DC False, and see if your problem is with the BufferedDC classes.

You also might try explicitly deleting the dc before calling
self.drawing_mutex.release(). If mutex.release() immediately allows the next code in the queue to run, then the dc might not be deleted yet. The way it's written, it won't get deleted until the end of the UpdateDrawing Method.

What wx.BufferedDC does is create a wx.MemoryDC, write to it, then, when it is destroyed, it copies the MemDC to the mainDC, and then deletes it.

so try:
del dc
self.drawing_mutex.release()

You still might have a problem, because I'm not sure if the C++ object gets deleted as soon as the Python object that wraps it does.

Did you try:

dc.SelectObject(wx.NullBitmap)
self.drawing_mutex.release()

let us know how it works out,

-Chris

···

--
Christopher Barker, Ph.D.
Oceanographer
                                         
NOAA/OR&R/HAZMAT (206) 526-6959 voice
7600 Sand Point Way NE (206) 526-6329 fax
Seattle, WA 98115 (206) 526-6317 main reception

Chris.Barker@noaa.gov

Alex Zeiger wrote:

What could be causing this problem?

Probably trying to do GUI operations in worker theads.

Any help would be greatly appreciated.

Change all calls to theWindow.UpdateDrawing() in the worker threads to wx.CallAfter(theWindow.UpdateDrawing) This will cause all the UpdateDrawing calls to happen in the main thread, (and you can get rid of the mutex. since it won't be needed any longer.)

···

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!

Robin Dunn wrote:

Change all calls to theWindow.UpdateDrawing() in the worker threads to wx.CallAfter(theWindow.UpdateDrawing) This will cause all the UpdateDrawing calls to happen in the main thread, (and you can get rid of the mutex. since it won't be needed any longer.)

Robin,

you really do get to the core of the matter so much better than me!

-Chris

···

--
Christopher Barker, Ph.D.
Oceanographer
                                         
NOAA/OR&R/HAZMAT (206) 526-6959 voice
7600 Sand Point Way NE (206) 526-6329 fax
Seattle, WA 98115 (206) 526-6317 main reception

Chris.Barker@noaa.gov

Chris Barker wrote:

Robin Dunn wrote:

Change all calls to theWindow.UpdateDrawing() in the worker threads to wx.CallAfter(theWindow.UpdateDrawing) This will cause all the UpdateDrawing calls to happen in the main thread, (and you can get rid of the mutex. since it won't be needed any longer.)

Robin,

you really do get to the core of the matter so much better than me!

It's a necessary skill when you have to read hundreds and type dozens of emails per day. :wink:

···

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!

Chris Barker wrote:

I've never used a mutex, and a quick scan of the mutex docs in the library reference makes no mention of mutex.acquire(), so I'm at a loss. Have you subclassed or written your own mutex code?

It's the standard threading.Lock object. Traditional CS lingo is "mutex", meaning mutual exclusion, although "lock" is probably more intuitive. Sorry for the confusion.

You also might try explicitly deleting the dc before calling
self.drawing_mutex.release(). If mutex.release() immediately allows the next code in the queue to run, then the dc might not be deleted yet. The way it's written, it won't get deleted until the end of the UpdateDrawing Method.

>

What wx.BufferedDC does is create a wx.MemoryDC, write to it, then, when it is destroyed, it copies the MemDC to the mainDC, and then deletes it.

so try:
del dc
self.drawing_mutex.release()

This seems to fix the problem! I had previously tried dc.Destroy(), but that caused the app to freeze and not draw anything.

You still might have a problem, because I'm not sure if the C++ object gets deleted as soon as the Python object that wraps it does.

Did you try:

dc.SelectObject(wx.NullBitmap)
self.drawing_mutex.release()

This also seems to stop the exception, except the drawing isn't immediately draw to the screen. Minimizing then maximizing shows the current drawing.

let us know how it works out,

-Chris

Thanks for your help.

Alex Zeiger wrote:

Did you try:

dc.SelectObject(wx.NullBitmap)
self.drawing_mutex.release()

This also seems to stop the exception, except the drawing isn't immediately draw to the screen.

On thinking about it, I'm not surprised. It won't draw to the wcreen until the BufferedDC is deleted.

You should probably go with Robin's suggestion of wx.CallAfter() anyway. It should be safer.

-Chris

···

--
Christopher Barker, Ph.D.
Oceanographer
                                         
NOAA/OR&R/HAZMAT (206) 526-6959 voice
7600 Sand Point Way NE (206) 526-6329 fax
Seattle, WA 98115 (206) 526-6317 main reception

Chris.Barker@noaa.gov