How to markup a transparent pygtk pycairo window and save the combined image?
Question: I am trying to develop a transparent window on which I can markup ANYTHING
that is on my screen (including dynamic). My final goal is to overlay graphs
in online scientific publications, click and accumulate points along curves
and eventually use generator functions to curve-fit to the points gathered.
This will include putting up lines and axes, tick marks, and other goodies.
However, I am trying to keep the question code very simple.

The code below does the transparency part fairly well (please criticize and
correct). I have done extensive research to understand how to save the
contents of the transparent window, and failed. I have tried to figure out how
to overlay anything at all (drawing primitives) and failed. I seek any and all
advice to move this project forward, and intend to make the final code open
source.

Please help.

    
    
        #!/usr/bin/env python
        """
        trans.py Transparent window with markup capability.
        Goals:
            1. make a transparent window that dynamically updates (working).
            2. draw opaque points, lines, text, and more (unimplemented).
            3. save window overlayed by opaque points to png (unimplemented).
            4. toggle overlay on/off (unimplemented).
            5. make cursor XOR of CROSSHAIR (unimplemented).
        """
    
        import pygtk
        pygtk.require('2.0')
        import gtk, cairo
    
        class Transparency(object):
            def __init__(self, widget, index):
                self.xy = widget.get_size()
                self.cr = widget.window.cairo_create()
                self.index = index
            def __enter__(self):
                self.cr.set_operator(cairo.OPERATOR_CLEAR)
                self.surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, *self.xy)
                self.cr.rectangle(0.0, 0.0, *self.xy)
                self.cr.fill()
                return self.cr, self.surface
            def __exit__( self, exc_type, exc_val, exc_tb ):
                filename = '%08d.png' % (self.index)
                with open(filename, 'w+') as png:
                    self.surface.write_to_png(png)
                    print filename
                self.cr.set_operator(cairo.OPERATOR_OVER)
    
        class Expose(object):
            def __init__(self):
                self.index = 0
            def __call__(self, widget, event):
                with Transparency(widget, self.index) as (cr, surface):
                    # cr and surface are available for drawing.
                    pass
                self.index += 1
    
        def main():
            x, y = 201, 201
            win = gtk.Window(gtk.WINDOW_TOPLEVEL)
            win.connect("destroy", lambda w: gtk.main_quit())
            win.set_decorated(True)
            win.set_app_paintable(True)
            win.set_size_request(x, y)
            win.set_colormap(win.get_screen().get_rgba_colormap())
            win.connect('expose-event', Expose())
            win.realize()
            win.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.DIAMOND_CROSS))
            win.show()
            gtk.main()
    
        if __name__ == "__main__":
            main()
    

UPDATE! Got most of what I needed working except the BIG one. How do you save
the image formed by the combination of the underlying window and the
transparent overlay? Points can be layed down and overlay can be toggled using
keyboard only controls in vi-style. Here is the latest source:

    
    
        #!/usr/bin/env python
        """
        trans.py Transparent window with markup capability.
        Goals:
            1. make a transparent window that dynamically updates (working).
            2. draw opaque points, lines, text, and more (working).
            3. save window overlayed by opaque points to png (unimplemented).
            4. toggle overlay on/off (working).
            5. make cursor XOR of CROSSHAIR (using pixel-wise crosshair instead).
            6. enable keyboard input in original emacs function table style (working).
        """
    
        import pygtk
        pygtk.require('2.0')
        import gtk, cairo
        from math import pi
    
        class Transparency(object):
            index = 0
            def __init__(self, widget):
                self.xy = widget.get_size()
                self.cr = widget.window.cairo_create()
                self.storing = False
            def __enter__(self):
                self.cr.set_operator(cairo.OPERATOR_CLEAR)
                self.surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, *self.xy)
                self.cr.rectangle(0.0, 0.0, *self.xy)
                self.cr.fill()
                return self.cr, self.surface
            def __exit__( self, exc_type, exc_val, exc_tb ):
                if self.storing:
                    filename = '%08d.png' % (Transparency.index)
                    with open(filename, 'w+') as png:
                        self.surface.write_to_png(png)
                        print filename
                self.cr.set_operator(cairo.OPERATOR_OVER)
                Transparency.index += 1
    
        class Expose(object):
            def __init__(self, window, xy):
                self.keep, self.points, self.bare = False, set(), False
                self.window = window
                self.X, self.Y = self.xy = xy
                self.x1, self.y1 = self.x0, self.y0 = self.xy[0]/2, self.xy[1]/2
                self.window.connect("key_press_event", self.key_press_event)
                self.window.set_events(gtk.gdk.KEY_PRESS_MASK)
                self.window.set_flags(gtk.HAS_FOCUS | gtk.CAN_FOCUS)
                self.window.grab_focus()
                # function table for keyboard driving
                self.function = [[self.noop for a in range(9)] for b in range(256)]
                self.function[ord('q')][0] = self.quit  # q for quit
                self.function[ord('h')][0] = self.lf    # h for left  (vi-style)
                self.function[ord('j')][0] = self.dn    # j for down  (vi-style)
                self.function[ord('k')][0] = self.up    # k for up    (vi-style)
                self.function[ord('l')][0] = self.rt    # l for right (vi-style)
                self.function[ord('h')][2] = self.lf    # h for left  (vi-style) with point
                self.function[ord('j')][2] = self.dn    # j for down  (vi-style) with point
                self.function[ord('k')][2] = self.up    # k for up    (vi-style) with point
                self.function[ord('l')][2] = self.rt    # l for right (vi-style) with point
                self.function[ord('.')][0] = self.mark  # . for point
                self.function[ord(',')][0] = self.state # , to toggle overlay
            def __call__(self, widget, event):
                self.xy = widget.get_size()
                self.x0, self.y0 = self.xy[0]/2, self.xy[1]/2
                with Transparency(widget) as (cr, surface):
                    if not self.bare:
                        self.point(    cr, surface)
                        self.aperture( cr, surface)
                        self.positions(cr, surface)
                        self.crosshair(cr, surface)
            def aperture(self, cr, surface):
                    cr.set_operator(cairo.OPERATOR_OVER)
                    cr.set_source_rgba(0.5,0.0,0.0,0.5) # dim red transparent
                    cr.arc(self.x0, self.y0, self.x0, 0, pi*2)
                    cr.fill()
                    return self
            def position(self, cr, surface, x, y, chosen):
                cr.set_operator(cairo.OPERATOR_OVER)
                #r, g, b, a = (0.0,0.0,0.0,1.0) if chosen else (0.0,0.0,1.0,0.5)
                r, g, b, a = (0.0,0.0,0.0,1.0)
                cr.set_source_rgba(r,g,b,a)
                cr.rectangle(x, y, 1, 1)
                cr.fill()
            def crosshair(self, cr, surface):
                for dx, dy in [(-2,-2),(-1,-1),(1,1),(2,2),(-2,2),(-1,1),(1,-1),(2,-2)]:
                    x, y = self.x1 + dx, self.y1 + dy
                    if 0       0))
            def dn(self, c, n): self.newxy(0, +int(self.y1       0), 0)
            def rt(self, c, n): self.newxy(+int(self.x1  127 else key
            def key_press_event(self, widget, event):
                keyname = gtk.gdk.keyval_name(event.keyval)
                mask = (1*int(0 != (event.state&gtk.gdk.;  SHIFT_MASK))+
                        2*int(0 != (event.state&gtk.gdk.CONTROL;_MASK))+
                        4*int(0 != (event.state&gtk.gdk.;   MOD1_MASK)))
                self.keep = 0 != (mask & 2)
                self.function[self.accept(event.keyval)][mask](keyname, event.keyval)
                self(widget, event)
                return True
    
        def main():
            x, y = xy = [201, 201]
            window = gtk.Window(gtk.WINDOW_TOPLEVEL)
            window.connect("destroy", gtk.main_quit)
            window.set_decorated(True)
            window.set_app_paintable(True)
            window.set_size_request(x, y)
            window.set_colormap(window.get_screen().get_rgba_colormap())
            window.connect('expose-event', Expose(window, xy))
            window.realize()
            window.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.DIAMOND_CROSS))
            window.show()
            gtk.main()
    
        if __name__ == "__main__":
            main()
    


Answer: This does not answer your specific question but I figure: why re-invent the
wheel? My suggestion is that you use an advanced screenshot application like
Shutter to capture windows or selections. Shutter will provide you with a
browseable gallery of your "saved" windows with the ability to edit and web-
publish, as well as automatically storing images in dedicated folders (by
project, desired resolution, etc) according to user defined profiles.

