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

12

我在Python 3中有一些RGBA数据,我想在一个GTK3窗口中显示它所代表的图像而不使用任何额外的库。

我尝试过的第一件事是编辑一个Pixbuf的数据,就像(C)文档中的例子(http://developer.gnome.org/gdk-pixbuf/stable/gdk-pixbuf-The-GdkPixbuf-Structure.html#put-pixel - 抱歉,我只被允许2个链接),使用Pixbuf.get_pixels()。然而,这给了我一个不可变的bytes对象,显然行不通。(gdk-pixbuf的一个错误报告(668084),如果SO能让我链接到它,我会链接到它的。)虽然功能有所改变。

接下来,我尝试使用Pixbuf.new_from_data()从数据创建Pixbuf,但这也存在问题(参见gdk-pixbuf bug 674691) (没错,在评论3中就是我)。

然后我看了看Cairo:ImageSurface.create_for_data应该可以做到这一点,但由于某种原因,Python 3绑定目前还没有这个功能。(我已经尝试过使用Python 2,将表面转换为Pixbuf,然后将其包装在gtk.Image中,它可以工作。但我正在使用Python3(即使这样,它也很凌乱——毕竟Cairo是一个矢量图形库))。

我在某个地方找到了一个关于使用PIL将图像写入PNG文件,然后将其读入Pixbuf的参考,但这太糟糕了,使用了额外的库,而且PIL也不支持Python 3。

所以...还有其他方法吗?

工作代码

基于Havok的答案,这里有一个可行的例子:

from array import array

from gi.repository import Gtk as gtk, GdkPixbuf

pixels = array('H')
for i in range(20):
    for j in range(20):
        px = (i < 10, j >= 10, (i < 10) ^ (j < 10))
        pixels.extend(65535 * c for c in px)

header = b'P6 20 20 65535 '
img_data = header + pixels

w = gtk.Window()
w.connect('delete-event', gtk.main_quit)
l = GdkPixbuf.PixbufLoader.new_with_type('pnm')
l.write(img_data)
w.add(gtk.Image.new_from_pixbuf(l.get_pixbuf()))
l.close()
w.show_all()
gtk.main()

编辑:实际上,为了正确性,在大端系统上添加到头文件之前,我认为这将需要一个 pixels.byteswap() (虽然当然在这些颜色中无关紧要)。


也许,DrawingArea 是你想要的小部件?就像这样:https://dev59.com/NkfRa4cB1Zd3GeqP702h如果你需要预先编辑它,可以在 pixbuf 中进行编辑,然后将其从 pixbuf 转移到 DrawingArea 并显示? - Boris Burkov
@Bob 但是我没有 Pixbuf,只有数据 - 这就是问题所在。(如果我有的话,我会像我之前提到的那样使用 gtk.Image)。 - ikn
你提到的 GNOME bug 追踪器中的错误链接:https://bugzilla.gnome.org/show_bug.cgi?id=674691 - Lassi
您可能考虑在原始RGB数据中添加一个简单的头文件,将其转换为NetPBM格式,如PPM。 - fmw42
3个回答

6

哇,这将是一个困难的问题,非常棘手。

在Python 3中,既没有PIL也没有Cairo的ImageSurface.create_from_data()可用。你唯一剩下的选择是直接使用GdkPixbuf,但正如你所说,这并不是一个完美的解决方案,特别是因为你希望使用alpha通道。我唯一的想法是让你尝试使用这个:

http://developer.gnome.org/gdk-pixbuf/stable/GdkPixbufLoader.html#gdk-pixbuf-loader-new-with-type

就像我在这里的结尾所做的那样: Converting PIL GdkPixbuf

loader = GdkPixbuf.PixbufLoader.new_with_type('pnm')
loader.write(contents)
pixbuf = loader.get_pixbuf()
loader.close()

请尝试查看支持的类型,无法找到与C文档中关于 gdk_pixbuf_format_get_name()gdk_pixbuf_get_formats() 相关的PyGObject版本。使用pnm格式将不会有alpha通道。

编辑

很好,您已经找到了PyGObject函数,这里发布输出以供文档记录:

>>> from gi.repository import GdkPixbuf
>>> formats = GdkPixbuf.Pixbuf.get_formats()
>>> [f.get_name() for f in formats]
['GdkPixdata', 'ras', 'tiff', 'wmf', 'icns', 'ico', 'png', 'qtif', 
 'wbmp', 'gif', 'pnm', 'tga', 'ani', 'xbm', 'xpm', 'jpeg2000', 
 'pcx', 'jpeg', 'bmp', 'svg']

这听起来不错,我会试一下。很多可用的格式似乎都有点过时了(函数是gi.repository.GdkPixbuf.Pixbuf.get_formats()gi.repository.GdkPixbuf.PixbufFormat.get_name())- 你认为pnm会保留下来吗?我实际上也不需要alpha通道(我可以自己混合它),所以如果我不需要,你是在建议有更好的方法吗? - ikn
啊哈,它起作用了!非常感谢;我正在我的问题中添加一个示例。 - ikn

3

我没有安装 gtk3,但是在 gtk2 中它完美地工作:

import gtk
import cairo


def main():
    window = gtk.Window(gtk.WINDOW_TOPLEVEL)
    window.set_title("Drawing Area")
    window.connect("destroy", lambda w:gtk.main_quit())

    sw = gtk.ScrolledWindow()
    area = Area()
    area.show()
    area.connect('expose-event', area.expose_handler)
    sw.add_with_viewport(area)
    window.add(sw)
    window.show_all()


class Area(gtk.DrawingArea):
    def __init__(self):
        super(Area, self).__init__()
        self.pixbuf = gtk.gdk.pixbuf_new_from_file("hard_drive_elements.png")

    def expose_handler(self, widget, event):
        cr = self.window.cairo_create()
        cr.set_operator(cairo.OPERATOR_SOURCE)
        cr.set_source_rgb(1,1,1)
        cr.paint()
        cr.set_source_pixbuf(self.pixbuf, 0, 0)
        cr.paint()


if __name__ == "__main__":
    main()
    gtk.main()

Drawing_area_from_pixbuf


我没有一个PNG文件,但我有一些在内存中的RGBA数据。 - ikn

0

这在 Python 3 中现在对我有效:

import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GdkPixbuf

pixels = open('test.raw', 'rb').read()
depth = 8
width = 460
height = 460
stride = width * 3
win = Gtk.Window()
win.connect('delete-event', Gtk.main_quit)
win.add(Gtk.Image.new_from_pixbuf(
    GdkPixbuf.Pixbuf.new_from_data(pixels, GdkPixbuf.Colorspace.RGB,
                                   False, depth, width, height, stride)))
win.show_all()
Gtk.main()

您可以使用ImageMagick将JPEG或其他常见图像格式创建为.raw图像:
convert test.jpeg -depth 8 -size 460x460 RGB:test.raw

我没有检查过 alpha 通道(透明度)数据是否可以加载,但是 new_from_data() 接受一个布尔标志来处理这个问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接