将Gtk.DrawingArea或Cairo图案的内容保存为磁盘上的图像
我有一个小的PyGI项目,它使用了一个Cairo图像表面,然后我用表面模式对它进行缩放,并在Gtk.DrawingArea上进行渲染。
我想把这个缩放过的版本保存为PNG文件。我尝试用Surface.write_to_png()从原始表面写入,但它只会以原始(也就是未缩放的)大小写入,所以我在这里遇到了瓶颈。
然后我想,也许可以从Gtk.DrawingArea获取渲染后的图像并将其写入磁盘,但我还没找到在PyGI中怎么做到这一点(这似乎只在GTK+ 2中可行 - 将gtk.DrawingArea保存到文件)。所以我正在想办法如何将我的缩放图像写入磁盘。
这是创建表面、缩放并渲染的代码:
def on_drawingarea1_draw (self, widget, ctx, data=None):
# 'widget' is a Gtk.DrawingArea
# 'ctx' is the Cairo context
text = self.ui.entry1.get_text()
if text == '':
return
# Get the data and encode it into the image
version, size, im = qrencode.encode(text)
im = im.convert('RGBA') # Cairo expects RGB
# Create a pixel array from the PIL image
bytearr = array.array('B', im.tostring())
height, width = im.size
# Convert the PIL image to a Cairo surface
self.surface = cairo.ImageSurface.create_for_data(bytearr,
cairo.FORMAT_ARGB32,
width, height,
width * 4)
# Scale the image
imgpat = cairo.SurfacePattern(self.surface)
scaler = cairo.Matrix()
scaler.scale(1.0/self.scale_factor, 1.0/self.scale_factor)
imgpat.set_matrix(scaler)
ctx.set_source(imgpat)
# Render the image
ctx.paint()
这是将表面写入PNG文件的代码:
def on_toolbuttonSave_clicked(self, widget, data=None):
if not self.surface:
return
# The following two lines did not seem to work
# ctx = cairo.Context(self.surface)
# ctx.scale(self.scale_factor, self.scale_factor)
self.surface.write_to_png('/tmp/test.png')
所以写入表面会生成一个未缩放的图像,而cairo.SurfacePattern中也没有写入方法。
我最后的办法是从gtk.DrawingArea获取缩放后的图像,将其放入GtkPixbuf.Pixbuf或新的表面中,然后再写入磁盘。使用pixbuf的方法在GTK+ 2中似乎有效,但在GTK+ 3中不行。
所以,有人知道我怎么才能把缩放的图像写入磁盘吗?
2 个回答
我找到了一种不同的方法,就是使用传给绘图事件处理程序的Cairo上下文,但这样做的结果是捕捉到了比DrawingArea更大的父窗口区域。
对我来说有效的方法是像你展示的那样使用PixBuf,但首先要调用DrawingArea的queue_draw()方法,强制进行完整的渲染,然后再等事件被处理(这很简单,我已经有了一个绘图处理程序)。否则,生成的图像可能会部分没有绘制出来。
好的,我找到了一个方法:
记住,Gtk.DrawingArea 是从 Gtk.Window 这个类派生出来的,所以我可以用 Gdk.pixbuf_get_from_window()
这个函数来获取绘图区域的内容,并把它放到一个 GdkPixbuf.Pixbuf 对象里。接着,我可以使用 GdkPixbuf.Pixbuf.savev()
这个函数把这个图像保存到硬盘上。
def drawing_area_write(self):
# drawingarea1 is a Gtk.DrawingArea
window = self.ui.drawingarea1.get_window()
# Some code to get the coordinates for the image, which is centered in the
# in the drawing area. You can ignore it for the purpose of this example
src_x, src_y = self.get_centered_coordinates(self.ui.drawingarea1,
self.surface)
image_height = self.surface.get_height() * self.scale_factor
image_width = self.surface.get_width() * self.scale_factor
# Fetch what we rendered on the drawing area into a pixbuf
pixbuf = Gdk.pixbuf_get_from_window(window, src_x, src_y,
image_width, image_height)
# Write the pixbuf as a PNG image to disk
pixbuf.savev('/tmp/testimage.png', 'png', [], [])
虽然这个方法有效,但如果有人能确认这是正确的方法,或者提供其他的替代方案,那就更好了。