简体   繁体   中英

How to markup a transparent pygtk pycairo window and save the combined image?

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()

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM