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 python3
  2"""Based on cairo-demo/X11/cairo-demo.c."""
  3
  4import cairo
  5import gi
  6
  7gi.require_version("Gtk", "4.0")
  8from gi.repository import Gtk
  9
 10SIZE = 30
 11
 12
 13class Application(Gtk.Application):
 14    def do_activate(self):
 15        window = Gtk.ApplicationWindow(
 16            application=self, default_width=450, default_height=600
 17        )
 18
 19        drawing_area = Gtk.DrawingArea()
 20        drawing_area.set_draw_func(self.draw)
 21        window.set_child(drawing_area)
 22
 23        window.present()
 24
 25    def triangle(self, ctx):
 26        ctx.move_to(SIZE, 0)
 27        ctx.rel_line_to(SIZE, 2 * SIZE)
 28        ctx.rel_line_to(-2 * SIZE, 0)
 29        ctx.close_path()
 30
 31    def square(self, ctx):
 32        ctx.move_to(0, 0)
 33        ctx.rel_line_to(2 * SIZE, 0)
 34        ctx.rel_line_to(0, 2 * SIZE)
 35        ctx.rel_line_to(-2 * SIZE, 0)
 36        ctx.close_path()
 37
 38    def bowtie(self, ctx):
 39        ctx.move_to(0, 0)
 40        ctx.rel_line_to(2 * SIZE, 2 * SIZE)
 41        ctx.rel_line_to(-2 * SIZE, 0)
 42        ctx.rel_line_to(2 * SIZE, -2 * SIZE)
 43        ctx.close_path()
 44
 45    def inf(self, ctx):
 46        ctx.move_to(0, SIZE)
 47        ctx.rel_curve_to(0, SIZE, SIZE, SIZE, 2 * SIZE, 0)
 48        ctx.rel_curve_to(SIZE, -SIZE, 2 * SIZE, -SIZE, 2 * SIZE, 0)
 49        ctx.rel_curve_to(0, SIZE, -SIZE, SIZE, -2 * SIZE, 0)
 50        ctx.rel_curve_to(-SIZE, -SIZE, -2 * SIZE, -SIZE, -2 * SIZE, 0)
 51        ctx.close_path()
 52
 53    def draw_shapes(self, ctx, x, y, fill):
 54        ctx.save()
 55
 56        ctx.new_path()
 57        ctx.translate(x + SIZE, y + SIZE)
 58        self.bowtie(ctx)
 59        if fill:
 60            ctx.fill()
 61        else:
 62            ctx.stroke()
 63
 64        ctx.new_path()
 65        ctx.translate(3 * SIZE, 0)
 66        self.square(ctx)
 67        if fill:
 68            ctx.fill()
 69        else:
 70            ctx.stroke()
 71
 72        ctx.new_path()
 73        ctx.translate(3 * SIZE, 0)
 74        self.triangle(ctx)
 75        if fill:
 76            ctx.fill()
 77        else:
 78            ctx.stroke()
 79
 80        ctx.new_path()
 81        ctx.translate(3 * SIZE, 0)
 82        self.inf(ctx)
 83        if fill:
 84            ctx.fill()
 85        else:
 86            ctx.stroke()
 87
 88        ctx.restore()
 89
 90    def fill_shapes(self, ctx, x, y):
 91        self.draw_shapes(ctx, x, y, True)
 92
 93    def stroke_shapes(self, ctx, x, y):
 94        self.draw_shapes(ctx, x, y, False)
 95
 96    def draw(self, da, ctx, width, height):
 97        ctx.set_source_rgb(0, 0, 0)
 98
 99        ctx.set_line_width(SIZE / 4)
100        ctx.set_tolerance(0.1)
101
102        ctx.set_line_join(cairo.LINE_JOIN_ROUND)
103        ctx.set_dash([SIZE / 4.0, SIZE / 4.0], 0)
104        self.stroke_shapes(ctx, 0, 0)
105
106        ctx.set_dash([], 0)
107        self.stroke_shapes(ctx, 0, 3 * SIZE)
108
109        ctx.set_line_join(cairo.LINE_JOIN_BEVEL)
110        self.stroke_shapes(ctx, 0, 6 * SIZE)
111
112        ctx.set_line_join(cairo.LINE_JOIN_MITER)
113        self.stroke_shapes(ctx, 0, 9 * SIZE)
114
115        self.fill_shapes(ctx, 0, 12 * SIZE)
116
117        ctx.set_line_join(cairo.LINE_JOIN_BEVEL)
118        self.fill_shapes(ctx, 0, 15 * SIZE)
119        ctx.set_source_rgb(1, 0, 0)
120        self.stroke_shapes(ctx, 0, 15 * SIZE)
121
122
123app = Application()
124app.run()