Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Segmentation fault when using `cairo.Region` in Python3 on Fedora 24

I'm trying to get an input shape to apply to a window, so I can have a transparent frame with only the interesting parts to be clickable, etc.

I'm using Cairo graphics with PyGObject (Python 3).

The Object is a regular Gtk Windows object, which then defines a Cairo drawing area:

class Face(Gtk.Window):
    def __init__(self):
        super(Face, self).__init__()
        self.init_ui()

    def init_ui(self):
        [...]
        self.darea = Gtk.DrawingArea()
        self.darea.connect("draw", self.on_draw)
        self.add(self.darea)

    def on_draw(self, widget, cr):
        [... (drawing a couple shapes into the context)]
        sface = cr.get_group_target()
        mregion = Gdk.cairo_region_create_from_surface(sface)
        # the above line produces the error
        # the following lines is wishful thinking at this point...
        #self.get_window().input_shape_combine_region(mregion, 0, 0)

So, the function on_draw() gets called whenever the draw area has to be redrawn, which is when the window is moved, resized, revealed after being hidden, and so on.

most of the window is actually empty, as it is supposed to be a mostly translucent frame, only visible parts are supposed to be clickable. However, I'm getting the following error:

Traceback (most recent call last):
  File "./lsc.py", line 236, in on_draw
    mregion = Gdk.cairo_region_create_from_surface(sface)
TypeError: Couldn't find foreign struct converter for 'cairo.Region'
python3: cairo-surface.c:953: cairo_surface_destroy: Assertion `CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&surface->ref_count)' failed.
zsh: abort (core dumped)  ./lsc.py

The packages for Python3, the PyGObject and the Cairo libs are installed, and I've also imported both parts of Cairo:

from gi.repository import Gtk, Gdk, cairo
import cairo

At this point I'm not entirely sure, whether this is a Python problem, or an error on my part. I'm unsure if even using the cairo.Region is even applicable like that, the example I'm going by on, is this: http://www.programcreek.com/python/example/81481/cairo.Region


Edit

At this point I'm seriously confused what's going on. I've been looking a bit into this and this is for what what I've found out:

There seems to be some sort of error with PyGI and Cairo.

When I use just the introspection part of cairo:

from gi.repository import Gtk, Gdk, cairo
#import cairo

And then run my script, I get this error:

raceback (most recent call last):
  File "./lsc.py", line 164, in on_draw
    cr.set_operator(cairo.OPERATOR_SOURCE)
  File "/usr/lib64/python3.5/site-packages/gi/module.py", line 139, in __getattr__
    self.__name__, name))
AttributeError: 'gi.repository.cairo' object has no attribute 'OPERATOR_SOURCE'

Obviously I'm using cairo.OPERATOR_SOURCE in my code to draw to a cairo context. However the introspected package doesn't even seem to contain te operators used to draw on a surface. Very strange.

When I use just the non-introspected module:

from gi.repository import Gtk, Gdk
import cairo

I get the same error as in my first part of the question.

for completion, this is the list of cairo packages, that I have currently installed:

cairo-devel-1.14.6-1.fc24.x86_64
pycairo-devel-1.10.0-4.fc24.x86_64
cairo-gobject-1.14.6-1.fc24.i686
mingw32-cairo-1.14.6-1.fc24.noarch
python3-cairocffi-0.7.2-5.fc24.noarch
cairo-1.14.6-1.fc24.i686
cairo-1.14.6-1.fc24.x86_64
pycairo-1.10.0-4.fc24.x86_64
python3-cairosvg-1.0.19-3.fc24.noarch
cairomm-devel-1.12.0-2.fc24.x86_64
cairo-clock-0.3.4-17.fc24.x86_64
cairomm-1.12.0-2.fc24.x86_64
cairo-gobject-1.14.6-1.fc24.x86_64
python3-cairo-1.10.0-15.fc24.x86_64
mingw32-cairomm-1.12.0-2.fc24.noarch
python3-cairo-devel-1.10.0-15.fc24.x86_64
cairo-gobject-devel-1.14.6-1.fc24.x86_64

I think you'll find, that all of them are quite recent, and meet the requirements as stated in the example I've linked above.

Also, please consider the lines 268 - 274 of countdown.py:

# make window click-through, this needs pycairo 1.10.0 for python3
# to work
rect = cairo.RectangleInt (0, 0, 1, 1)
region = cairo.Region (rect)
if (not region.is_empty ()):
    self.window.input_shape_combine_region (None)
    self.window.input_shape_combine_region (region)

The author isn't using the introspected cairo, he just does import cairo, which leads me to believe it's the more correct way, perhaps.

Anyway, the whole script doesn't work either.

At this point I'm kinda stuck again. I don't seem to be able to find a recent and/or working example of how to make use of this functionality.

So far it seems, as if using the introspected version of cairo is of no use, at it defines almost no elements of the original. However, when using the regular pycairo (by importing with import cairo), I get the error that made me post this question in the first place.

I'm beginning to believe I'm having a conceptual error, maybe?

like image 720
polemon Avatar asked Oct 31 '22 00:10

polemon


1 Answers

OK... I know that this post is a little dated but I found it while I was looking for an answer to the same question. (This is my first contribution to stack overflow, so be patient with me). I wanted to create a non-rectangular window in GTK 3 and I also came up empty after hours of research.

Eventually, I found the same example program as Polemon mentions above and I read through the Cairo and Gdk documentation. In the end, the two functions that achieve the goal of creating a non-rectangular window that responds to input only on the non-transparent regions of a Cairo surface is Gdk.cairo_region_create_from_surface() followed by GdkWindow.input_shape_combine_region().

Now, my application focuses on creating an application launcher using a GtkDialog window, but this code can be applied to a standard GTKWindow.

Here are the steps I followed to achieve this effect. First, I added the following lines to the constructor of my dialog window:

    self.set_decorated(False)  # Creates a borderless window without a title bar
    self.set_app_paintable(True) # Tells GTK not to redraw the window area but let the application handle the drawing
    self.connect('draw', self.draw) # Registers a callback function for the draw signal

Next, I defined the callback function responsible for handling the draw signal. In it I put the following:

    # Create a Cairo surface from a PNG image
    self.image = cairo.ImageSurface.create_from_png("image-with-transparent-regions.png")
    # Create a new Cairo region based on this Cairo surface
    region = Gdk.cairo_region_create_from_surface(self.image)
    # Combine this region with the window dialog window region.
    # Input area is now restricted to non-transparent areas of the dialog window 
    self.input_shape_combine_region(region)

    # Sets the Cairo compositing Operator to replace the contents of the window background
    context.set_operator(cairo.OPERATOR_SOURCE)
    # Sets the Cairo surface as the pattern to be drawn on the window background
    context.set_source_surface(self.image, 0, 0)
    # Paints the background image with the Cairo surface pattern
    context.paint()
    # Restores the default state of the Cairo compositing operator to draw the source layer on top of the destination window layer.
    # This prevents the background image from being erased by alpha component of the widgets being drawn on top of it.
    context.set_operator(cairo.OPERATOR_OVER)

For anyone interested in trying out ready-to-run code, check out my full program.

like image 136
Praxis Avatar answered Nov 15 '22 08:11

Praxis