Follow-the-pointer mini screencast Python app for Ubuntu

I had this idea for a “screencast” which follows the mouse pointer around, rather than making you nominate a fixed screen area; ideal for little demo animated GIFs of how to do a thing on websites. No existing screencast app seems to do this, so I threw a quick thing together to do it for me. Python (because that’s what I use for native apps that can’t be done in pure QML) and Gtk (because there’s no point in using Qt for this since the Python bindings are weird, I use Qt/QML for Ubuntu SDK apps but this can’t be done there anyway because the phone is Mir rather than X and app confinement will prevent screenshots anyway).

Lots of hardcoded things, so it’s not a proper useful app, but it works for what I needed it for. Note that it contains a hardcoded mouse pointer image, because getting the mouse pointer image is insane and requires you to talk directly to X, which is possible with Python but needs loads of extra libraries, and look life’s just too short what are you thinking. Also uses modern Gdk and GI, not pygtk which is ancient and yet what all the existing posted code samples are. Probably should use Cairo from GI too but cairo.CONTENT_COLOR doesn’t seem to exist there. Anyway, if you need to take screenshots from Python using modern Gtk, or you want to do a little animated gif screencast which follows the mouse around, here you go.

import time
from gi.repository import Gdk
import cairo
from PIL import Image
from PIL.GifImagePlugin import getheader, getdata
import StringIO


def shot(width, height):
    # Take a screenshot
    t = time.time()
    _, mousex, mousey, _ = w.get_pointer()
    s=Gdk.Window.create_similar_surface(w, cairo.CONTENT_COLOR, width, height)
    Gdk.cairo_set_source_window(ctx,w,-mousex + (width/2),-mousey + (height/2))
    return (t, s)

WIDTH = 300
HEIGHT = 150
FPS = 25
surfaces = []
start = time.time()
last = time.time()
while (time.time() - start) < SECONDS_DURATION:
    while (time.time() - last) < (1.0/FPS):
    last = time.time()

# now, convert each surface to a PIL PNG then write as a gif

buffer = StringIO.StringIO()
cursor_image =
fp = open("anim.gif", "wb")
previous = None
for t, surface in surfaces:
    sio = StringIO.StringIO()
    im =
    im.paste(cursor_image, (WIDTH/2, HEIGHT/2), mask=cursor_image)
    im = im.convert('RGB').convert('P', palette=Image.WEB)
    del surface

    # Fixme: add specific delay (from t) for each frame, and add looping
    if not previous:
        for s in getheader(im) + getdata(im):
        for s in getdata(im):
    previous = im.copy()

print "Now optimise with gifsicle -b -O3 anim.gif"

More in the discussion (powered by webmentions)

  • A response was written at Building a... ( The Major: But, Fawlty, how did the starling get in the bar? Basil: No, no, no. You were in the bar. The Major: I was in the bar? Yes! Yes, I was! Bas…
  • A response was written at Building a... ( The Major: But, Fawlty, how did the starling get in the bar? Basil: No, no, no. You were in the bar. The Major: I was in the bar? Yes! Yes, I was! Bas…