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