summaryrefslogtreecommitdiff
path: root/demos/gtk-demo/demos/images.py
diff options
context:
space:
mode:
Diffstat (limited to 'demos/gtk-demo/demos/images.py')
-rw-r--r--demos/gtk-demo/demos/images.py312
1 files changed, 312 insertions, 0 deletions
diff --git a/demos/gtk-demo/demos/images.py b/demos/gtk-demo/demos/images.py
new file mode 100644
index 00000000..51c241e3
--- /dev/null
+++ b/demos/gtk-demo/demos/images.py
@@ -0,0 +1,312 @@
+#!/usr/bin/env python
+# -*- Mode: Python; py-indent-offset: 4 -*-
+# vim: tabstop=4 shiftwidth=4 expandtab
+#
+# Copyright (C) 2010 Red Hat, Inc., John (J5) Palmieri <johnp@redhat.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
+# USA
+
+title = "Images"
+description = """GtkImage is used to display an image; the image can be in a number of formats. Typically, you load an image into a GdkPixbuf, then display the pixbuf.
+This demo code shows some of the more obscure cases, in the simple case a call to gtk_image_new_from_file() is all you need.
+"""
+
+from gi.repository import Gtk, Gdk, GdkPixbuf, GLib, Gio, GObject
+import os
+import math
+from os import path
+
+class ImagesApp:
+ def __init__(self):
+ self.pixbuf_loader = None
+ self.image_stream = None
+
+ self.window = Gtk.Window(title="Images")
+ self.window.connect('destroy', self.cleanup_cb)
+ self.window.set_border_width(8)
+
+ vbox = Gtk.VBox(spacing=8)
+ vbox.set_border_width(8)
+ self.window.add(vbox)
+
+ label = Gtk.Label()
+ label.set_markup('<u>Image loaded from file</u>')
+ vbox.pack_start(label, False, False, 0)
+
+ frame = Gtk.Frame()
+ frame.set_shadow_type(Gtk.ShadowType.IN)
+
+ # The alignment keeps the frame from growing when users resize
+ # the window
+ align = Gtk.Alignment(xalign=0.5,
+ yalign=0.5,
+ xscale=0,
+ yscale=0)
+ align.add(frame)
+ vbox.pack_start(align, False, False, 0)
+
+ self.base_path = 'data'
+ if not path.isdir(self.base_path):
+ self.base_path = path.join('demos', self.base_path)
+
+ try:
+ img_path = path.join(self.base_path, 'gtk-logo-rgb.gif')
+ pixbuf = GdkPixbuf.Pixbuf.new_from_file(img_path)
+ except GObject.GError as e:
+ dialog = Gtk.MessageDialog(self.window,
+ Gtk.DialogFlags.DESTROY_WITH_PARENT,
+ Gtk.MessageType.ERROR,
+ Gtk.ButtonsType.CLOSE,
+ e.message)
+
+ dialog.show()
+ dialog.connect('response', lambda x,y: dialog.destroy())
+
+ image = Gtk.Image.new_from_pixbuf(pixbuf)
+ frame.add(image)
+
+ # Animation
+
+ label = Gtk.Label()
+ label.set_markup('<u>Animation loaded from file</u>')
+ vbox.pack_start(label, False, False, 0)
+
+ frame = Gtk.Frame()
+ frame.set_shadow_type(Gtk.ShadowType.IN)
+
+ # The alignment keeps the frame from growing when users resize
+ # the window
+ align = Gtk.Alignment(xalign=0.5,
+ yalign=0.5,
+ xscale=0,
+ yscale=0)
+ align.add(frame)
+ vbox.pack_start(align, False, False, 0)
+
+ img_path = path.join(self.base_path, 'floppybuddy.gif')
+ image = Gtk.Image.new_from_file(img_path)
+ frame.add(image)
+
+ # Symbolic icon
+
+ label = Gtk.Label()
+ label.set_markup('<u>Symbolic themed icon</u>')
+ vbox.pack_start(label, False, False, 0)
+
+ frame = Gtk.Frame()
+ frame.set_shadow_type(Gtk.ShadowType.IN)
+
+ # The alignment keeps the frame from growing when users resize
+ # the window
+ align = Gtk.Alignment(xalign=0.5,
+ yalign=0.5,
+ xscale=0,
+ yscale=0)
+ align.add(frame)
+ vbox.pack_start(align, False, False, 0)
+
+ gicon = Gio.ThemedIcon.new_with_default_fallbacks('battery-caution-charging-symbolic')
+ image = Gtk.Image.new_from_gicon(gicon, Gtk.IconSize.DIALOG)
+ frame.add(image)
+
+ # progressive
+
+ label = Gtk.Label()
+ label.set_markup('<u>Progressive image loading</u>')
+ vbox.pack_start(label, False, False, 0)
+
+ frame = Gtk.Frame()
+ frame.set_shadow_type(Gtk.ShadowType.IN)
+
+ # The alignment keeps the frame from growing when users resize
+ # the window
+ align = Gtk.Alignment(xalign=0.5,
+ yalign=0.5,
+ xscale=0,
+ yscale=0)
+ align.add(frame)
+ vbox.pack_start(align, False, False, 0)
+
+ image = Gtk.Image.new_from_pixbuf(None)
+ frame.add(image)
+
+ self.start_progressive_loading(image)
+
+ # Sensistivity control
+ button = Gtk.ToggleButton(label='_Insensitive')
+ button.connect('toggled', self.toggle_sensitivity_cb, vbox)
+ vbox.pack_start(button, False, False, 0)
+
+ self.window.show_all()
+
+ def toggle_sensitivity_cb(self, button, container):
+ widget_list = container.get_children()
+ for w in widget_list:
+ if w != button:
+ w.set_sensitive(not button.get_active())
+
+ return True
+
+ def progressive_timeout(self, image):
+ # This shows off fully-paranoid error handling, so looks scary.
+ # You could factor out the error handling code into a nice separate
+ # function to make things nicer.
+
+ if self.image_stream:
+ try:
+ buf = self.image_stream.read(256)
+ except IOError as e:
+ dialog = Gtk.MessageDialog(self.window,
+ Gtk.DialogFlags.DESTROY_WITH_PARENT,
+ Gtk.MessageType.ERROR,
+ Gtk.ButtonsType.CLOSE,
+ "Failure reading image file 'alphatest.png': %s" %str(e))
+
+ self.image_stream.close()
+ self.image_stream = None
+ self.load_timeout = 0
+
+ dialog.show()
+ dialog.connect('response', lambda x,y: dialog.destroy())
+
+ return False # uninstall the timeout
+
+ try:
+ self.pixbuf_loader.write(buf)
+
+ except GObject.GError as e:
+ dialog = Gtk.MessageDialog(self.window,
+ Gtk.DialogFlags.DESTROY_WITH_PARENT,
+ Gtk.MessageType.ERROR,
+ Gtk.ButtonsType.CLOSE,
+ e.message)
+
+ self.image_stream.close()
+ self.image_stream = None
+ self.load_timeout = 0
+
+ dialog.show()
+ dialog.connect('response', lambda x,y: dialog.destroy())
+
+ return False # uninstall the timeout
+
+ if len(buf) < 256: # eof
+ self.image_stream.close()
+ self.image_stream = None
+
+ # Errors can happen on close, e.g. if the image
+ # file was truncated we'll know on close that
+ # it was incomplete.
+ try:
+ self.pixbuf_loader.close()
+ except GObject.GError as e:
+ dialog = Gtk.MessageDialog(self.window,
+ Gtk.DialogFlags.DESTROY_WITH_PARENT,
+ Gtk.MessageType.ERROR,
+ Gtk.ButtonsType.CLOSE,
+ 'Failed to load image: %s' % e.message)
+
+ self.load_timeout = 0
+
+ dialog.show()
+ dialog.connect('response', lambda x,y: dialog.destroy())
+
+ return False # uninstall the timeout
+ else:
+ img_path = path.join(self.base_path, 'alphatest.png')
+ try:
+ self.image_stream = open(img_path, 'rb')
+ except IOError as e:
+ dialog = Gtk.MessageDialog(self.window,
+ Gtk.DialogFlags.DESTROY_WITH_PARENT,
+ Gtk.MessageType.ERROR,
+ Gtk.ButtonsType.CLOSE,
+ str(e))
+ self.load_timeout = 0
+ dialog.show()
+ dialog.connect('response', lambda x,y: dialog.destroy())
+
+ return False # uninstall the timeout
+
+ if self.pixbuf_loader:
+ try:
+ self.pixbuf_loader.close()
+ except GObject.GError:
+ pass
+ self.pixbuf_loader = None
+
+ self.pixbuf_loader = GdkPixbuf.PixbufLoader()
+
+ self.pixbuf_loader.connect('area-prepared',
+ self.progressive_prepared_callback,
+ image)
+
+ self.pixbuf_loader.connect('area-updated',
+ self.progressive_updated_callback,
+ image)
+ # leave timeout installed
+ return True
+
+ def progressive_prepared_callback(self, loader, image):
+ pixbuf = loader.get_pixbuf()
+ # Avoid displaying random memory contents, since the pixbuf
+ # isn't filled in yet.
+ pixbuf.fill(0xaaaaaaff)
+ image.set_from_pixbuf(pixbuf)
+
+ def progressive_updated_callback(self, loader, x, y, width, height, image):
+ # We know the pixbuf inside the GtkImage has changed, but the image
+ # itself doesn't know this; so queue a redraw. If we wanted to be
+ # really efficient, we could use a drawing area or something
+ # instead of a GtkImage, so we could control the exact position of
+ # the pixbuf on the display, then we could queue a draw for only
+ # the updated area of the image.
+ image.queue_draw()
+
+ def start_progressive_loading(self, image):
+ # This is obviously totally contrived (we slow down loading
+ # on purpose to show how incremental loading works).
+ # The real purpose of incremental loading is the case where
+ # you are reading data from a slow source such as the network.
+ # The timeout simply simulates a slow data source by inserting
+ # pauses in the reading process.
+
+ self.load_timeout = Gdk.threads_add_timeout(150,
+ GLib.PRIORITY_DEFAULT_IDLE,
+ self.progressive_timeout,
+ image)
+
+ def cleanup_cb(self, widget):
+ if self.load_timeout:
+ GObject.source_remove(self.load_timeout)
+
+ if self.pixbuf_loader:
+ try:
+ self.pixbuf_loader.close()
+ except GObject.GError:
+ pass
+
+ if self.image_stream:
+ self.image_stream.close()
+
+ Gtk.main_quit()
+
+def main(demoapp=None):
+ app = ImagesApp()
+ Gtk.main()
+
+if __name__ == '__main__':
+ main()