0
votes

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)
1
I would re-post to the wxPython users group too. The main devs frequent that group and should be able to give you some pointers.Mike Driscoll
Thanks for the response! I'll definatelly do that.Jeremy

1 Answers

0
votes

I did as Mike Driscoll directed me to do (in the comment above) and posted my question on both the wxPython group and the Cairo mailing list, and the response from a member of the Cairo list was:

Have a look at wxWindowDC, wxClientDC.

After looking up these classes, I read:

A wxClientDC must be constructed if an application wishes to paint on the client area of a window from outside an OnPaint event. This should normally be constructed as a temporary stack object; don't store a wxClientDC object.

This is exactly what I was looking for. I hope this helps anyone running into the same problem in the future.

Thank you! Jeremy Overman