在 Fedora 24 上的 Python3 中使用 `cairo.Region` 时出现分段错误

pol*_*mon 5 python gtk pygobject pycairo python-3.x

我正在尝试将输入形状应用于窗口,因此我可以拥有一个透明框架,其中只有有趣的部分可点击等。

我正在使用带有 PyGObject (Python 3) 的开罗图形。

Object 是一个常规的 Gtk Windows 对象,它定义了一个 Cairo 绘图区:

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)
Run Code Online (Sandbox Code Playgroud)

因此,on_draw()每当需要重绘绘制区域时,即在移动、调整大小、隐藏后显示等时,都会调用该函数。

大部分窗口实际上是空的,因为它应该是一个半透明的框架,只有可见部分应该是可点击的。但是,我收到以下错误:

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
Run Code Online (Sandbox Code Playgroud)

安装了 Python3 的包、PyGObject 和 Cairo 库,并且我还导入Cairo 的两个部分:

from gi.repository import Gtk, Gdk, cairo
import cairo
Run Code Online (Sandbox Code Playgroud)

在这一点上,我不完全确定,这是 Python 问题,还是我的错误。我不确定即使使用它cairo.Region是否也适用,我正在举的例子是这样的:http : //www.programcreek.com/python/example/81481/cairo.Region


编辑

在这一点上,我很困惑发生了什么。我一直在研究这个,这是我发现的:

PyGI和 Cairo似乎存在某种错误。

当我只使用 cairo 的内省部分时:

from gi.repository import Gtk, Gdk, cairo
#import cairo
Run Code Online (Sandbox Code Playgroud)

然后运行我的脚本,我收到此错误:

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'
Run Code Online (Sandbox Code Playgroud)

显然,我cairo.OPERATOR_SOURCE在我的代码中使用来绘制开罗上下文。然而,introspected 包似乎甚至不包含用于在表面上绘制的 te 运算符。很奇怪。

当我使用非内省模块时:

from gi.repository import Gtk, Gdk
import cairo
Run Code Online (Sandbox Code Playgroud)

我遇到了与问题第一部分相同的错误。

为了完成,这是我当前安装的 cairo 软件包列表:

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
Run Code Online (Sandbox Code Playgroud)

我想你会发现,所有这些都是最近的,并且满足我上面链接的示例中所述的要求。

另外,请考虑第 268 - 274 行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)
Run Code Online (Sandbox Code Playgroud)

作者没有使用 introspected cairo,他只是使用import cairo,这让我相信这也许是更正确的方法。

无论如何,整个脚本也不起作用。

在这一点上,我又有点卡住了。我似乎无法找到有关如何使用此功能的最近和/或工作示例。

到目前为止,似乎使用自省版本cairo是没有用的,因为它几乎没有定义原始元素。但是,当使用常规pycairo(通过使用 导入import cairo)时,我得到了一个错误,使我首先发布了这个问题。

我开始相信我有一个概念错误,也许?

Pra*_*xis 3

好吧...我知道这篇文章有点过时,但我在寻找同一问题的答案时发现了它。(这是我对堆栈溢出的第一个贡献,所以请耐心等待)。我想在 GTK 3 中创建一个非矩形窗口,但经过几个小时的研究后我也发现了空。

最终,我找到了与上面提到的 Polemon 相同的示例程序,并通读了 Cairo 和 Gdk 文档。最后,实现创建仅响应开罗表面非透明区域上的输入的非矩形窗口的目标的两个函数是Gdk.cairo_region_create_from_surface()GdkWindow.input_shape_combine_region()

现在,我的应用程序专注于使用 GtkDialog 窗口创建应用程序启动器,但此代码可以应用于标准 GTKWindow。

以下是我为实现此效果而遵循的步骤。首先,我将以下几行添加到对话框窗口的构造函数中:

    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
Run Code Online (Sandbox Code Playgroud)

接下来,我定义了负责处理绘制信号的回调函数。我在其中添加了以下内容:

    # 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)
Run Code Online (Sandbox Code Playgroud)

对于任何有兴趣尝试可立即运行的代码的人,请查看我的完整程序