如何使用 GTK3 和 PyGObject 绘制 GdkPixbuf

Posted

技术标签:

【中文标题】如何使用 GTK3 和 PyGObject 绘制 GdkPixbuf【英文标题】:How to draw a GdkPixbuf using GTK3 and PyGObject 【发布时间】:2012-05-03 10:43:36 【问题描述】:

我有一个小应用程序,它使用DrawingArea 使用PyGObjectGTK3 绘制一个简单的地图。

我使用 Pixbuf 加载

from gi.repository import Gtk, GdkPixbuf
pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size("logo.png", 25, 25)

然后尝试在DrawingArea的绘制事件信号中绘制它

def draw(self, widget, context):
    window = widget.get_window()
    ctx = window.cairo_create()
    ctx.set_source_pixbuf(pixbuf, 0, 0)

但我收到错误消息

"AttributeError: 'cairo.Context' object has no attribute 'set_source_pixbuf'"

如果我正确阅读了Gtk2 to Gtk3 migration guide,这应该可以。 我做错了什么?

【问题讨论】:

你确定你从 get_window 得到了一些可绘制的东西吗? 是的,我得到了一个<gtk.gdk.X11Window object at 0xb71aab44 (GdkX11Window at 0x9db5aa0)> 对象,这样就实现了DrawingArea 小部件。我似乎不知道在 Gtk3 中绘制 pixbuf 应该如何工作。 既然公认的答案是不使用pixbuf,而真正的问题不在于如何绘制pixbuf,你能否改变问题以便人们(比如我)来这里寻找如何使用 GTK3 和 PyGObject 真正绘制 GdkPixbuf 的答案不要进入这个问题? 【参考方案1】:

新的 draw 信号使用已经将 cairo 上下文作为参数传递的回调,您不需要像在 PyGtk 中那样执行 window = widget.get_window() 之类的操作来获取 cairo 上下文参加 expose-event 信号。在 PYGObject 中更简单:

import cairo

class Foo(object):
    def __init__(self):

       (...)
        self.image = cairo.ImageSurface.create_from_png('logo.png')
       (...)

    def draw(self, widget, context):
        if self.image is not None:
            context.set_source_surface(self.image, 0.0, 0.0)
            context.paint()
        else:
            print('Invalid image')
        return False

也就是说,如果您不需要 PixBuf,但如果您需要它来做其他事情,您有多种选择:

    将两个对象都保存在内存中。如果两者都是从 PNG 加载的,那么除了浪费内存之外应该没有太多问题。 将 GdkPixbuf 转换为 PIL 图像,然后将 PIL 图像转换为数据数组,然后使用 create_for_data() 从该数据数组创建 Cairo ImageSurface。牦牛:S我不知道更好,对不起:S 使用 hock 提出的 Gdk.cairo_set_source_pixbuf()。这似乎是在 ImageSurface 中绘制 Pixbuf 的正确方法,但它完全不是 Python 的(这就是为什么我讨厌这种 Introspection 的东西,所有看起来都像 C,就像一个糟糕的 C 端口)。

如果您选择糟糕的第二个选项,方法如下:

import Image
import array
from gi.repository import Gtk, GdkPixbuf

width = 25
height = 25
pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size('logo.png', width, height)
pil_image = Image.fromstring('RGBA', (width, height), pixbuf.get_pixels())
byte_array = array.array('B', pil_image.tostring())
cairo_surface = cairo.ImageSurface.create_for_data(byte_array, cairo.FORMAT_ARGB32, width, height, width * 4)

请注意,create_for_data() 是 not yet available for Python3、only for Python2。

如果这是您想要实现的目标,请查看我关于如何在 PyGObject 中使用双缓冲区的答案:Drawing in PyGobject (python3)

亲切的问候

【讨论】:

谢谢。与我的各种努力相比,这似乎是在 GTK3 中处理图形的更明智的方法;) +1 "但它完全不是 Python 的(这就是为什么我讨厌这种自省的东西,所有看起来都像 C,就像一个糟糕的 C 端口)。" 更新 create_for_data 现已可用,当前文档在这里pycairo.readthedocs.io/en/latest/reference/…【参考方案2】:

以下似乎可以完成这项工作:

def draw(self, widget, context):
    Gdk.cairo_set_source_pixbuf(context, self.pixbuf, 0, 0)
    context.paint()

还有一个问题:这是首选的做事方式吗?

【讨论】:

我的程序在执行cairo_set_source_pixbuf后产生“分段错误”有什么帮助吗? 在 Gtk 文档中他们说“小心,这个函数不是来自 cairo ...”。我应该在这个答案之上 -1 答案,但似乎对 +1 这个答案更有建设性。 Gtk3 是一种不同的方式,它使用 cairo 并将处理程序返回到表面,所以不要表现得像它是一个外部工具:绘图发生在哪里。 @AkashShende - 我也遇到了使用.cairo_get_source_pixbuf 的分段错误,你知道如何解决这个问题吗? @nluigi 我放弃了 Pixbuf 并使用 Cairo_Surface 并使用方法 cairo_set_source_surface 设置图像。检查这个github.com/akash0x53/i3lock/commit/… @AkashShende - thx,结果证明.cairo_set_source_pixbuf 对我来说不是问题,但我正在拨打GdkPixbuf.Pixbuf.new_from_data 电话。 Changing 到 GdkPixbuf.Pixbuf.new_from_bytes 为我修复了它。

以上是关于如何使用 GTK3 和 PyGObject 绘制 GdkPixbuf的主要内容,如果未能解决你的问题,请参考以下文章

使用 Pygobject 和 Python 3 从内存数据中显示图像

如何使用 Cairo 和 Gtk3 在 GtkDrawingArea 中绘制一条线

如何在 Gtk3 中在开罗上下文中使用 Skia 绘制

使用 Gtk3 和 Cairo 绘制到 GdkWindow 根窗口

在 GTK3 中显示股票图标的非弃用方式是啥?

如何将 GTK2 像素图移植到 GTK3_