如何使用 GTK3 和 PyGObject 绘制 GdkPixbuf
Posted
技术标签:
【中文标题】如何使用 GTK3 和 PyGObject 绘制 GdkPixbuf【英文标题】:How to draw a GdkPixbuf using GTK3 and PyGObject 【发布时间】:2012-05-03 10:43:36 【问题描述】:我有一个小应用程序,它使用DrawingArea
使用PyGObject
和GTK3
绘制一个简单的地图。
我使用 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 中绘制一条线