Dynamic Drawing with Cairo and wxPython

though I'm noticing a big caveat with wxPython that I'm having a hard time
working around. I'm am fairly new to wxPython, and very new to Cairo, so the
answer may be obvious, but I can't seem to find it anywhere.

The problem I'm running into is that it seems that you are only able to draw
on the graphics context (wx.GraphicsContext, I believe) when wx.EVT_PAINT is
called, though I need external classes to be able to draw to the canvas
easily.

I think you do need a GraphicsContext, but it does not need to be
generated during a Paint event -- that's only one option.

What you probably want to do be doing your drawing to an off-screen
bitmap. Then your EVT_PAINT handler will simply blit that to the
screen (see wx.BufferedPaintDC -- or something like that...)

My original plan was to create the Cairo context then store it in the class
that the canvas is on, then any class that needs access to the context to
draw would simple get it using something like canvas.ctx.

I"m not sure if a Cairo Context is something you want to keep around,
you may need to create it when you draw, but:

you can create a wx.MemoryDC that connects to your bitmap, then
connect the Graphics context to that when you want to draw, something
like:

dc = wx.MemoryDC()
dc.SelectObject(My_wxBitmap)
gc = wx.GraphicsContext.Create(dc)
nc = gc.GetNativeContext()
ctx = CairoAPI.Context_FromSWIGObject(nc)

Note that on Windows at least (a couple years ago, anyway...) you
couldn't do anything else with that Bitmap while it was selected in
the DC -- so It's probably best to go through that while procedure
each time you want to update the drawing -- i.e. the wx.Bitmap is the
only thing you persist.

See the Double BufferedDrawing page in the Wik for more detail (though
you'll need to adapt it for Cairo)

This is very much a wxPython question -- not a Cairo one!

NOTE: another option would be to use Cairo directly rather than
through wxGraphicsContext -- in that case, you'd do whatever you need
to do to draw to an in memory bitmap with Cairo, then, in your Paint
Handler, figure out how to grab that bitmap and blit it to the screen
-- it should be doable.

Oh, any maybe check out wx.lib.floatcanvas -- it would be cool to
adapt that for Cairo...

-Chris

···

On Tue, Jul 24, 2012 at 4:43 PM, Jeremy <jeremyoverman@overmanscomputers.com> wrote:

--

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

Having done a bit of Cairo (having authored the Smalltalk Cairo bindings), I can confirm that the "advised" usage pattern is to treat cr contexts as transient. You create one to draw with, you draw, you dump it. Think of it as transactional. In the case of a direct window draw, that means on each paint/damage event. It all works really well this way. Doing it other ways creates interesting effects. Creating and destroying them is very fast/easy, it's not like creating a lot of other windows "resources" where creating/deleting has the assumed heavy overhead.

Travis Griggs
"A vital ingredient of success is not knowing that what you're attempting can't be done." -- Sir Terry Pratchett

···

On Jul 24, 2012, at 10:02 PM, Chris Barker wrote:

On Tue, Jul 24, 2012 at 4:43 PM, Jeremy > <jeremyoverman@overmanscomputers.com> wrote:

though I'm noticing a big caveat with wxPython that I'm having a hard time
working around. I'm am fairly new to wxPython, and very new to Cairo, so the
answer may be obvious, but I can't seem to find it anywhere.

The problem I'm running into is that it seems that you are only able to draw
on the graphics context (wx.GraphicsContext, I believe) when wx.EVT_PAINT is
called, though I need external classes to be able to draw to the canvas
easily.

I think you do need a GraphicsContext, but it does not need to be
generated during a Paint event -- that's only one option.

What you probably want to do be doing your drawing to an off-screen
bitmap. Then your EVT_PAINT handler will simply blit that to the
screen (see wx.BufferedPaintDC -- or something like that...)

My original plan was to create the Cairo context then store it in the class
that the canvas is on, then any class that needs access to the context to
draw would simple get it using something like canvas.ctx.

I"m not sure if a Cairo Context is something you want to keep around,
you may need to create it when you draw, but: