Friday, March 27, 2009

Integrating Cairo into wxWidgets...

Some python code to integrate Cairo into wxWidgets:

This goes into the top of your code:


#----------------------------------------------------------------------
# The Cairo interface.
import wx.lib.wxcairo

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):
"""
(Internal)
get the Cairo object for drawing.
"""
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)


Then inside your drawing class you will need to add an OnPaint event:


def OnPaint(self, event):
"""
Call to get Cairo to paint, this is done automatically if bound
to a Panel.
"""
try:
dc = wx.PaintDC(self.panel) # make each time OnPaint is called.
#dc = wx.AutoBufferedPaintDC(self) # not clear why this doesn't work ...
self.size = dc.GetSizeTuple()
self.setViewPortSize(self.size[0], self.size[1])
gc = wx.GraphicsContext.Create(dc)
nc = gc.GetNativeContext()
ctx = Context_FromSWIGObject(nc)
except:
raise ErrorCairoAcquireDevice

self._paint(ctx) # actual paint calls go here.
return(True)


I found it useful to bind all of this into a panel on my wxWidgets GUI. I have a panel in the GUI already, then I bind another panel on top of that panel.
My panel needs a few special options (see below), so I can't just bind it into that panel.


def bindPanel(self, panel):
"""
bind a wx.Panel into gDraw.
"""
self.panel = drawPanel(panel, self.OnPaint)

return(self.panel)


And this is my drawPanel class, which I used to bind the events into and exists somewhere were I need it in the GUI.


class drawPanel(wx.Panel):
"""
An empty Panel class that sets things up suitably for Cairo.
"""
def __init__(self, parent, paint_Procedure):
# This has to be set to full repaint to work,
# otherwise it will try to repaint only small parts.
wx.Panel.__init__(self, parent, -1, size=(-1,-1), style=wx.FULL_REPAINT_ON_RESIZE)
self.Bind(wx.EVT_PAINT, paint_Procedure, self)
self.Fit()

Welcome to oAxiom

An occasional spot for me to write stuff I find interesting, and my faltering efforts to find funding for a biotech venture.

I sit at the interface of biology and bioinformatics - I'm in the unique position to be both a fully trained biologist (cell culture, Western blots, all that stuff). And I am also an experienced programmer (Python, C, R).

My project sits at the interface between these two people. As such this blog will contain snippets of code, snippets of biology and a dose of entrepreneurship, as wells as other miscellaneous nonsense.

Welcome to oAxiom, its only employee (if I could call myself that): Andrew Hutchins. I'm in Singapore (currently), and you can contact me at oaxiom@oaxiom.com.