matplotlib - wx backend empty canvas

Hi,

I have been digging a bit on the matplotlib issues.

The problem when using the 'wx' backend, instead of the 'wxagg' backend is that for some reason this is not working (as of wxPython 2.9).

In FigureCanvasWx.gui_repaint we have this call:

             drawDC.DrawBitmap(self.bitmap, 0, 0)

If I set a breakpoint and then do 'self.bitmap.SaveFile' I see the information which should be shown on the screen, but the screen is showing an empty canvas instead and after the above is called we go into an 'idle' state, so I don't think it is being cleared later on.

Werner

Searching more I came across this:
So, to test this I tried this in gui_repaint:
mdc = wx.MemoryDC()
mdc.SelectObject(self.bitmap)
and I do get an exception:
Am I right to now think that ‘drawDC.DrawBitmap(self.bitmap, 0, 0)’
is silently failing (only on Windows with wxPython 2.9+) because the
bitmap is still held by something?
I searched but GraphicsContextWx is always doing a
“SelectObject(wx.NullBitmap)” after it used the dc, could that
somehow silently fail or what else might hang on to the bitmap?
Werner

···

On 9/25/2014 15:40, Werner wrote:

  Hi,




  I have been digging a bit on the matplotlib issues.




  The problem when using the 'wx' backend, instead of the 'wxagg'

backend is that for some reason this is not working (as of
wxPython 2.9).

  In FigureCanvasWx.gui_repaint we have this call:




              drawDC.DrawBitmap(self.bitmap, 0, 0)




  If I set a breakpoint and then do 'self.bitmap.SaveFile' I see the

information which should be shown on the screen, but the screen is
showing an empty canvas instead and after the above is called we
go into an ‘idle’ state, so I don’t think it is being cleared
later on.

http://wiki.wxpython.org/wxPython%20Platform%20Inconsistencies#wxDC.DrawBitmap.28.29

  wx._core.PyAssertionError:

C++ assertion “Assert failure” failed at
…..\src\msw\dcmemory.cpp(130) in wxMemoryDCImpl::DoSelect():
Couldn’t select a bitmap into wxMemoryDC

Hi,

It seems that it is a problem that dc.SelectObject(wx.NullBitmap) is not working correctly on 2.9+ and Windows.

In gui_repaint instead of doing:

drawDC.DrawBitmap(self.bitmap, 0, 0)

doing this:

             img = self.bitmap.ConvertToImage()
             bmp = img.ConvertToBitmap()
             drawDC.DrawBitmap(bmp, 0, 0)

makes it work.

I'll try to make a minimal sample to show the problem and then do a trac ticket.

Werner

Doing the minimal sample I narrowed it down to mpl using '_cache = weakref.WeakKeyDictionary()' which stores the 'MemoryDC' and the 'GraphicsContext' and in the minimal sample if I move the line dc.SelectObject to below the line where the cache is updated the problem goes away.

But the same trick doesn't do it in mpl - anyone any ideas?

Werner

dcselectproblem.py (1.87 KB)

···

On 9/26/2014 12:22, Werner wrote:

Hi,

It seems that it is a problem that dc.SelectObject(wx.NullBitmap) is not working correctly on 2.9+ and Windows.

In gui_repaint instead of doing:

drawDC.DrawBitmap(self.bitmap, 0, 0)

doing this:

            img = self.bitmap.ConvertToImage()
            bmp = img.ConvertToBitmap()
            drawDC.DrawBitmap(bmp, 0, 0)

makes it work.

I'll try to make a minimal sample to show the problem and then do a trac ticket.

Werner,

Thanks for digging into this. Sorry I don’t have more time to look at the MPL code right now but an idea:

drawDC.DrawBitmap(self.bitmap, 0, 0)

There are indeed issues with doing that is self.bitmap is selected into a DC at the time. Is that the issue at hand? My understanding is that DCs are not designed to be “kept around”, ie. you really want to create the DC, use it, then delete it, right in the same code.

IN practice, I’ve never seen keeping a DC around cause any problems other than this one.

So if it’s ingrained into the MPL wx back-end to keep that MemoryDC around then the alternative is to use it, instead of teh DrawBitmap call, somethign like:

drawDC.Blit(0, 0, self.width, self.height, the_memoryDC)

Assuming you have access to the memoryDC in this part of the code.

-Chris

···

On Fri, Sep 26, 2014 at 5:00 AM, Werner wernerfbd@gmx.ch wrote:

Hi,

It seems that it is a problem that dc.SelectObject(wx.NullBitmap) is not working correctly on 2.9+ and Windows.

In gui_repaint instead of doing:

drawDC.DrawBitmap(self.bitmap, 0, 0)

doing this:

        img = self.bitmap.ConvertToImage()

        bmp = img.ConvertToBitmap()

        drawDC.DrawBitmap(bmp, 0, 0)

makes it work.

I’ll try to make a minimal sample to show the problem and then do a trac ticket.

On 9/26/2014 12:22, Werner wrote:

Doing the minimal sample I narrowed it down to mpl using ‘_cache = weakref.WeakKeyDictionary()’ which stores the ‘MemoryDC’ and the ‘GraphicsContext’ and in the minimal sample if I move the line dc.SelectObject to below the line where the cache is updated the problem goes away.

But the same trick doesn’t do it in mpl - anyone any ideas?

