【问题标题】:How to draw a GdkPixbuf using GTK3 and PyGObject如何使用 GTK3 和 PyGObject 绘制 GdkPixbuf
【发布时间】: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 的答案不要进入这个问题?

标签: python gtk3 pygobject


【解决方案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,但如果您需要它来做其他事情,您有多种选择:

  1. 将两个对象都保存在内存中。如果两者都是从 PNG 加载的,那么除了浪费内存之外应该没有太多问题。
  2. 将 GdkPixbuf 转换为 PIL 图像,然后将 PIL 图像转换为数据数组,然后使用 create_for_data() 从该数据数组创建 Cairo ImageSurface。牦牛:S我不知道更好,对不起:S
  3. 使用 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 Python3only 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 电话。 ChangingGdkPixbuf.Pixbuf.new_from_bytes 为我修复了它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多