Dynamic Drawing with Cairo and wxPython
===

I'm working on a new project that is written using wxPython and Cairo in order to dynamically draw objects on a canvas. Everything works pretty well, 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.

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.

My overall question is, is there any way that I can have access to the Cairo Context without needing wx.EVT_PAINT being calling?

Here's the code I'm working with right now:

    import wx
    import CairoAPI
    
    class Canvas(wx.Frame):
        def __init__(self):
            wx.Frame.__init__(self, None, -1, "test", pos=(0, 0), size=(640,480))
            #self.ShowFullScreen(1)
            self.Bind(wx.EVT_PAINT, self.OnPaint)
    
        def DrawRectangle(self):
            pass
    
        def OnPaint(self, event):
            dc = wx.PaintDC(self)
            w,h = dc.GetSizeTuple()
            gc = wx.GraphicsContext.Create(dc)
            nc = gc.GetNativeContext()
            ctx = CairoAPI.Context_FromSWIGObject(nc)
    
    
    if __name__=="__main__":
    
        app = wx.App()
        canvas = Canvas()
        canvas.Show()
        app.MainLoop()

In this code, what I want to accomplish is to make canvas.DrawRectangle and have the ability to write to the canvas without the function being called from within canvas.OnPaint.

Sorry if I've made this questions confusing. I just started learning Cairo last night and I'm still a little unfamiliar with how it works and the correct terminology for some things.

Thank you!


EDIT: I forgot some code: The "CairoAPI" module is something I got from a tutorial (can't remember which one off hand). Here's the source for it.

    import ctypes
    import cairo
    from ctypes.util import find_library
    
    cairo_dll = ctypes.CDLL(find_library("cairo"))
    
    # Pycairo's API representation (from pycairo.h)
    class Pycairo_CAPI(ctypes.Structure):
       _fields_ = [
          ('Context_Type', ctypes.py_object),
          ('Context_FromContext', ctypes.PYFUNCTYPE(ctypes.py_object,
                                                    ctypes.c_void_p,
                                                    ctypes.py_object,
                                                    ctypes.py_object)),
          ('FontFace_Type', ctypes.py_object),
          ('FontFace_FromFontFace', ctypes.PYFUNCTYPE(ctypes.py_object, ctypes.c_void_p)),
          ('FontOptions_Type', ctypes.py_object),
          ('FontOptions_FromFontOptions', ctypes.PYFUNCTYPE(ctypes.py_object, ctypes.c_void_p)),
          ('Matrix_Type', ctypes.py_object),
          ('Matrix_FromMatrix', ctypes.PYFUNCTYPE(ctypes.py_object, ctypes.c_void_p)),
          ('Path_Type', ctypes.py_object),
          ('Path_FromPath', ctypes.PYFUNCTYPE(ctypes.py_object, ctypes.c_void_p)),
          ('Pattern_Type', ctypes.py_object),
          ('SolidPattern_Type', ctypes.py_object),
          ('SurfacePattern_Type', ctypes.py_object),
          ('Gradient_Type', ctypes.py_object),
          ('LinearGradient_Type', ctypes.py_object),
          ('RadialGradient_Type', ctypes.py_object),
          ('Pattern_FromPattern', ctypes.c_void_p),
          ('ScaledFont_Type', ctypes.py_object),
          ('ScaledFont_FromScaledFont', ctypes.PYFUNCTYPE(ctypes.py_object, ctypes.c_void_p)),
          ('Surface_Type', ctypes.py_object),
          ('ImageSurface_Type', ctypes.py_object),
          ('PDFSurface_Type', ctypes.py_object),
          ('PSSurface_Type', ctypes.py_object),
          ('SVGSurface_Type', ctypes.py_object),
          ('Win32Surface_Type', ctypes.py_object),
          ('XlibSurface_Type', ctypes.py_object),
          ('Surface_FromSurface', ctypes.PYFUNCTYPE(ctypes.py_object, ctypes.c_void_p)),
          ('Check_Status', ctypes.PYFUNCTYPE(ctypes.c_int, ctypes.c_int))]
    
    # look up the API
    ctypes.pythonapi.PyCObject_Import.restype = ctypes.POINTER(Pycairo_CAPI)
    pycairo_api = ctypes.pythonapi.PyCObject_Import("cairo", "CAPI").contents
    
    ContextType = pycairo_api.Context_Type
    
    def Context_FromSWIGObject(swigObj):
        ptr = ctypes.c_void_p(int(swigObj))
        #increment the native context's ref count, since the Pycairo_Context decrements it
        #when it is finalised.
        cairo_dll.cairo_reference(ptr)
        return pycairo_api.Context_FromContext(ptr, ContextType, None)