I have a PyGTK app that needs to load an image of unknown size, however I am having the problem that if the image is either very big or very small, the window layout becomes distorted and hard to use. I need some way of making the image automatically scale to fit its parent widget. Unfortunately, after doing some research, it seems there is no code, built in or otherwise, that does what I'm looking for.
我有一个PyGTK应用程序,需要加载未知大小的图像,但我遇到的问题是,如果图像非常大或非常小,窗口布局变得扭曲和难以使用。我需要一些方法使图像自动缩放以适合其父窗口小部件。不幸的是,经过一些研究,似乎没有代码,内置或其他方式,做我正在寻找的东西。
How could I go about writing something to do this? I would have thought that somebody would have written some code for this already; is there something that I missed?
我怎么能写点什么呢?我原本以为有人会为此编写一些代码;有什么我错过了吗?
3 个解决方案
#1
9
You can use widget.get_allocation() to find out the size of the parent widget and pixbuf.scale_simple to scale the image, like this:
您可以使用widget.get_allocation()来查找父窗口小部件的大小,使用pixbuf.scale_simple来缩放图像,如下所示:
allocation = parent_widget.get_allocation()
desired_width = allocation.width
desired_height = allocation.height
pixbuf = gtk.gdk.pixbuf_new_from_file('your_image.png')
pixbuf = pixbuf.scale_simple(desired_width, desired_height, gtk.gdk.INTERP_BILINEAR)
image = gtk.image_new_from_pixbuf(pixbuf)
If you want the image to scale each time the window is resized, you'll have to put the code above (or something similar, to avoid loading the image from disk every time) in a function connected to the size_allocate signal of the parent widget. To avoid infinite loops, make sure that the image you put in the widget doesn't alter its size again.
如果您希望每次调整窗口大小时图像都缩放,您必须在连接到父窗口小部件的size_allocate信号的函数中放置上面的代码(或类似的东西,以避免每次从磁盘加载图像) 。要避免无限循环,请确保放在窗口小部件中的图像不会再次改变其大小。
References:
参考文献:
- How to resize an image (I think you already visited that)
- 如何调整图像大小(我想你已经访问过了)
- About the "resize" event
- 关于“调整大小”事件
- Other link about resizing, in Stack Overflow
- 关于调整大小的其他链接,在Stack Overflow中
#2
0
Here some small snippet class which allows you to use auto scaled image.
这里有一些小片段类,允许您使用自动缩放图像。
import gtk
class ImageEx(gtk.Image):
pixbuf = None
def __init__(self, *args, **kwargs):
super(ImageEx, self).__init__(*args, **kwargs)
self.connect("size-allocate", self.on_size_allocate)
def set_pixbuf(self, pixbuf):
"""
use this function instead set_from_pixbuf
it sets additional pixbuf, which allows to implement autoscaling
"""
self.pixbuf = pixbuf
self.set_from_pixbuf(pixbuf)
def on_size_allocate(self, obj, rect):
# skip if no pixbuf set
if self.pixbuf is None:
return
# calculate proportions for image widget and for image
k_pixbuf = float(self.pixbuf.props.height) / self.pixbuf.props.width
k_rect = float(rect.height) / rect.width
# recalculate new height and width
if k_pixbuf < k_rect:
newWidth = rect.width
newHeight = int(newWidth * k_pixbuf)
else:
newHeight = rect.height
newWidth = int(newHeight / k_pixbuf)
# get internal image pixbuf and check that it not yet have new sizes
# that's allow us to avoid endless size_allocate cycle
base_pixbuf = self.get_pixbuf()
if base_pixbuf.props.height == newHeight and base_pixbuf.props.width == newWidth:
return
# scale image
base_pixbuf = self.pixbuf.scale_simple(
newWidth,
newHeight,
gtk.gdk.INTERP_BILINEAR
)
# set internal image pixbuf to scaled image
self.set_from_pixbuf(base_pixbuf)
And small usage example:
小用法示例:
class MainWindow(object):
def __init__(self):
self.window = gtk.Window()
self.window.connect("destroy", gtk.main_quit)
# create new ImageEx
self.image = ImageEx()
# set size request, to limit image size
self.image.set_size_request(width=400, height=400)
# load image from file, change path with path of some of your image
pixbuf = gtk.gdk.pixbuf_new_from_file("path/to/your/image.jpeg")
# that's the key moment, instead `set_from_pixbuf` function
# we use our newly created set_pixbuf, which do some additional assignments
self.image.set_pixbuf(pixbuf)
# add widget and show window
self.window.add(self.image)
self.window.show_all()
if __name__ == '__main__':
MainWindow()
gtk.main()
#3
0
Here is an except that accomplishes this task on a drawing area:
除了在绘图区域完成此任务之外,还有以下内容:
self.spash_pixbuf = GdkPixbuf.Pixbuf.new_from_file('myfile.png')
...
def on_draw(self, widget, cairo_ct):
"""
draw
"""
self._cairo_ct = cairo_ct
self._width = widget.get_allocated_width()
self._height = widget.get_allocated_height()
self._draw_cover(self.spash_pixbuf)
def _draw_cover(self, pixbuf):
"""
Paint pixbuf to cover drawingarea.
"""
img_width = float(pixbuf.get_width())
img_height = float(pixbuf.get_height())
# Scale
width_ratio = self._width / img_width
height_ratio = self._height / img_height
scale_xy = max(height_ratio, width_ratio)
# Center
off_x = (self._width - round(img_width*scale_xy)) //2
off_y = (self._height - round(img_height*scale_xy)) //2
# Paint
self._cairo_ct.save()
self._cairo_ct.translate(off_x, off_y)
self._cairo_ct.scale(scale_xy, scale_xy)
Gdk.cairo_set_source_pixbuf(self._cairo_ct, pixbuf, 0, 0)
self._cairo_ct.paint()
self._cairo_ct.restore()
#1
9
You can use widget.get_allocation() to find out the size of the parent widget and pixbuf.scale_simple to scale the image, like this:
您可以使用widget.get_allocation()来查找父窗口小部件的大小,使用pixbuf.scale_simple来缩放图像,如下所示:
allocation = parent_widget.get_allocation()
desired_width = allocation.width
desired_height = allocation.height
pixbuf = gtk.gdk.pixbuf_new_from_file('your_image.png')
pixbuf = pixbuf.scale_simple(desired_width, desired_height, gtk.gdk.INTERP_BILINEAR)
image = gtk.image_new_from_pixbuf(pixbuf)
If you want the image to scale each time the window is resized, you'll have to put the code above (or something similar, to avoid loading the image from disk every time) in a function connected to the size_allocate signal of the parent widget. To avoid infinite loops, make sure that the image you put in the widget doesn't alter its size again.
如果您希望每次调整窗口大小时图像都缩放,您必须在连接到父窗口小部件的size_allocate信号的函数中放置上面的代码(或类似的东西,以避免每次从磁盘加载图像) 。要避免无限循环,请确保放在窗口小部件中的图像不会再次改变其大小。
References:
参考文献:
- How to resize an image (I think you already visited that)
- 如何调整图像大小(我想你已经访问过了)
- About the "resize" event
- 关于“调整大小”事件
- Other link about resizing, in Stack Overflow
- 关于调整大小的其他链接,在Stack Overflow中
#2
0
Here some small snippet class which allows you to use auto scaled image.
这里有一些小片段类,允许您使用自动缩放图像。
import gtk
class ImageEx(gtk.Image):
pixbuf = None
def __init__(self, *args, **kwargs):
super(ImageEx, self).__init__(*args, **kwargs)
self.connect("size-allocate", self.on_size_allocate)
def set_pixbuf(self, pixbuf):
"""
use this function instead set_from_pixbuf
it sets additional pixbuf, which allows to implement autoscaling
"""
self.pixbuf = pixbuf
self.set_from_pixbuf(pixbuf)
def on_size_allocate(self, obj, rect):
# skip if no pixbuf set
if self.pixbuf is None:
return
# calculate proportions for image widget and for image
k_pixbuf = float(self.pixbuf.props.height) / self.pixbuf.props.width
k_rect = float(rect.height) / rect.width
# recalculate new height and width
if k_pixbuf < k_rect:
newWidth = rect.width
newHeight = int(newWidth * k_pixbuf)
else:
newHeight = rect.height
newWidth = int(newHeight / k_pixbuf)
# get internal image pixbuf and check that it not yet have new sizes
# that's allow us to avoid endless size_allocate cycle
base_pixbuf = self.get_pixbuf()
if base_pixbuf.props.height == newHeight and base_pixbuf.props.width == newWidth:
return
# scale image
base_pixbuf = self.pixbuf.scale_simple(
newWidth,
newHeight,
gtk.gdk.INTERP_BILINEAR
)
# set internal image pixbuf to scaled image
self.set_from_pixbuf(base_pixbuf)
And small usage example:
小用法示例:
class MainWindow(object):
def __init__(self):
self.window = gtk.Window()
self.window.connect("destroy", gtk.main_quit)
# create new ImageEx
self.image = ImageEx()
# set size request, to limit image size
self.image.set_size_request(width=400, height=400)
# load image from file, change path with path of some of your image
pixbuf = gtk.gdk.pixbuf_new_from_file("path/to/your/image.jpeg")
# that's the key moment, instead `set_from_pixbuf` function
# we use our newly created set_pixbuf, which do some additional assignments
self.image.set_pixbuf(pixbuf)
# add widget and show window
self.window.add(self.image)
self.window.show_all()
if __name__ == '__main__':
MainWindow()
gtk.main()
#3
0
Here is an except that accomplishes this task on a drawing area:
除了在绘图区域完成此任务之外,还有以下内容:
self.spash_pixbuf = GdkPixbuf.Pixbuf.new_from_file('myfile.png')
...
def on_draw(self, widget, cairo_ct):
"""
draw
"""
self._cairo_ct = cairo_ct
self._width = widget.get_allocated_width()
self._height = widget.get_allocated_height()
self._draw_cover(self.spash_pixbuf)
def _draw_cover(self, pixbuf):
"""
Paint pixbuf to cover drawingarea.
"""
img_width = float(pixbuf.get_width())
img_height = float(pixbuf.get_height())
# Scale
width_ratio = self._width / img_width
height_ratio = self._height / img_height
scale_xy = max(height_ratio, width_ratio)
# Center
off_x = (self._width - round(img_width*scale_xy)) //2
off_y = (self._height - round(img_height*scale_xy)) //2
# Paint
self._cairo_ct.save()
self._cairo_ct.translate(off_x, off_y)
self._cairo_ct.scale(scale_xy, scale_xy)
Gdk.cairo_set_source_pixbuf(self._cairo_ct, pixbuf, 0, 0)
self._cairo_ct.paint()
self._cairo_ct.restore()