Cairo Integration

Despite cairo not being a GObject based library, PyGObject provides special cairo integration through pycairo. Functions returning and taking cairo data types get automatically converted to pycairo objects and vice versa.

Some distros ship the PyGObject cairo support in a separate package. If you’ve followed the instructions on “Getting Started” you should have everything installed.

If your application requires the cairo integration you can use gi.require_foreign():

try:
    gi.require_foreign("cairo")
except ImportError:
    print("No pycairo integration :(")

Note that PyGObject currently does not support cairocffi, only pycairo.

Demo

The following example shows a Gtk.Window with a custom drawing in Python using pycairo.

../_images/cairo_integration.png
  1#!/usr/bin/env python
  2"""
  3Based on cairo-demo/X11/cairo-demo.c
  4"""
  5
  6import cairo
  7import gi
  8gi.require_version("Gtk", "3.0")
  9from gi.repository import Gtk
 10
 11SIZE = 30
 12
 13
 14def triangle(ctx):
 15    ctx.move_to(SIZE, 0)
 16    ctx.rel_line_to(SIZE, 2 * SIZE)
 17    ctx.rel_line_to(-2 * SIZE, 0)
 18    ctx.close_path()
 19
 20
 21def square(ctx):
 22    ctx.move_to(0, 0)
 23    ctx.rel_line_to(2 * SIZE, 0)
 24    ctx.rel_line_to(0, 2 * SIZE)
 25    ctx.rel_line_to(-2 * SIZE, 0)
 26    ctx.close_path()
 27
 28
 29def bowtie(ctx):
 30    ctx.move_to(0, 0)
 31    ctx.rel_line_to(2 * SIZE, 2 * SIZE)
 32    ctx.rel_line_to(-2 * SIZE, 0)
 33    ctx.rel_line_to(2 * SIZE, -2 * SIZE)
 34    ctx.close_path()
 35
 36
 37def inf(ctx):
 38    ctx.move_to(0, SIZE)
 39    ctx.rel_curve_to(0, SIZE, SIZE, SIZE, 2 * SIZE, 0)
 40    ctx.rel_curve_to(SIZE, -SIZE, 2 * SIZE, -SIZE, 2 * SIZE, 0)
 41    ctx.rel_curve_to(0, SIZE, -SIZE, SIZE, - 2 * SIZE, 0)
 42    ctx.rel_curve_to(-SIZE, -SIZE, - 2 * SIZE, -SIZE, - 2 * SIZE, 0)
 43    ctx.close_path()
 44
 45
 46def draw_shapes(ctx, x, y, fill):
 47    ctx.save()
 48
 49    ctx.new_path()
 50    ctx.translate(x + SIZE, y + SIZE)
 51    bowtie(ctx)
 52    if fill:
 53        ctx.fill()
 54    else:
 55        ctx.stroke()
 56
 57    ctx.new_path()
 58    ctx.translate(3 * SIZE, 0)
 59    square(ctx)
 60    if fill:
 61        ctx.fill()
 62    else:
 63        ctx.stroke()
 64
 65    ctx.new_path()
 66    ctx.translate(3 * SIZE, 0)
 67    triangle(ctx)
 68    if fill:
 69        ctx.fill()
 70    else:
 71        ctx.stroke()
 72
 73    ctx.new_path()
 74    ctx.translate(3 * SIZE, 0)
 75    inf(ctx)
 76    if fill:
 77        ctx.fill()
 78    else:
 79        ctx.stroke()
 80
 81    ctx.restore()
 82
 83
 84def fill_shapes(ctx, x, y):
 85    draw_shapes(ctx, x, y, True)
 86
 87
 88def stroke_shapes(ctx, x, y):
 89    draw_shapes(ctx, x, y, False)
 90
 91
 92def draw(da, ctx):
 93    ctx.set_source_rgb(0, 0, 0)
 94
 95    ctx.set_line_width(SIZE / 4)
 96    ctx.set_tolerance(0.1)
 97
 98    ctx.set_line_join(cairo.LINE_JOIN_ROUND)
 99    ctx.set_dash([SIZE / 4.0, SIZE / 4.0], 0)
100    stroke_shapes(ctx, 0, 0)
101
102    ctx.set_dash([], 0)
103    stroke_shapes(ctx, 0, 3 * SIZE)
104
105    ctx.set_line_join(cairo.LINE_JOIN_BEVEL)
106    stroke_shapes(ctx, 0, 6 * SIZE)
107
108    ctx.set_line_join(cairo.LINE_JOIN_MITER)
109    stroke_shapes(ctx, 0, 9 * SIZE)
110
111    fill_shapes(ctx, 0, 12 * SIZE)
112
113    ctx.set_line_join(cairo.LINE_JOIN_BEVEL)
114    fill_shapes(ctx, 0, 15 * SIZE)
115    ctx.set_source_rgb(1, 0, 0)
116    stroke_shapes(ctx, 0, 15 * SIZE)
117
118
119def main():
120    win = Gtk.Window()
121    win.connect('destroy', lambda w: Gtk.main_quit())
122    win.set_default_size(450, 550)
123
124    drawingarea = Gtk.DrawingArea()
125    win.add(drawingarea)
126    drawingarea.connect('draw', draw)
127
128    win.show_all()
129    Gtk.main()
130
131
132if __name__ == '__main__':
133    main()