使用 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,但代码运行良好