Werner

You received this message because you are subscribed to the Google Groups “wxPython-users” group.

To unsubscribe from this group and stop receiving emails from it, send an email to wxpython-users+unsubscribe@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

Christopher Barker, Ph.D.
Oceanographer

Emergency Response Division
NOAA/NOS/OR&R (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

It is over my head but I like challenges:)
I narrowed it down to the MemoryDC which is used in
backend_wx.GraphicsContextWx, there the DC is assigned to ‘gfx_ctx =
wx.GraphicsContext.Create(dc)’, and then ‘gfx_ctx’ is kept in a
cache within that GraphicsContextWx class "_cache =
weakref.WeakKeyDictionary()'.
It is strange that this did not cause a problem in 2.8, and that it
fails silently in 2.9+ (only on Windows), I think it should give an
error/warning.
It is accessible, but not that nicely ‘dc, gc =
self.renderer.gc._cache.pop(self.bitmap)’.
I asked the question on the PR why MPL is keeping a cache of the
GC’s, in my tests there is always only one Bitmap in play and
therefore only one cache entry. So I tried to get rid of it by
changing ‘RendererWx.new_gc’ to only create a new GC if there isn’t
one and just return the existing one. The graph shows but can’t get
the tick labels to show for some reason.
Werner

···

Hi Chris,

  On 9/26/2014 23:55, Chris Barker wrote:

Werner,

Thanks for digging into this.

      Sorry I don't have more time to look at the MPL code right

now but an idea:

        drawDC.DrawBitmap(self.bitmap,

0, 0)

      There are indeed issues with doing that is self.bitmap is

selected into a DC at the time. Is that the issue at hand? My
understanding is that DCs are not designed to be “kept
around”, ie. you really want to create the DC, use it, then
delete it, right in the same code.

      IN practice, I've never seen keeping a DC around cause any

problems other than this one.

      So if it's ingrained into the MPL wx back-end to keep that

MemoryDC around then the alternative is to use it, instead of
teh DrawBitmap call, somethign like:

drawDC.Blit(0, 0, self.width, self.height, the_memoryDC)

      Assuming you have access to the memoryDC in this part of

the code.

Sorry to jump in not having tested the code (btw how would I reproduce the actual mpl error?), but is it possible to simply get rid of the storing and retrieving of the DC, and just create a new one or queue operations until the OnPaint method is called?

···

On Saturday, September 27, 2014 1:39:40 AM UTC-7, werner wrote:

Hi Chris,

  On 9/26/2014 23:55, Chris Barker wrote:

Werner,

Thanks for digging into this.

It is over my head but I like challenges:)

      Sorry I don't have more time to look at the MPL code right

now but an idea:

        drawDC.DrawBitmap(self.bitmap,

0, 0)

      There are indeed issues with doing that is self.bitmap is

selected into a DC at the time. Is that the issue at hand? My
understanding is that DCs are not designed to be “kept
around”, ie. you really want to create the DC, use it, then
delete it, right in the same code.

I narrowed it down to the MemoryDC which is used in

backend_wx.GraphicsContextWx, there the DC is assigned to ‘gfx_ctx =
wx.GraphicsContext.Create(dc)’ , and then ‘gfx_ctx’ is kept in a
cache within that GraphicsContextWx class "_cache =
weakref.WeakKeyDictionary()'.

Run the ‘embedding_in_wx2.py’ which is one of the samples included
with mpl and activate the 'matplotlib.use(‘WX’) lines and comment
the ‘WXAgg’ lines. The problem only shows on Windows with wxPython
2.9+, it works with 2.8.
The caching is done for speed optimization (according to the
comment) to handle multiple bitmaps/DC’s/GC’s, in my tests I haven’t
been able to come up with a case where multiple cache entries are
created.
I tried getting rid of the caching and just returning the existing
one when needed (which is many times for a simple graph like the one
in the example), and then deleting it from within the OnPaint event,
but couldn’t get that to work fully. A work around is to pop the bitmap etc from the cache in the OnPaint
event, that works but feels like a hack.
Werner

···

Hi Nathan,

  On 9/29/2014 8:09, Nathan McCorkle wrote:
    On Saturday, September 27, 2014 1:39:40 AM UTC-7, werner wrote:

Hi Chris,

          On 9/26/2014 23:55, Chris Barker wrote:

Werner,

Thanks for digging into this.

It is over my head but I like challenges:)

              Sorry I don't have more time to look at the MPL

code right now but an idea:

                drawDC.DrawBitmap(self.bitmap,

0, 0)

              There are indeed issues with doing that is

self.bitmap is selected into a DC at the time. Is that
the issue at hand? My understanding is that DCs are
not designed to be “kept around”, ie. you really want
to create the DC, use it, then delete it, right in the
same code.

        I narrowed it down to the MemoryDC which is used in

backend_wx.GraphicsContextWx, there the DC is assigned to
‘gfx_ctx = wx.GraphicsContext.Create(dc)’ , and then
‘gfx_ctx’ is kept in a cache within that GraphicsContextWx
class "_cache = weakref.WeakKeyDictionary()'.

      Sorry to jump in not having tested the code (btw how would

I reproduce the actual mpl error?),

      but is it possible to simply get rid of the storing and

retrieving of the DC, and just create a new one or queue
operations until the OnPaint method is called?