wx

Thanks for the pointers.

> I made some progress!

[snup]

> The bug was a line I introduced when I added some more print
> debugs

I had fixed this one...

> The front end change that is causing your problems is that I now pass
> a gcedge and a gcface. I used to pass a gcface and a face color.
> Maybe I'll revert to that.
>
> When I instantiate a gcedge and a gcface on the front end, you make 2
> calls to new_gc
>
> def new_gc(self):
> """
> Return an instance of a GraphicsContextWx, and sets the current
> gc copy """
> DEBUG_MSG('new_gc()', 2, self)
> self.gc = GraphicsContextWx(self.bitmap, self)
> assert self.gc.Ok(), "wxMemoryDC not OK to use"
> return self.gc
>
> I think initing 2 GCs with the same bitmap is causing the problem. If
> you see an easy backend solution, that would be ideal. If not, I
> could re-refactor the frontend to be like it was: pass a gcedge and a
> facecolor.

This is, indeed, illegal in wx. It sometimes works under Windows, but
consistently fails (dramatically) under Linux (at least for wxGtk).

To be more exact about what happens, here is the synopsis:

In patches.py, Patch._draw() instantiates two gc instances

    def _draw(self, renderer, *args, **kwargs):

#here=============>
        gc = renderer.new_gc()
        gc.set_foreground(self._edgecolor)
        gc.set_linewidth(self._linewidth)
        gc.set_alpha(self._alpha)
        if self._clipOn:
            gc.set_clip_rectangle(self.bbox.get_bounds())
        if not self.fill: gcFace = None
        else:
#and here===========>
            gcFace = renderer.new_gc()
            gcFace.set_foreground(self._facecolor)
            gcFace.set_alpha(self._alpha)
        self._derived_draw(renderer, gc, gcFace)

Each call to backend_wx.py RendererWx.new_gc() does the following:

        self.gc = GraphicsContextWx(self.bitmap, self)
        assert self.gc.Ok(), "wxMemoryDC not OK to use"
        return self.gc

But instantiating a GraphicsContextWx does:

    def __init__(self, bitmap, renderer):
        GraphicsContextBase.__init__(self)
        wxMemoryDC.__init__(self)
        DEBUG_MSG("__init__()", 1, self)
        # Make sure (belt and braces!) that existing wxDC is not selected to
        # to a bitmap.
        if GraphicsContextWx._lastWxDC != None:
#the gcEdge gets selected into a NULL bitmap here
            GraphicsContextWx._lastWxDC.SelectObject(wxNullBitmap)
        self.SelectObject(bitmap)
        self.SetPen(wxPen('BLACK', 1, wxSOLID))
        self._style = wxSOLID
        self.renderer = renderer
        GraphicsContextWx._lastWxDC = self

You will note that in order to enforce the wx rule that no more than one
wxMemoryDC can be selected into a bitmap, I ensure that the last wxMemoryDC
instance activated is selected to a wxNullBitmap.

In the case of the above code, this means that the gc representing the
rectangle has already been selected to a NULL bitmap. When you try to draw to
this, an exception is generated within the C++ code which is not propagated
back up to the Python layer. Unfortunately, the assert() calls don't help
much here as each gc instance is OK when it is constructed.

I'm afraid I'd missed the significance of the change from a facecolor to a
gcFace for fill colours.

I'll see if I can come up with any ideas, but passing a gcedge and
facecolor is likely to be simpler to get to work reliably.

I have thought about this, and to only other option I can think of is for me
to select between 'currently active' gc instances as required. Obviously,
this would probably slow backend_wx down, but I'm not yet sure what the
performance hit will be. Let me try it...

[an hour or so later...]

Attached is a backend_wx which seems to work with most of the examples I've
tried (Linux only - expect Windows to work as it's generally better behaved
than wxGtk, but would be nice if someone would try it.

Some issues (for now):
- Clipping seems to be broken (c.f. arctest.py)
- Object picker demo doesn't work (haven't implemented this functionality)
- Some types of dotted lines are not working properly (c.f. subplot_demo.py)
- embedding_in_wx doesn't work - due to some of the recent API changes. I'll
fix this one pretty soon - it shouldn't be much work.
- It's about 20% slower than the previous implementation in which only one GC
was active at a time.

Not sure if the dotted lines and clipping are down to information being lost
when I switch between gcs - I'll have to look into this, but the attached
will be a good start for getting things to work on wx again.

John, I'll leave it as your call whether you want to switch the API back to
using a facecolor instead of a gcFace. It would be better from the wx
perspective, but there may be good reasons for doing otherwise on other
backends.

There are a couple of optimisations I can probably make to speed things a
little if we stick with the current API -it should be possible to do
something slightly more intelligent than simply selecting a bitmap at the
start of each call, and deselecting it afterwards - although, as noted above,
wxMemoryDC seems to be extremely fragile under wxGtk - not sure why.

I have checked the attached into CVS for now, but we may have the usual
'mirror update' issue.

Regards

Jeremy

backend_wx.py (54 KB)

ยทยทยท

On Saturday 14 February 2004 8:03 am, you wrote:

On Friday 13 February 2004 9:20 pm, you wrote: