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

Posted

技术标签:

【中文标题】使用 Pygobject 和 Python 3 从内存数据中显示图像【英文标题】:Displaying an image with Pygobject and Python 3 from in-memory data 【发布时间】:2012-09-06 23:26:37 【问题描述】:

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

我尝试的第一件事是编辑 Pixbuf 的数据,例如 (http://developer.gnome.org/gdk-pixbuf/stable/gdk-pixbuf-The-GdkPixbuf-Structure.html#put -pixel - 抱歉,我只允许在 (C) 文档中使用 2 个链接,使用 Pixbuf.get_pixels()。不过,这给了我一个不可变的bytes 对象,因此显然行不通。 (一个 gdk-pixbuf 错误报告(668084,如果允许我会链接到它)涵盖了相同的功能,但从那以后东西必须有所改变。)

接下来,我尝试使用 Pixbuf.new_from_data() 从数据中创建 Pixbuf,但这也有问题(请参阅 gdk-pixbuf 错误 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()

编辑:实际上,为了正确起见,我认为在添加到标题之前,在 little-endian 系统上需要一个 pixels.byteswap()(当然,这些颜色并不重要)。

【问题讨论】:

可能,DrawingArea 是你想要的小部件?像这样:***.com/questions/775528/… 如果你需要预编辑,你可以在 pixbuf 中进行,而不是从 pixbuf 转移到 DrawingArea 并显示? @Bob 但我没有Pixbuf,只有数据——这就是问题所在。 (如果我这样做了,我会使用 gtk.Image,就像我提到的那样。) 链接到您在 GNOME 错误跟踪器中提到的错误:bugzilla.gnome.org/show_bug.cgi?id=674691 您可以考虑在原始 RGB 数据中添加一个简单的标头,以将其转换为 PPM 等 NetPBM 格式。 【参考方案1】:

哇,这将是一个艰难的,非常困难。

PIL 或 Cairo 的 ImageSurface.create_from_data() 都不适用于 Python 3。您唯一的选择是直接使用 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 format,您将没有 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 通道(我可以将其混合到自己中),所以如果我不需要,您是否建议有更好的方法? 啊哈,它有效!非常感谢;我正在为我的问题添加一个示例。【参考方案2】:

我没有安装 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()

【讨论】:

我没有 PNG 文件,内存中有一些 RGBA 数据。【参考方案3】:

这现在适用于 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() 需要一个布尔标志。

【讨论】:

以上是关于使用 Pygobject 和 Python 3 从内存数据中显示图像的主要内容,如果未能解决你的问题,请参考以下文章

PyCharm 无法解析 PyGObject 3.0,但代码运行良好

Windows 上的 PyGObject

用 pygobject 编写 D-Bus 服务?

如何使用 Python 2.6 在 Windows 上安装 PyGTK / PyGobject?

PyGObject GTK+ 3 - 文档?

在 PyGObject 内省中,带有 Python 的 GTK 中的线程是不是发生了变化?