I used to use pygame, but outdated SDL problems resulting in malfunctioning touchscreen forced me to use a different solution. Touchscreen works with python-evdev and I needed graphics. Cairo is the obvious choice for 2D and after a few days of playing with different solutions I came up with this:

#Very simple way of accessing PiTFT 240x320 display from python with cairo

import mmap
import cairo

# 320 * 240 * 2 bytes per pixel = 153600
PiTFT_mem_size = 153600

# Framebuffer file, might be /dev/fb0
fb_fd  = open("/dev/fb1", "r+")

#Map the framebuffer to memory
fb_map = mmap.mmap(fb_fd.fileno(), PiTFT_mem_size)

#Create cairo surface. If you get "Function not implemented" error, you need a newer cairo
surface = cairo.ImageSurface.create_for_data(fb_map, cairo.FORMAT_RGB16_565, 240, 320)

#Create cairo context
cr = cairo.Context(surface)

# Paint the surface black
cr.set_source_rgba (0.0, 0.0, 0.0, 1.);
cr.paint_with_alpha (1.0);

for i in range(0, 10):
    c = i / 10.0
    cr.set_source_rgb (1.0 - c, 1.0 - c, 1.0)
    cr.rectangle(15 * i + 10, 15 * i + 20, 100, 100)
    cr.fill ()

#Close the mapped framebuffer
fb_map.close()

That's how it looks on the piTFT:

piTFT_and_cairo.jpg
piTFT_and_cairo.jpg, Jul 2018