summaryrefslogtreecommitdiff
path: root/demos
diff options
context:
space:
mode:
authorDieter Verfaillie <dieterv@optionexplicit.be>2011-03-24 10:42:17 +0100
committerDieter Verfaillie <dieterv@optionexplicit.be>2011-03-24 10:42:17 +0100
commitfef1e60f6e20022fac7f1f1268004b6a022491dd (patch)
tree52f4cbe44fbe4e220c6e1ab0209ae21d63ab5c3b /demos
parentad5dabefef5f232d27f3b190ce3f871119ad3677 (diff)
parent27496e0e86e7d8798caf019fd09af5c6a30ec633 (diff)
downloadpygobject-fef1e60f6e20022fac7f1f1268004b6a022491dd.tar.gz
Merge branch 'pygobject-2-28' into windows
Diffstat (limited to 'demos')
-rw-r--r--demos/gtk-demo/demos/Entry/search_entry.py252
-rw-r--r--demos/gtk-demo/demos/Icon View/__init__.py0
-rw-r--r--demos/gtk-demo/demos/Icon View/iconviewbasics.py218
-rw-r--r--demos/gtk-demo/demos/Icon View/iconviewedit.py101
-rw-r--r--demos/gtk-demo/demos/Tree View/__init__.py0
-rw-r--r--demos/gtk-demo/demos/Tree View/liststore.py205
-rw-r--r--demos/gtk-demo/demos/appwindow.py64
-rw-r--r--demos/gtk-demo/demos/assistant.py3
-rw-r--r--demos/gtk-demo/demos/builder.py3
-rw-r--r--demos/gtk-demo/demos/button_box.py10
-rw-r--r--demos/gtk-demo/demos/clipboard.py40
-rw-r--r--demos/gtk-demo/demos/colorselector.py46
-rw-r--r--demos/gtk-demo/demos/combobox.py83
-rw-r--r--demos/gtk-demo/demos/dialogs.py153
-rw-r--r--demos/gtk-demo/demos/drawingarea.py147
-rw-r--r--demos/gtk-demo/demos/expander.py60
-rw-r--r--demos/gtk-demo/demos/images.py312
-rw-r--r--demos/gtk-demo/demos/infobars.py99
-rw-r--r--demos/gtk-demo/demos/links.py74
-rw-r--r--demos/gtk-demo/demos/menus.py122
-rw-r--r--demos/gtk-demo/demos/pickers.py74
-rw-r--r--demos/gtk-demo/demos/pixbuf.py183
-rw-r--r--demos/gtk-demo/demos/printing.py173
-rw-r--r--demos/gtk-demo/demos/rotatedtext.py196
-rwxr-xr-xdemos/gtk-demo/gtk-demo.py22
25 files changed, 2409 insertions, 231 deletions
diff --git a/demos/gtk-demo/demos/Entry/search_entry.py b/demos/gtk-demo/demos/Entry/search_entry.py
new file mode 100644
index 00000000..bf290f40
--- /dev/null
+++ b/demos/gtk-demo/demos/Entry/search_entry.py
@@ -0,0 +1,252 @@
+#!/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 = "Search Entry"
+description = """
+GtkEntry allows to display icons and progress information. This demo shows how to use these features in a search entry.
+ """
+
+from gi.repository import Gtk, Gdk, GdkPixbuf, GLib, GObject
+
+(PIXBUF_COL,
+ TEXT_COL) = range(2)
+
+class SearchboxApp:
+ def __init__(self, demoapp):
+ self.demoapp = demoapp
+
+ self.window = Gtk.Dialog('Search Entry',
+ buttons=(Gtk.STOCK_CLOSE, Gtk.ResponseType.NONE))
+
+ self.window.connect('response', lambda x, y: self.window.destroy())
+ self.window.connect('destroy', Gtk.main_quit)
+
+ content_area = self.window.get_content_area()
+
+ vbox = Gtk.VBox(spacing=5)
+ content_area.pack_start(vbox, True, True, 0)
+ vbox.set_border_width(5)
+
+ label = Gtk.Label()
+ label.set_markup('Search entry demo')
+ vbox.pack_start(label, False, False, 0)
+
+ hbox = Gtk.HBox(homogeneous=False, spacing=10)
+ hbox.set_border_width(0)
+ vbox.pack_start(hbox, True, True, 0)
+
+ # Create our entry
+ entry = Gtk.Entry()
+ hbox.pack_start(entry, False, False, 0)
+
+ # Create the find and cancel buttons
+ notebook = Gtk.Notebook()
+ self.notebook = notebook
+ notebook.set_show_tabs(False)
+ notebook.set_show_border(False)
+ hbox.pack_start(notebook, False, False, 0)
+
+ find_button = Gtk.Button('Find')
+ find_button.connect('clicked', self.start_search, entry)
+ notebook.append_page(find_button, None)
+ find_button.show()
+
+ cancel_button = Gtk.Button('Cancel')
+ cancel_button.connect('clicked', self.stop_search, entry)
+ notebook.append_page(cancel_button, None)
+ cancel_button.show()
+
+ # Set up the search icon
+ self.search_by_name(None, entry)
+
+ # Set up the clear icon
+ entry.set_icon_from_stock(Gtk.EntryIconPosition.SECONDARY,
+ Gtk.STOCK_CLEAR)
+ self.text_changed_cb(entry, None, find_button)
+
+ entry.connect('notify::text', self.text_changed_cb, find_button)
+
+ entry.connect('activate', self.activate_cb)
+
+ # Create the menu
+ menu = self.create_search_menu(entry)
+ entry.connect('icon-press', self.icon_press_cb, menu)
+
+ # FIXME: this should take None for the detach callback
+ # but our callback implementation does not allow
+ # it yet, so we pass in a noop callback
+ menu.attach_to_widget(entry, self.detach)
+
+ # add accessible alternatives for icon functionality
+ entry.connect('populate-popup', self.entry_populate_popup)
+
+ self.window.show_all()
+
+ def detach(self, *args):
+ pass
+
+ def show_find_button(self):
+ self.notebook.set_current_page(0)
+
+ def show_cancel_button(self):
+ self.notebook.set_current_page(1)
+
+ def search_progress(self, entry):
+ entry.progress_pulse()
+ return True
+
+ def search_progress_done (self, entry):
+ entry.set_progress_fraction(0.0)
+
+ def finish_search(self, button, entry):
+ self.show_find_button()
+ GObject.source_remove(self.search_progress_id)
+ self.search_progress_done(entry)
+ self.search_progress_id = 0
+
+ return False
+
+ def start_search_feedback(self, entry):
+ self.search_progress_id = GObject.timeout_add(100,
+ self.search_progress,
+ entry)
+
+ return False
+
+ def start_search(self, button, entry):
+ self.show_cancel_button()
+ self.search_progress_id = GObject.timeout_add_seconds(1,
+ self.start_search_feedback,
+ entry)
+ self.finish_search_id = GObject.timeout_add_seconds(15,
+ self.finish_search,
+ button)
+
+ def stop_search(self, button, entry):
+ GObject.source_remove(self.finish_search_id)
+ self.finish_search(button, entry)
+
+ def clear_entry_swapped(self, widget, entry):
+ self.clear_entry(entry)
+
+ def clear_entry(self, entry):
+ entry.set_text('')
+
+ def search_by_name (self, item, entry):
+ entry.set_icon_from_stock(Gtk.EntryIconPosition.PRIMARY,
+ Gtk.STOCK_FIND)
+ entry.set_icon_tooltip_text(Gtk.EntryIconPosition.PRIMARY,
+ 'Search by name\n' + \
+ 'Click here to change the search type')
+
+ def search_by_description(self, item, entry):
+ entry.set_icon_from_stock (Gtk.EntryIconPosition.PRIMARY,
+ Gtk.STOCK_EDIT)
+ entry.set_icon_tooltip_text(Gtk.EntryIconPosition.PRIMARY,
+ 'Search by description\n' + \
+ 'Click here to change the search type')
+
+ def search_by_file(self, item, entry):
+ entry.set_icon_from_stock(Gtk.EntryIconPosition.PRIMARY,
+ Gtk.STOCK_OPEN)
+ entry.set_icon_tooltip_text(Gtk.EntryIconPosition.PRIMARY,
+ 'Search by file name\n' + \
+ 'Click here to change the search type')
+
+ def create_search_menu(self, entry):
+ menu = Gtk.Menu()
+
+ item = Gtk.ImageMenuItem.new_with_mnemonic('Search by _name')
+ image = Gtk.Image.new_from_stock(Gtk.STOCK_FIND, Gtk.IconSize.MENU)
+ item.set_image(image)
+ item.set_always_show_image(True)
+ item.connect('activate', self.search_by_name, entry)
+ menu.append(item)
+
+ item = Gtk.ImageMenuItem.new_with_mnemonic('Search by _description')
+ image = Gtk.Image.new_from_stock(Gtk.STOCK_EDIT, Gtk.IconSize.MENU)
+ item.set_image(image)
+ item.set_always_show_image(True)
+ item.connect('activate', self.search_by_description, entry)
+ menu.append(item)
+
+ item = Gtk.ImageMenuItem.new_with_mnemonic('Search by _file name')
+ image = Gtk.Image.new_from_stock(Gtk.STOCK_OPEN, Gtk.IconSize.MENU)
+ item.set_image(image)
+ item.set_always_show_image(True)
+ item.connect('activate', self.search_by_name, entry)
+ menu.append(item)
+
+ menu.show_all()
+
+ return menu
+
+
+ def icon_press_cb(self, entry, position, event, menu):
+ if position == Gtk.EntryIconPosition.PRIMARY:
+ menu.popup(None, None, None, None,
+ event.button, event.time)
+ else:
+ self.clear_entry(entry)
+
+ def text_changed_cb(self, entry, pspec, button):
+ has_text = entry.get_text_length() > 0
+ entry.set_icon_sensitive(Gtk.EntryIconPosition.SECONDARY, has_text)
+ button.set_sensitive(has_text)
+
+ def activate_cb (self, entry, button):
+ if self.search_progress_id != 0:
+ return
+ self.start_search(button, entry)
+
+ def search_entry_destroyed(self, widget):
+ if finish_search_id != 0:
+ GObject.source_remove(finish_search_id)
+ if search_progress_id != 0:
+ GObject.source_remove(search_progress_id)
+
+ self.window = None
+
+ def entry_populate_popup(self, entry, menu):
+ has_text = entry.get_text_length() > 0
+
+ item = Gtk.SeparatorMenuItem()
+ item.show()
+ menu.append(item)
+
+ item = Gtk.MenuItem.new_with_mnemonic("C_lear")
+ item.show()
+ item.connect('activate', self.clear_entry_swapped, entry)
+ menu.append(item)
+ item.set_sensitive(has_text)
+
+ search_menu = self.create_search_menu(entry)
+ item = Gtk.MenuItem.new_with_label('Search by')
+ item.show()
+ item.set_submenu(search_menu)
+ menu.append (item)
+
+def main(demoapp=None):
+ app = SearchboxApp(demoapp)
+ Gtk.main()
+
+if __name__ == '__main__':
+ main()
diff --git a/demos/gtk-demo/demos/Icon View/__init__.py b/demos/gtk-demo/demos/Icon View/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/demos/gtk-demo/demos/Icon View/__init__.py
diff --git a/demos/gtk-demo/demos/Icon View/iconviewbasics.py b/demos/gtk-demo/demos/Icon View/iconviewbasics.py
new file mode 100644
index 00000000..e2cf45ce
--- /dev/null
+++ b/demos/gtk-demo/demos/Icon View/iconviewbasics.py
@@ -0,0 +1,218 @@
+#!/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 = "Icon View Basics"
+description = """
+The GtkIconView widget is used to display and manipulate icons. It uses a GtkTreeModel for data storage, so the list store example might be helpful.
+We also use the Gio.File API to get the icons for each file type.
+ """
+
+import os
+from gi.repository import GLib, Gtk, Gio, GdkPixbuf
+import glib
+
+class IconViewApp:
+ (COL_PATH,
+ COL_DISPLAY_NAME,
+ COL_PIXBUF,
+ COL_IS_DIRECTORY,
+ NUM_COLS) = range(5)
+
+ def __init__(self, demoapp):
+ self.pixbuf_lookup = {}
+
+ self.demoapp = demoapp
+
+ self.window = Gtk.Window()
+ self.window.set_title('Gtk.IconView demo')
+ self.window.set_default_size(650, 400)
+ self.window.connect('destroy', Gtk.main_quit)
+
+ vbox = Gtk.VBox()
+ self.window.add(vbox)
+
+ tool_bar = Gtk.Toolbar()
+ vbox.pack_start(tool_bar, False, False, 0)
+
+ up_button = Gtk.ToolButton(stock_id=Gtk.STOCK_GO_UP)
+ up_button.set_is_important(True)
+ up_button.set_sensitive(False)
+ tool_bar.insert(up_button, -1)
+
+ home_button = Gtk.ToolButton(stock_id=Gtk.STOCK_HOME)
+ home_button.set_is_important(True)
+ tool_bar.insert(home_button, -1)
+
+ sw = Gtk.ScrolledWindow()
+ sw.set_shadow_type(Gtk.ShadowType.ETCHED_IN)
+ sw.set_policy(Gtk.PolicyType.AUTOMATIC,
+ Gtk.PolicyType.AUTOMATIC)
+
+ vbox.pack_start(sw, True, True, 0)
+
+ # create the store and fill it with content
+ self.parent_dir = '/'
+ store = self.create_store()
+ self.fill_store(store)
+
+ icon_view = Gtk.IconView(model=store)
+ icon_view.set_selection_mode(Gtk.SelectionMode.MULTIPLE)
+ sw.add(icon_view)
+
+ # connect to the 'clicked' signal of the "Up" tool button
+ up_button.connect('clicked', self.up_clicked, store)
+
+ # connect to the 'clicked' signal of the "home" tool button
+ home_button.connect('clicked', self.home_clicked, store)
+
+ self.up_button = up_button
+ self.home_button = home_button
+
+ # we now set which model columns that correspond to the text
+ # and pixbuf of each item
+ icon_view.set_text_column(self.COL_DISPLAY_NAME)
+ icon_view.set_pixbuf_column(self.COL_PIXBUF)
+
+ # connect to the "item-activated" signal
+ icon_view.connect('item-activated', self.item_activated, store)
+ icon_view.grab_focus()
+
+ self.window.show_all()
+
+ def sort_func(self, store, a_iter, b_iter, user_data):
+ (a_name, a_is_dir) = store.get(a_iter,
+ self.COL_DISPLAY_NAME,
+ self.COL_IS_DIRECTORY)
+
+ (b_name, b_is_dir) = store.get(b_iter,
+ self.COL_DISPLAY_NAME,
+ self.COL_IS_DIRECTORY)
+
+ if a_name is None:
+ a_name = ''
+
+ if b_name is None:
+ b_name = ''
+
+ if (not a_is_dir) and b_is_dir:
+ return 1
+ elif a_is_dir and (not b_is_dir):
+ return -1
+ elif a_name > b_name:
+ return 1
+ elif a_name < b_name:
+ return -1
+ else:
+ return 0
+
+ def up_clicked(self, item, store):
+ self.parent_dir = os.path.split(self.parent_dir)[0]
+ self.fill_store(store)
+ # de-sensitize the up button if we are at the root
+ self.up_button.set_sensitive(self.parent_dir != '/')
+
+ def home_clicked(self, item, store):
+ self.parent_dir = GLib.get_home_dir()
+ self.fill_store(store)
+
+ # Sensitize the up button
+ self.up_button.set_sensitive(True)
+
+ def item_activated(self, icon_view, tree_path, store):
+ iter_ = store.get_iter(tree_path)
+ (path, is_dir) = store.get(iter_, self.COL_PATH, self.COL_IS_DIRECTORY)
+ if not is_dir:
+ return
+
+ self.parent_dir = path
+ self.fill_store(store)
+
+ self.up_button.set_sensitive(True)
+
+ def create_store(self):
+ store = Gtk.ListStore(str, str, GdkPixbuf.Pixbuf, bool)
+
+ # set sort column and function
+ store.set_default_sort_func(self.sort_func)
+ store.set_sort_column_id(-1,
+ Gtk.SortType.ASCENDING)
+
+ return store
+
+ def file_to_icon_pixbuf(self, path):
+ pixbuf = None
+
+ # get the theme icon
+ f = Gio.file_new_for_path(path)
+ info = f.query_info(Gio.FILE_ATTRIBUTE_STANDARD_ICON,
+ Gio.FileQueryInfoFlags.NONE,
+ None)
+ gicon = info.get_icon()
+
+ # check to see if it is an image format we support
+ for format in GdkPixbuf.Pixbuf.get_formats():
+ for mime_type in format.get_mime_types():
+ content_type = Gio.content_type_from_mime_type(mime_type)
+ if content_type is not None:
+ break
+
+ format_gicon = Gio.content_type_get_icon(content_type)
+ if format_gicon.equal(gicon):
+ gicon = f.icon_new()
+ break
+
+ if gicon in self.pixbuf_lookup:
+ return self.pixbuf_lookup[gicon]
+
+ if isinstance(gicon, Gio.ThemedIcon):
+ names = gicon.get_names()
+ icon_theme = Gtk.IconTheme.get_default()
+ for name in names:
+ try:
+ pixbuf = icon_theme.load_icon(name, 64, 0)
+ break
+ except glib.GError:
+ pass
+
+ self.pixbuf_lookup[gicon] = pixbuf
+
+ elif isinstance(gicon, Gio.FileIcon):
+ icon_file = gicon.get_file()
+ path = icon_file.get_path()
+ pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(path, 72, 72)
+ self.pixbuf_lookup[gicon] = pixbuf
+
+ return pixbuf
+
+ def fill_store(self, store):
+ store.clear()
+ for name in os.listdir(self.parent_dir):
+ path = os.path.join(self.parent_dir, name)
+ is_dir = os.path.isdir(path)
+ pixbuf = self.file_to_icon_pixbuf(path)
+ store.append((path, name, pixbuf, is_dir))
+
+def main(demoapp=None):
+ app = IconViewApp(demoapp)
+ Gtk.main()
+
+if __name__ == '__main__':
+ main()
diff --git a/demos/gtk-demo/demos/Icon View/iconviewedit.py b/demos/gtk-demo/demos/Icon View/iconviewedit.py
new file mode 100644
index 00000000..9233b09b
--- /dev/null
+++ b/demos/gtk-demo/demos/Icon View/iconviewedit.py
@@ -0,0 +1,101 @@
+#!/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 = "Editing and Drag-and-Drop"
+description = """
+The GtkIconView widget supports Editing and Drag-and-Drop. This example also demonstrates using the generic GtkCellLayout interface to set up cell renderers in an icon view.
+"""
+
+from gi.repository import Gtk, Gdk, GdkPixbuf
+
+class IconviewEditApp:
+ COL_TEXT = 0
+ NUM_COLS = 1
+
+ def __init__(self):
+ self.window = Gtk.Window()
+ self.window.set_title('Editing and Drag-and-Drop')
+ self.window.set_border_width(8)
+ self.window.connect('destroy', Gtk.main_quit)
+
+ store = Gtk.ListStore(str)
+ colors = ['Red', 'Green', 'Blue', 'Yellow']
+ store.clear()
+ for c in colors:
+ store.append([c])
+
+ icon_view = Gtk.IconView(model=store)
+ icon_view.set_selection_mode(Gtk.SelectionMode.SINGLE)
+ icon_view.set_item_orientation(Gtk.Orientation.HORIZONTAL)
+ icon_view.set_columns(2)
+ icon_view.set_reorderable(True)
+
+ renderer = Gtk.CellRendererPixbuf()
+ icon_view.pack_start(renderer, True)
+ icon_view.set_cell_data_func(renderer,
+ self.set_cell_color,
+ None)
+
+ renderer = Gtk.CellRendererText()
+ icon_view.pack_start(renderer, True)
+ renderer.props.editable = True
+ renderer.connect('edited', self.edited, icon_view)
+ icon_view.add_attribute(renderer, 'text', self.COL_TEXT)
+
+ self.window.add(icon_view)
+
+ self.window.show_all()
+
+ def set_cell_color (self,
+ cell_layout,
+ cell,
+ tree_model,
+ iter_,
+ icon_view):
+
+ # FIXME return single element instead of tuple
+ text = tree_model.get(iter_, self.COL_TEXT)[0]
+ color = Gdk.color_parse(text)[1]
+ pixel = 0
+ if color is not None:
+ pixel = \
+ (color.red >> 8) << 24 | \
+ (color.green >> 8) << 16 | \
+ (color.blue >> 8) << 8
+
+ pixbuf = GdkPixbuf.Pixbuf.new(GdkPixbuf.Colorspace.RGB, False, 8, 24, 24)
+ pixbuf.fill(pixel)
+
+ cell.props.pixbuf = pixbuf
+
+ def edited (self, cell, path_string, text, icon_view):
+ model = icon_view.get_model()
+ path = Gtk.TreePath(path_string)
+
+ iter_ = model.get_iter(path)
+ model.set_row(iter_, [text])
+
+def main(demoapp=None):
+ app = IconviewEditApp()
+ Gtk.main()
+
+if __name__ == '__main__':
+ main()
diff --git a/demos/gtk-demo/demos/Tree View/__init__.py b/demos/gtk-demo/demos/Tree View/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/demos/gtk-demo/demos/Tree View/__init__.py
diff --git a/demos/gtk-demo/demos/Tree View/liststore.py b/demos/gtk-demo/demos/Tree View/liststore.py
new file mode 100644
index 00000000..0be862ef
--- /dev/null
+++ b/demos/gtk-demo/demos/Tree View/liststore.py
@@ -0,0 +1,205 @@
+#!/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 = "List Store"
+description = """
+The GtkListStore is used to store data in list form, to be used later on by a GtkTreeView to display it. This demo builds a simple GtkListStore and displays it. See the Stock Browser demo for a more advanced example.
+"""
+
+from gi.repository import Gtk, GObject
+import sys
+
+class Bug:
+ def __init__(self, is_fixed, number, severity, description):
+ self.is_fixed = is_fixed
+ self.number = number
+ self.severity = severity
+ self.description = description
+
+# initial data we use to fill in the store
+data = [Bug(False, 60482, "Normal", "scrollable notebooks and hidden tabs"),
+ Bug(False, 60620, "Critical", "gdk_window_clear_area (gdkwindow-win32.c) is not thread-safe"),
+ Bug(False, 50214, "Major", "Xft support does not clean up correctly"),
+ Bug(True, 52877, "Major", "GtkFileSelection needs a refresh method. "),
+ Bug(False, 56070, "Normal", "Can't click button after setting in sensitive"),
+ Bug(True, 56355, "Normal", "GtkLabel - Not all changes propagate correctly"),
+ Bug(False, 50055, "Normal", "Rework width/height computations for TreeView"),
+ Bug(False, 58278, "Normal", "gtk_dialog_set_response_sensitive () doesn't work"),
+ Bug(False, 55767, "Normal", "Getters for all setters"),
+ Bug(False, 56925, "Normal", "Gtkcalender size"),
+ Bug(False, 56221, "Normal", "Selectable label needs right-click copy menu"),
+ Bug(True, 50939, "Normal", "Add shift clicking to GtkTextView"),
+ Bug(False, 6112, "Enhancement","netscape-like collapsable toolbars"),
+ Bug(False, 1, "Normal", "First bug :=)")]
+
+class ListStoreApp:
+ (COLUMN_FIXED,
+ COLUMN_NUMBER,
+ COLUMN_SEVERITY,
+ COLUMN_DESCRIPTION,
+ COLUMN_PULSE,
+ COLUMN_ICON,
+ COLUMN_ACTIVE,
+ COLUMN_SENSITIVE,
+ NUM_COLUMNS) = range(9)
+
+ def __init__(self):
+ self.window = Gtk.Window()
+ self.window.set_title('Gtk.ListStore Demo')
+ self.window.connect('destroy', Gtk.main_quit)
+
+ vbox = Gtk.VBox(spacing=8)
+ self.window.add(vbox)
+
+ label = Gtk.Label('This is the bug list (note: not based on real data, it would be nice to have a nice ODBC interface to bugzilla or so, though).')
+ vbox.pack_start(label, False, False, 0)
+
+ sw = Gtk.ScrolledWindow()
+ sw.set_shadow_type(Gtk.ShadowType.ETCHED_IN)
+ sw.set_policy(Gtk.PolicyType.NEVER,
+ Gtk.PolicyType.AUTOMATIC)
+ vbox.pack_start(sw, True, True, 0)
+
+ self.create_model()
+ treeview = Gtk.TreeView(model=self.model)
+ treeview.set_rules_hint(True)
+ treeview.set_search_column(self.COLUMN_DESCRIPTION)
+ sw.add(treeview)
+
+ self.add_columns(treeview)
+
+ self.window.set_default_size(280, 250)
+ self.window.show_all()
+
+ self.window.connect('delete-event', self.window_closed)
+ self.timeout = GObject.timeout_add (80, self.spinner_timeout)
+
+ def window_closed(self, window, event):
+ if self.timeout != 0:
+ GObject.source_remove(self.timeout)
+
+ def spinner_timeout(self):
+ if self.model is None:
+ return False
+
+ iter_ = self.model.get_iter_first()
+ pulse = self.model.get(iter_, self.COLUMN_PULSE)[0]
+ if pulse == 999999999:
+ pulse = 0
+ else:
+ pulse += 1
+
+ self.model.set_value(iter_, self.COLUMN_PULSE, pulse)
+ self.model.set_value(iter_, self.COLUMN_ACTIVE, True)
+
+ return True
+
+ def create_model(self):
+ self.model = Gtk.ListStore(bool,
+ GObject.TYPE_INT,
+ str,
+ str,
+ GObject.TYPE_INT,
+ str,
+ bool,
+ bool)
+
+ col = 0
+ for bug in data:
+ if col == 1 or col == 3:
+ icon_name = 'battery-critical-charging-symbolic'
+ else:
+ icon_name = ''
+ if col == 3:
+ is_sensitive = False
+ else:
+ is_sensitive = True
+
+ self.model.append([bug.is_fixed,
+ bug.number,
+ bug.severity,
+ bug.description,
+ 0,
+ icon_name,
+ False,
+ is_sensitive])
+ col += 1
+
+ def add_columns(self, treeview):
+ model = treeview.get_model()
+
+ # column for is_fixed toggle
+ renderer = Gtk.CellRendererToggle()
+ renderer.connect('toggled', self.is_fixed_toggled, model)
+
+ column = Gtk.TreeViewColumn("Fixed?", renderer,
+ active=self.COLUMN_FIXED)
+ column.set_fixed_width(50)
+ column.set_sizing(Gtk.TreeViewColumnSizing.FIXED)
+ treeview.append_column(column)
+
+ # column for severities
+ renderer = Gtk.CellRendererText()
+ column = Gtk.TreeViewColumn("Severity", renderer,
+ text=self.COLUMN_SEVERITY)
+ column.set_sort_column_id(self.COLUMN_SEVERITY)
+ treeview.append_column(column)
+
+ # column for description
+ renderer = Gtk.CellRendererText()
+ column = Gtk.TreeViewColumn("Description", renderer,
+ text=self.COLUMN_DESCRIPTION)
+ column.set_sort_column_id(self.COLUMN_DESCRIPTION)
+ treeview.append_column(column)
+
+ # column for spinner
+ renderer = Gtk.CellRendererSpinner()
+ column = Gtk.TreeViewColumn("Spinning", renderer,
+ pulse=self.COLUMN_PULSE,
+ active=self.COLUMN_ACTIVE)
+ column.set_sort_column_id(self.COLUMN_PULSE)
+ treeview.append_column(column)
+
+ # column for symbolic icon
+ renderer = Gtk.CellRendererPixbuf()
+ renderer.props.follow_state = True
+ column = Gtk.TreeViewColumn("Symbolic icon", renderer,
+ icon_name=self.COLUMN_ICON,
+ sensitive=self.COLUMN_SENSITIVE)
+ column.set_sort_column_id(self.COLUMN_ICON)
+ treeview.append_column(column)
+
+ def is_fixed_toggled(self, cell, path_str, model):
+ # get toggled iter
+ iter_ = model.get_iter(path_str)
+ is_fixed = model.get_value(iter_, self.COLUMN_FIXED)
+
+ # do something with value
+ is_fixed ^= 1
+
+ model.set_value(iter_, self.COLUMN_FIXED, is_fixed)
+
+def main(demoapp=None):
+ app = ListStoreApp()
+ Gtk.main()
+
+if __name__ == '__main__':
+ main()
diff --git a/demos/gtk-demo/demos/appwindow.py b/demos/gtk-demo/demos/appwindow.py
index 8b7c99c3..a71720f9 100644
--- a/demos/gtk-demo/demos/appwindow.py
+++ b/demos/gtk-demo/demos/appwindow.py
@@ -24,11 +24,9 @@ description = """
Demonstrates a typical application window with menubar, toolbar, statusbar.
"""
-# See FIXME's
-is_fully_bound = False
-
from gi.repository import Gtk, GdkPixbuf, Gdk
import sys, os
+import glib
global infobar
global window
@@ -38,7 +36,7 @@ global _demoapp
def widget_destroy(widget, button):
widget.destroy()
-def activate_action(action):
+def activate_action(action, user_data):
global window
name = action.get_name()
@@ -50,16 +48,15 @@ def activate_action(action):
return
- dialog = Gtk.MessageDialog(message_type=Gtk.MessageType.INFO,
+ dialog = Gtk.MessageDialog(parent=window,
+ message_type=Gtk.MessageType.INFO,
buttons=Gtk.ButtonsType.CLOSE,
text='You activated action: "%s" of type %s' % (name, _type))
- # FIXME: this should be done in the constructor
- dialog.set_transient_for(window)
dialog.connect('response', widget_destroy)
dialog.show()
-def activate_radio_action(action, current):
+def activate_radio_action(action, current, user_data):
global infobar
global messagelabel
@@ -87,29 +84,7 @@ def update_statusbar(buffer, statusbar):
def mark_set_callback(buffer, new_location, mark, data):
update_statusbar(buffer, data)
-def update_resize_grip(widget, event, statusbar):
- # FIXME: for some reason we get attribute errors in handlers
- # if we try to access flags without first evaluating
- # the parent struct
- Gdk.WindowState
-
- # FIXME: event should be a Gdk.EventWindowState but is only a Gdk.Event
- if event.window_state.changed_mask and (Gdk.WindowState.MAXIMIZED or
- Gdk.WindowState.FULLSCREEN):
-
- maximized = event.window_state.new_window_state and (Gdk.WindowState.MAXIMIZED or
- Gdk.WindowState.FULLSCREEN)
- statusbar.set_has_resize_grip(not maximized)
-
-def activate_email(about, link, data):
- text = 'send mail to %s' % (link,)
- print text
-
-def activate_url(about, link, data):
- text = 'show url %s' % (link,)
- print text
-
-def about_cb(widget):
+def about_cb(widget, data):
global window
authors = ['John (J5) Palmieri',
@@ -137,13 +112,16 @@ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
"""
filename = os.path.join('data', 'gtk-logo-rgb.gif')
- pixbuf = GdkPixbuf.Pixbuf.new_from_file(filename)
+ try:
+ pixbuf = GdkPixbuf.Pixbuf.new_from_file(filename)
+ except glib.GError:
+ filename = os.path.join('demos', filename)
+ pixbuf = GdkPixbuf.Pixbuf.new_from_file(filename)
+
transparent = pixbuf.add_alpha(True, 0xff, 0xff, 0xff)
- Gtk.AboutDialog.set_email_hook(activate_email, None)
- Gtk.AboutDialog.set_url_hook(activate_url, None)
- # FIXME: override Gtk.show_about_dialog
- # make about dailog constructor take a parent argument
- about = Gtk.AboutDialog(program_name='GTK+ Code Demos',
+ about = Gtk.AboutDialog(
+ parent=window,
+ program_name='GTK+ Code Demos',
version='0.1',
copyright='(C) 2010 The PyGI Team',
license=license,
@@ -152,12 +130,10 @@ Boston, MA 02111-1307, USA.
authors=authors,
documenters=documentors,
logo=transparent,
- title='About GTK+ Code Demos')
+ title='About GTK+ Code Demos')
- about.set_transient_for(window)
about.connect('response', widget_destroy)
- about.show()
-
+ about.run()
action_entries = (
("FileMenu", None, "_File"), # name, stock id, label
@@ -337,8 +313,8 @@ def main(demoapp=None):
window.set_title('Application Window')
window.set_icon_name('gtk-open')
window.connect_after('destroy', _quit)
- table = Gtk.Table(n_rows=1,
- n_columns=5,
+ table = Gtk.Table(rows=1,
+ columns=5,
homogeneous=False)
window.add(table)
@@ -412,8 +388,6 @@ def main(demoapp=None):
buffer.connect('changed', update_statusbar, statusbar)
buffer.connect('mark_set', mark_set_callback, statusbar)
- window.connect('window_state_event', update_resize_grip, statusbar)
-
update_statusbar(buffer, statusbar);
window.set_default_size(200, 200)
diff --git a/demos/gtk-demo/demos/assistant.py b/demos/gtk-demo/demos/assistant.py
index e1a7ecde..8285d043 100644
--- a/demos/gtk-demo/demos/assistant.py
+++ b/demos/gtk-demo/demos/assistant.py
@@ -26,9 +26,6 @@ an operation into several simpler sequential steps, and to guide the user
through these steps.
"""
-# See FIXME's
-is_fully_bound = True
-
from gi.repository import Gtk, GdkPixbuf
class AssistantApp:
diff --git a/demos/gtk-demo/demos/builder.py b/demos/gtk-demo/demos/builder.py
index 5ecf748f..15af2543 100644
--- a/demos/gtk-demo/demos/builder.py
+++ b/demos/gtk-demo/demos/builder.py
@@ -24,9 +24,6 @@ description = """
Demonstrates an interface loaded from a XML description.
"""
-# See FIXME's
-is_fully_bound = True
-
from gi.repository import Gtk, GdkPixbuf, Gdk
import os
diff --git a/demos/gtk-demo/demos/button_box.py b/demos/gtk-demo/demos/button_box.py
index 7cd8d52e..04d34196 100644
--- a/demos/gtk-demo/demos/button_box.py
+++ b/demos/gtk-demo/demos/button_box.py
@@ -24,9 +24,6 @@ description = """
The Button Box widgets are used to arrange buttons with padding.
"""
-# See FIXME's
-is_fully_bound = False
-
from gi.repository import Gtk
class ButtonBoxApp:
@@ -101,14 +98,13 @@ class ButtonBoxApp:
bbox.set_layout(layout)
bbox.set_spacing(spacing)
- # FIXME: GtkButton consturctor should take a stock_id
- button = Gtk.Button.new_from_stock(Gtk.STOCK_OK)
+ button = Gtk.Button(Gtk.STOCK_OK)
bbox.add(button)
- button = Gtk.Button.new_from_stock(Gtk.STOCK_CANCEL)
+ button = Gtk.Button(Gtk.STOCK_CANCEL)
bbox.add(button)
- button = Gtk.Button.new_from_stock(Gtk.STOCK_HELP)
+ button = Gtk.Button(Gtk.STOCK_HELP)
bbox.add(button)
return frame
diff --git a/demos/gtk-demo/demos/clipboard.py b/demos/gtk-demo/demos/clipboard.py
index fa8f2da9..759c0f72 100644
--- a/demos/gtk-demo/demos/clipboard.py
+++ b/demos/gtk-demo/demos/clipboard.py
@@ -56,16 +56,14 @@ class ClipboardApp:
entry = Gtk.Entry()
hbox.pack_start(entry, True, True, 0)
- #FIXME: have the button constuctor take a stock_id
- # create button
- button = Gtk.Button.new_from_stock(Gtk.STOCK_COPY)
+ button = Gtk.Button(Gtk.STOCK_COPY)
hbox.pack_start(button, False, False, 0)
button.connect('clicked', self.copy_button_clicked, entry)
-
label = Gtk.Label(label='"Paste" will paste the text from the clipboard to the entry')
vbox.pack_start(label, False, False, 0)
+
hbox = Gtk.HBox(homogeneous=False, spacing=4)
hbox.set_border_width(8)
vbox.pack_start(hbox, False, False, 0)
@@ -73,9 +71,7 @@ class ClipboardApp:
# create secondary entry
entry = Gtk.Entry()
hbox.pack_start(entry, True, True, 0)
- #FIXME: have the button constuctor take a stock_id
- # create button
- button = Gtk.Button.new_from_stock(Gtk.STOCK_PASTE)
+ button = Gtk.Button(Gtk.STOCK_PASTE)
hbox.pack_start(button, False, False, 0)
button.connect('clicked', self.paste_button_clicked, entry)
@@ -95,16 +91,16 @@ class ClipboardApp:
hbox.add(ebox)
# make ebox a drag source
- Gtk.drag_source_set(ebox, Gdk.ModifierType.BUTTON1_MASK,
+ ebox.drag_source_set(Gdk.ModifierType.BUTTON1_MASK,
None, Gdk.DragAction.COPY)
- Gtk.drag_source_add_image_targets(ebox)
+ ebox.drag_source_add_image_targets()
ebox.connect('drag-begin', self.drag_begin, image)
ebox.connect('drag-data-get', self.drag_data_get, image)
# accept drops on ebox
- Gtk.drag_dest_set(ebox, Gtk.DestDefaults.ALL,
+ ebox.drag_dest_set(Gtk.DestDefaults.ALL,
None, Gdk.DragAction.COPY)
- Gtk.drag_dest_add_image_targets(ebox)
+ ebox.drag_dest_add_image_targets()
ebox.connect('drag-data-received', self.drag_data_received, image)
# context menu on ebox
@@ -119,16 +115,16 @@ class ClipboardApp:
hbox.add(ebox)
# make ebox a drag source
- Gtk.drag_source_set(ebox, Gdk.ModifierType.BUTTON1_MASK,
+ ebox.drag_source_set(Gdk.ModifierType.BUTTON1_MASK,
None, Gdk.DragAction.COPY)
- Gtk.drag_source_add_image_targets(ebox)
+ ebox.drag_source_add_image_targets()
ebox.connect('drag-begin', self.drag_begin, image)
ebox.connect('drag-data-get', self.drag_data_get, image)
# accept drops on ebox
- Gtk.drag_dest_set(ebox, Gtk.DestDefaults.ALL,
+ ebox.drag_dest_set(Gtk.DestDefaults.ALL,
None, Gdk.DragAction.COPY)
- Gtk.drag_dest_add_image_targets(ebox)
+ ebox.drag_dest_add_image_targets()
ebox.connect('drag-data-received', self.drag_data_received, image)
# context menu on ebox
@@ -138,7 +134,7 @@ class ClipboardApp:
#FIXME: Allow sending strings a Atoms and convert in PyGI
atom = Gdk.atom_intern('CLIPBOARD', True)
clipboard = Gtk.Clipboard.get(atom)
- clipboard.set_can_store(None, 0)
+ clipboard.set_can_store(None)
self.window.show_all()
@@ -183,7 +179,7 @@ class ClipboardApp:
selection_data.set_pixbuf(pixbuf)
def drag_data_received(self, widget, context, x, y, selection_data, info, time, data):
- if selection_data.length > 0:
+ if selection_data.get_length() > 0:
pixbuf = selection_data.get_pixbuf()
data.set_from_pixbuf(pixbuf)
@@ -205,24 +201,24 @@ class ClipboardApp:
data.set_from_pixbuf(pixbuf)
def button_press(self, widget, event, data):
- if event.button.button != 3:
+ if event.button != 3:
return False
- menu = Gtk.Menu()
+ self.menu = Gtk.Menu()
#FIXME: default constructor should take a stock property
item = Gtk.ImageMenuItem.new_from_stock(Gtk.STOCK_COPY, None)
item.connect('activate', self.copy_image, data)
item.show()
- menu.append(item)
+ self.menu.append(item)
#FIXME: default constructor should take a stock property
item = Gtk.ImageMenuItem.new_from_stock(Gtk.STOCK_PASTE, None)
item.connect('activate', self.paste_image, data)
item.show()
- menu.append(item)
+ self.menu.append(item)
- menu.popup(None, None, None, None, 3, event.button.time)
+ self.menu.popup(None, None, None, None, event.button, event.time)
def main(demoapp=None):
app = ClipboardApp()
diff --git a/demos/gtk-demo/demos/colorselector.py b/demos/gtk-demo/demos/colorselector.py
index 514718b7..f3dd74f7 100644
--- a/demos/gtk-demo/demos/colorselector.py
+++ b/demos/gtk-demo/demos/colorselector.py
@@ -32,9 +32,11 @@ from gi.repository import Gtk, Gdk
class ColorSelectorApp:
def __init__(self):
- # FIXME: we should allow Gdk.Color to be allocated without parameters
- # Also color doesn't seem to work
- self.color = Gdk.Color(0, 65535, 0)
+ self.color = Gdk.RGBA()
+ self.color.red = 0
+ self.color.blue = 1
+ self.color.green = 0
+ self.color.alpha = 1
self.window = Gtk.Window()
self.window.set_title('Color Selection')
@@ -52,12 +54,12 @@ class ColorSelectorApp:
vbox.pack_start(frame, True, True, 0)
self.da = Gtk.DrawingArea()
- self.da.connect('expose_event', self.expose_event_cb)
+ self.da.connect('draw', self.draw_cb)
# set a minimum size
self.da.set_size_request(200, 200)
# set the color
- self.da.modify_bg(Gtk.StateType.NORMAL, self.color)
+ self.da.override_background_color(0, self.color)
frame.add(self.da)
alignment = Gtk.Alignment(xalign=1.0,
@@ -74,42 +76,28 @@ class ColorSelectorApp:
self.window.show_all()
- def expose_event_cb(self, widget, event):
- window = widget.get_window()
-
- if window:
-
- style = widget.get_style()
- x = event.expose.area.x
- y = event.expose.area.y
- width = event.expose.area.width
- height = event.expose.area.height
-
- """
- Gdk.draw_rectangle(window,
- # FIXME: accessing style attribute segfaults
- style.bg_gc[Gtk.StateType.NORMAL],
- True,
- event.expose.area.x, event.expose.area.y,
- event.expose.area.width, event.expose.area.height)
- """
- return True
+ def draw_cb(self, widget, cairo_ctx):
+ style = widget.get_style_context()
+ bg_color = style.get_background_color(0)
+ Gdk.cairo_set_source_rgba(cairo_ctx, bg_color)
+ cairo_ctx.paint()
+ return True
def change_color_cb(self, button):
dialog = Gtk.ColorSelectionDialog(title='Changing color')
dialog.set_transient_for(self.window)
colorsel = dialog.get_color_selection()
- colorsel.set_previous_color(self.color)
- colorsel.set_current_color(self.color)
+ colorsel.set_previous_rgba(self.color)
+ colorsel.set_current_rgba(self.color)
colorsel.set_has_palette(True)
response = dialog.run()
if response == Gtk.ResponseType.OK:
- self.color = colorsel.get_current_color()
- self.da.modify_bg(Gtk.StateType.NORMAL, self.color)
+ self.color = colorsel.get_current_rgba()
+ self.da.override_background_color(0, self.color)
dialog.destroy()
diff --git a/demos/gtk-demo/demos/combobox.py b/demos/gtk-demo/demos/combobox.py
index 965457ee..1869dfd4 100644
--- a/demos/gtk-demo/demos/combobox.py
+++ b/demos/gtk-demo/demos/combobox.py
@@ -31,7 +31,7 @@ How the options are displayed is controlled by cell renderers.
# See FIXME's
is_fully_bound = False
-from gi.repository import Gtk, Gdk, GdkPixbuf, GLib
+from gi.repository import Gtk, Gdk, GdkPixbuf, GLib, GObject
(PIXBUF_COL,
TEXT_COL) = range(2)
@@ -45,15 +45,24 @@ class MaskEntry(Gtk.Entry):
self.connect('changed', self.changed_cb)
+ self.error_color = Gdk.RGBA()
+ self.error_color.red = 1.0
+ self.error_color.green = 0.9
+ self.error_color_blue = 0.9
+ self.error_color.alpha = 1.0
+
+ # workaround since override_color doesn't accept None yet
+ style_ctx = self.get_style_context()
+ self.normal_color = style_ctx.get_color(0)
+
def set_background(self):
- error_color = Gdk.Color(65535, 60000, 60000)
if self.mask:
if not GLib.regex_match_simple(self.mask,
self.get_text(), 0, 0):
- self.modify_base(Gtk.StateType.NORMAL, error_color)
+ self.override_color(0, self.error_color)
return
- self.modify_base(Gtk.StateType.NORMAL, None)
+ self.override_color(0, self.normal_color)
def changed_cb(self, entry):
self.set_background()
@@ -113,13 +122,12 @@ class ComboboxApp:
combo.add_attribute(renderer, 'text', 0)
combo.set_cell_data_func(renderer, self.is_capital_sensistive, None)
- # FIXME: make new_from_indices work
- # make constructor take list or string of indices
- path = Gtk.TreePath.new_from_string('0:8')
- (success, treeiter) = model.get_iter(path)
+ path = Gtk.TreePath('0:8')
+ treeiter = model.get_iter(path)
combo.set_active_iter(treeiter)
# A GtkComboBoxEntry with validation.
+
frame = Gtk.Frame(label='Editable')
vbox.pack_start(frame, False, False, 0)
@@ -127,22 +135,68 @@ class ComboboxApp:
box.set_border_width(5)
frame.add(box)
- combo = Gtk.ComboBoxEntry.new_text()
+ combo = Gtk.ComboBoxText.new_with_entry()
self.fill_combo_entry(combo)
box.add(combo)
entry = MaskEntry(mask='^([0-9]*|One|Two|2\302\275|Three)$')
- combo.remove(combo.get_child())
+ Gtk.Container.remove(combo, combo.get_child())
combo.add(entry)
+ # A combobox with string IDs
+
+ frame = Gtk.Frame(label='String IDs')
+ vbox.pack_start(frame, False, False, 0)
+
+ box = Gtk.VBox(homogeneous=False, spacing=0)
+ box.set_border_width(5)
+ frame.add(box)
+
+ # FIXME: model is not setup when constructing Gtk.ComboBoxText()
+ # so we call new() - Gtk should fix this to setup the model
+ # in __init__, not in the constructor
+ combo = Gtk.ComboBoxText.new()
+ combo.append('never', 'Not visible')
+ combo.append('when-active', 'Visible when active')
+ combo.append('always', 'Always visible')
+ box.add(combo)
+
+ entry = Gtk.Entry()
+
+ # FIXME: a bug in PyGObject does not allow us to access dynamic
+ # methods on GObject.Object, so bind properties the hard way
+ # GObject.Object.bind_property(combo, 'active-id',
+ # entry, 'text',
+ # GObject.BindingFlags.BIDIRECTIONAL)
+ self.combo_notify_id = \
+ combo.connect('notify::active-id',
+ self.combo_active_id_changed, entry)
+ self.entry_notify_id = \
+ entry.connect('notify::text',
+ self.entry_text_changed, combo)
+
+ box.add(entry)
self.window.show_all()
+ def combo_active_id_changed(self, combo, pspec, entry):
+ entry.disconnect(self.entry_notify_id)
+ entry.set_text(combo.get_property('active-id'))
+ self.entry_notify_id = \
+ entry.connect('notify::text',
+ self.entry_text_changed, combo)
+
+ def entry_text_changed(self, entry, pspec, combo):
+ combo.disconnect(self.combo_notify_id)
+ combo.set_property('active-id', entry.get_text())
+ self.combo_notify_id = \
+ combo.connect('notify::active-id',
+ self.combo_active_id_changed, entry)
+
def strip_underscore(self, s):
return s.replace('_', '')
def create_stock_icon_store(self):
- item = Gtk.StockItem()
stock_id = (Gtk.STOCK_DIALOG_WARNING,
Gtk.STOCK_STOP,
Gtk.STOCK_NEW,
@@ -156,8 +210,7 @@ class ComboboxApp:
for id in stock_id:
if id is not None:
pixbuf = cellview.render_icon(id, Gtk.IconSize.BUTTON, None)
- # FIXME: item should be annotated (out)
- Gtk.stock_lookup(id, item)
+ item = Gtk.stock_lookup(id)
label = self.strip_underscore(item.label)
store.append((pixbuf, label))
else:
@@ -173,7 +226,7 @@ class ComboboxApp:
"""
path = tree_model.get_path(treeiter)
- indices = path.get_indices_with_depth()
+ indices = path.get_indices()
sensitive = not(indices[0] == 1)
@@ -188,7 +241,7 @@ class ComboboxApp:
path = model.get_path(treeiter)
- indices = path.get_indices_with_depth()
+ indices = path.get_indices()
result = (indices[0] == 4)
return result
diff --git a/demos/gtk-demo/demos/dialogs.py b/demos/gtk-demo/demos/dialogs.py
new file mode 100644
index 00000000..78ad5df0
--- /dev/null
+++ b/demos/gtk-demo/demos/dialogs.py
@@ -0,0 +1,153 @@
+#!/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 = "Dialog and Message Boxes"
+description = """
+Dialog widgets are used to pop up a transient window for user feedback.
+"""
+
+from gi.repository import Gtk, GdkPixbuf
+
+class DialogsApp:
+ def __init__(self):
+ self.dialog_counter = 1
+
+ self.window = Gtk.Window(title="Dialogs")
+ self.window.set_border_width(8)
+ self.window.connect('destroy', Gtk.main_quit)
+
+ frame = Gtk.Frame(label="Dialogs")
+ self.window.add(frame)
+
+ vbox = Gtk.VBox(spacing=8)
+ vbox.set_border_width(8)
+ frame.add(vbox)
+
+ # Standard message dialog
+ hbox = Gtk.HBox(spacing=8);
+ vbox.pack_start(hbox, False, False, 0)
+ button = Gtk.Button.new_with_mnemonic("_Message Dialog");
+
+ button.connect('clicked',
+ self._message_dialog_clicked)
+ hbox.pack_start(button, False, False, 0)
+
+ vbox.pack_start(Gtk.HSeparator(),
+ False, False, 0);
+
+ # Interactive dialog
+ hbox = Gtk.HBox(spacing=8);
+ vbox.pack_start(hbox, False, False, 0)
+ vbox2 = Gtk.VBox(spacing=0);
+ button = Gtk.Button.new_with_mnemonic("_Interactive Dialog");
+
+ button.connect('clicked',
+ self._interactive_dialog_clicked)
+ hbox.pack_start(vbox2, False, False, 0)
+ vbox2.pack_start(button, False, False, 0)
+
+ table = Gtk.Table(n_rows=2, n_columns=2, homogeneous=False)
+ table.set_row_spacings(4)
+ table.set_col_spacings(4)
+ hbox.pack_start(table, False, False, 0);
+
+ label = Gtk.Label.new_with_mnemonic("_Entry 1")
+ table.attach_defaults (label, 0, 1, 0, 1)
+
+ self.entry1 = Gtk.Entry()
+ table.attach_defaults(self.entry1, 1, 2, 0, 1)
+ label.set_mnemonic_widget (self.entry1)
+
+ label = Gtk.Label.new_with_mnemonic("E_ntry 2");
+
+ table.attach_defaults(label, 0, 1, 1, 2);
+
+ self.entry2 = Gtk.Entry()
+ table.attach_defaults(self.entry2, 1, 2, 1, 2)
+ label.set_mnemonic_widget(self.entry2)
+
+ self.window.show_all()
+
+ def _interactive_dialog_clicked(self, button):
+ dialog = Gtk.Dialog('Interactive Dialog',
+ self.window,
+ Gtk.DialogFlags.MODAL |
+ Gtk.DialogFlags.DESTROY_WITH_PARENT,
+ (Gtk.STOCK_OK, Gtk.ResponseType.OK,
+ "_Non-stock Button", Gtk.ResponseType.CANCEL))
+
+ content_area = dialog.get_content_area ()
+ hbox = Gtk.HBox(spacing=8)
+ hbox.set_border_width(8)
+ content_area.pack_start(hbox, False, False, 0)
+
+ stock = Gtk.Image(stock=Gtk.STOCK_DIALOG_QUESTION,
+ icon_size=Gtk.IconSize.DIALOG)
+
+ hbox.pack_start(stock, False, False, 0)
+
+ table = Gtk.Table(2, 2, False)
+ table.set_row_spacings(4)
+ table.set_col_spacings(4)
+ hbox.pack_start(table, True, True, 0)
+ label = Gtk.Label.new_with_mnemonic("_Entry 1")
+ table.attach_defaults(label, 0, 1, 0, 1);
+ local_entry1 = Gtk.Entry();
+ local_entry1.set_text(self.entry1.get_text())
+ table.attach_defaults(local_entry1, 1, 2, 0, 1)
+ label.set_mnemonic_widget(local_entry1)
+
+ label = Gtk.Label.new_with_mnemonic("E_ntry 2")
+ table.attach_defaults (label, 0, 1, 1, 2)
+
+ local_entry2 = Gtk.Entry()
+ local_entry2.set_text (self.entry2.get_text())
+ table.attach_defaults(local_entry2, 1, 2, 1, 2)
+ label.set_mnemonic_widget(local_entry2);
+
+ hbox.show_all();
+
+ response = dialog.run()
+ if response == Gtk.ResponseType.OK:
+ self.entry1.set_text(local_entry1.get_text());
+ self.entry2.set_text(local_entry2.get_text());
+
+ dialog.destroy()
+
+ def _message_dialog_clicked(self, button):
+ dialog = Gtk.MessageDialog(self.window,
+ Gtk.DialogFlags.MODAL |
+ Gtk.DialogFlags.DESTROY_WITH_PARENT,
+ Gtk.MessageType.INFO,
+ Gtk.ButtonsType.OK,
+ "This message box has been popped up the following\nnumber of times:")
+ dialog.format_secondary_text ('%d' % self.dialog_counter)
+ dialog.run()
+
+ self.dialog_counter += 1
+ dialog.destroy()
+
+def main(demoapp=None):
+ app = DialogsApp()
+ Gtk.main()
+
+if __name__ == '__main__':
+ main()
diff --git a/demos/gtk-demo/demos/drawingarea.py b/demos/gtk-demo/demos/drawingarea.py
index b5dc1db4..1d7471dd 100644
--- a/demos/gtk-demo/demos/drawingarea.py
+++ b/demos/gtk-demo/demos/drawingarea.py
@@ -34,14 +34,12 @@ and drag in the scribble area to draw squiggles. Resize the window
to clear the area.
"""
-# See FIXME's
-is_fully_bound = False
-
from gi.repository import Gtk, Gdk
+import cairo
class DrawingAreaApp:
def __init__(self):
- self.pixmap = None
+ self.sureface = None
window = Gtk.Window()
window.set_title(title)
@@ -63,7 +61,7 @@ class DrawingAreaApp:
da = Gtk.DrawingArea()
da.set_size_request(100, 100)
frame.add(da)
- da.connect('expose-event', self.checkerboard_expose_event)
+ da.connect('draw', self.checkerboard_draw_event)
# create scribble area
label = Gtk.Label()
@@ -77,7 +75,7 @@ class DrawingAreaApp:
da = Gtk.DrawingArea()
da.set_size_request(100, 100)
frame.add(da)
- da.connect('expose-event', self.scribble_expose_event)
+ da.connect('draw', self.scribble_draw_event)
da.connect('configure-event', self.scribble_configure_event)
# event signals
@@ -94,49 +92,34 @@ class DrawingAreaApp:
window.show_all()
- def checkerboard_expose_event(self, da, event):
- check_size = 10
- spacing = 2
+ def checkerboard_draw_event(self, da, cairo_ctx):
- # At the start of an expose handler, a clip region of event->area
- # is set on the window, and event->area has been cleared to the
+ # At the start of a draw handler, a clip region has been set on
+ # the Cairo context, and the contents have been cleared to the
# widget's background color. The docs for
# gdk_window_begin_paint_region() give more details on how this
# works.
-
- # It would be a bit more efficient to keep these
- # GC's around instead of recreating on each expose, but
- # this is the lazy/slow way.
-
- gc1 = Gdk.GC.new(da.get_window())
- color = Gdk.Color(30000, 0, 30000)
- gc1.set_rgb_fg_color(color)
-
- gc2 = Gdk.GC.new(da.get_window())
- color = Gdk.Color(65535, 65535, 65535)
- gc2.set_rgb_fg_color(color)
+ check_size = 10
+ spacing = 2
xcount = 0
i = spacing
+ width = da.get_allocated_width()
+ height = da.get_allocated_height()
- while i < da.allocation.width:
+ while i < width:
j = spacing
ycount = xcount % 2 # start with even/odd depending on row
- while j < da.allocation.height:
+ while j < height:
if ycount % 2:
- gc = gc1
+ cairo_ctx.set_source_rgb(0.45777, 0, 0.45777)
else:
- gc = gc2
-
- # If we're outside event->area, this will do nothing.
- # It might be mildly more efficient if we handled
- # the clipping ourselves, but again we're feeling lazy.
- Gdk.draw_rectangle(da.get_window(),
- gc,
- True,
- i, j,
- check_size,
- check_size)
+ cairo_ctx.set_source_rgb(1, 1, 1)
+ # If we're outside the clip this will do nothing.
+ cairo_ctx.rectangle(i, j,
+ check_size,
+ check_size)
+ cairo_ctx.fill()
j += check_size + spacing
ycount += 1
@@ -146,72 +129,43 @@ class DrawingAreaApp:
return True
- def scribble_expose_event(self, da, event):
-
- # We use the "foreground GC" for the widget since it already exists,
- # but honestly any GC would work. The only thing to worry about
- # is whether the GC has an inappropriate clip region set.
+ def scribble_draw_event(self, da, cairo_ctx):
- # FIXME: we should be using widget.style.forground_gc but
- # there is a bug in GObject Introspection when getting
- # certain struct offsets so we create a new GC here
- black_gc = Gdk.GC.new(da.get_window())
- color = Gdk.Color(0, 0, 0)
- black_gc.set_rgb_fg_color(color)
-
- Gdk.draw_drawable(da.get_window(),
- black_gc,
- self.pixmap,
- event.expose.area.x, event.expose.area.y,
- event.expose.area.x, event.expose.area.y,
- event.expose.area.width, event.expose.area.height)
+ cairo_ctx.set_source_surface(self.surface, 0, 0)
+ cairo_ctx.paint()
return False
def draw_brush(self, widget, x, y):
- update_rect = Gdk.Rectangle(x - 3,
- y - 3,
- 6,
- 6)
-
- # FIXME: we should be using widget.style.black_gc but
- # there is a bug in GObject Introspection when getting
- # certain struct offsets so we create a new GC here
- black_gc = Gdk.GC.new(widget.get_window())
- color = Gdk.Color(0, 0, 0)
- black_gc.set_rgb_fg_color(color)
-
- Gdk.draw_rectangle(self.pixmap,
- black_gc,
- True,
- update_rect.x, update_rect.y,
- update_rect.width, update_rect.height)
+ update_rect = Gdk.Rectangle()
+ update_rect.x = x - 3
+ update_rect.y = y - 3
+ update_rect.width = 6
+ update_rect.height = 6
+
+ # paint to the surface where we store our state
+ cairo_ctx = cairo.Context(self.surface)
+
+ Gdk.cairo_rectangle(cairo_ctx, update_rect)
+ cairo_ctx.fill()
widget.get_window().invalidate_rect(update_rect, False)
def scribble_configure_event(self, da, event):
- self.pixmap = Gdk.Pixmap.new(da.get_window(),
- da.allocation.width,
- da.allocation.height,
- -1)
-
- # FIXME: we should be using widget.style.white_gc but
- # there is a bug in GObject Introspection when getting
- # certain struct offsets so we create a new GC here
- white_gc = Gdk.GC.new(da.get_window())
- color = Gdk.Color(65535, 65535, 65535)
- white_gc.set_rgb_fg_color(color)
- Gdk.draw_rectangle(self.pixmap,
- white_gc,
- True,
- 0, 0,
- da.allocation.width,
- da.allocation.height)
+
+ allocation = da.get_allocation()
+ self.surface = da.get_window().create_similar_surface(cairo.CONTENT_COLOR,
+ allocation.width,
+ allocation.height)
+
+ cairo_ctx = cairo.Context(self.surface)
+ cairo_ctx.set_source_rgb(1, 1, 1)
+ cairo_ctx.paint()
return True
def scribble_motion_notify_event(self, da, event):
- if self.pixmap == None: # paranoia check, in case we haven't gotten a configure event
+ if self.surface == None: # paranoia check, in case we haven't gotten a configure event
return False
# This call is very important; it requests the next motion event.
@@ -224,20 +178,19 @@ class DrawingAreaApp:
# we avoid getting a huge number of events faster than we
# can cope.
- (window, x, y, state) = event.motion.window.get_pointer()
+ (window, x, y, state) = event.window.get_pointer()
- # FIXME: for some reason Gdk.ModifierType.BUTTON1_MASK doesn't exist
- #if state & Gdk.ModifierType.BUTTON1_MASK:
- # self.draw_brush(da, x, y)
+ if state & Gdk.ModifierType.BUTTON1_MASK:
+ self.draw_brush(da, x, y)
return True
def scribble_button_press_event(self, da, event):
- if self.pixmap == None: # paranoia check, in case we haven't gotten a configure event
+ if self.surface == None: # paranoia check, in case we haven't gotten a configure event
return False
- if event.button.button == 1:
- self.draw_brush(da, event.button.x, event.button.y)
+ if event.button == 1:
+ self.draw_brush(da, event.x, event.y)
return True
diff --git a/demos/gtk-demo/demos/expander.py b/demos/gtk-demo/demos/expander.py
new file mode 100644
index 00000000..96387b21
--- /dev/null
+++ b/demos/gtk-demo/demos/expander.py
@@ -0,0 +1,60 @@
+#!/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 = "Expander"
+description = """
+GtkExpander allows to provide additional content that is initially hidden.
+This is also known as "disclosure triangle".
+"""
+
+from gi.repository import Gtk, GdkPixbuf
+
+class ExpanderApp:
+ def __init__(self):
+ self.window = Gtk.Dialog("GtkExpander",
+ None, 0,
+ (Gtk.STOCK_CLOSE, Gtk.ResponseType.NONE))
+ self.window.set_resizable(False)
+ self.window.connect('response', lambda window, x:window.destroy())
+ self.window.connect('destroy', Gtk.main_quit)
+
+ content_area = self.window.get_content_area()
+ vbox = Gtk.VBox(spacing=5)
+ content_area.pack_start(vbox, True, True, 0)
+ vbox.set_border_width(5)
+
+ label = Gtk.Label('Expander demo. Click on the triangle for details.')
+ vbox.pack_start(label, True, True, 0)
+
+ expander = Gtk.Expander(label='Details')
+ vbox.pack_start(expander, False, False, 0)
+
+ label = Gtk.Label('Details can be shown or hidden')
+ expander.add(label)
+
+ self.window.show_all()
+
+def main(demoapp=None):
+ app = ExpanderApp()
+ Gtk.main()
+
+if __name__ == '__main__':
+ main()
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()
diff --git a/demos/gtk-demo/demos/infobars.py b/demos/gtk-demo/demos/infobars.py
new file mode 100644
index 00000000..58190386
--- /dev/null
+++ b/demos/gtk-demo/demos/infobars.py
@@ -0,0 +1,99 @@
+#!/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 = "Info Bars"
+description = """
+Info bar widgets are used to report important messages to the user.
+"""
+
+from gi.repository import Gtk
+
+class InfobarApp:
+ def __init__(self):
+ self.window = Gtk.Window()
+ self.window.set_title('Info Bars')
+ self.window.set_border_width(8)
+ self.window.connect('destroy', Gtk.main_quit)
+
+ vbox = Gtk.VBox(spacing=0)
+ self.window.add(vbox)
+
+ bar = Gtk.InfoBar()
+ vbox.pack_start(bar, False, False, 0)
+ bar.set_message_type(Gtk.MessageType.INFO)
+ label = Gtk.Label('This is an info bar with message type Gtk.MessageType.INFO')
+ bar.get_content_area().pack_start(label, False, False, 0)
+
+ bar = Gtk.InfoBar()
+ vbox.pack_start(bar, False, False, 0)
+ bar.set_message_type(Gtk.MessageType.WARNING)
+ label = Gtk.Label('This is an info bar with message type Gtk.MessageType.WARNING')
+ bar.get_content_area().pack_start(label, False, False, 0)
+
+ bar = Gtk.InfoBar()
+ bar.add_button(Gtk.STOCK_OK, Gtk.ResponseType.OK)
+ bar.connect('response', self.on_bar_response)
+ vbox.pack_start(bar, False, False, 0)
+ bar.set_message_type(Gtk.MessageType.QUESTION)
+ label = Gtk.Label('This is an info bar with message type Gtk.MessageType.QUESTION')
+ bar.get_content_area().pack_start(label, False, False, 0)
+
+ bar = Gtk.InfoBar()
+ vbox.pack_start(bar, False, False, 0)
+ bar.set_message_type(Gtk.MessageType.ERROR)
+ label = Gtk.Label('This is an info bar with message type Gtk.MessageType.ERROR')
+ bar.get_content_area().pack_start(label, False, False, 0)
+
+ bar = Gtk.InfoBar()
+ vbox.pack_start(bar, False, False, 0)
+ bar.set_message_type(Gtk.MessageType.OTHER)
+ label = Gtk.Label('This is an info bar with message type Gtk.MessageType.OTHER')
+ bar.get_content_area().pack_start(label, False, False, 0)
+
+ frame = Gtk.Frame(label="Info bars")
+ vbox.pack_start(frame, False, False, 8)
+
+ vbox2 = Gtk.VBox(spacing=8)
+ vbox2.set_border_width(8)
+ frame.add(vbox2)
+
+ # Standard message dialog
+ label = Gtk.Label('An example of different info bars')
+ vbox2.pack_start(label, False, False, 0)
+
+ self.window.show_all()
+
+ def on_bar_response(self, info_bar, response_id):
+ dialog = Gtk.MessageDialog(self.window,
+ Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT,
+ Gtk.MessageType.INFO,
+ Gtk.ButtonsType.OK,
+ 'You clicked on an info bar')
+ dialog.format_secondary_text('Your response has id %d' % response_id)
+ dialog.run()
+ dialog.destroy()
+
+def main(demoapp=None):
+ app = InfobarApp()
+ Gtk.main()
+
+if __name__ == '__main__':
+ main()
diff --git a/demos/gtk-demo/demos/links.py b/demos/gtk-demo/demos/links.py
new file mode 100644
index 00000000..898f3e6a
--- /dev/null
+++ b/demos/gtk-demo/demos/links.py
@@ -0,0 +1,74 @@
+#!/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 = "Links"
+description = """
+GtkLabel can show hyperlinks. The default action is to call gtk_show_uri() on
+their URI, but it is possible to override this with a custom handler.
+"""
+
+from gi.repository import Gtk
+
+class LinksApp:
+ def __init__(self):
+ self.window = Gtk.Window()
+ self.window.set_title('Links')
+ self.window.set_border_width(12)
+ self.window.connect('destroy', Gtk.main_quit)
+
+ label = Gtk.Label("""Some <a href="http://en.wikipedia.org/wiki/Text"
+title="plain text">text</a> may be marked up
+as hyperlinks, which can be clicked
+or activated via <a href="keynav">keynav</a>""")
+
+ label.set_use_markup(True)
+ label.connect("activate-link", self.activate_link)
+ self.window.add(label);
+ label.show()
+
+ self.window.show()
+
+ def activate_link(self, label, uri):
+ if uri == 'keynav':
+ parent = label.get_toplevel()
+ markup = """The term <i>keynav</i> is a shorthand for
+keyboard navigation and refers to the process of using
+a program (exclusively) via keyboard input."""
+ dialog = Gtk.MessageDialog(parent,
+ Gtk.DialogFlags.DESTROY_WITH_PARENT,
+ Gtk.MessageType.INFO,
+ Gtk.ButtonsType.OK,
+ text=markup,
+ use_markup=True)
+ dialog.present()
+ dialog.connect('response', self.response_cb)
+
+ return True
+
+ def response_cb(self, dialog, response_id):
+ dialog.destroy()
+
+def main(demoapp=None):
+ app = LinksApp()
+ Gtk.main()
+
+if __name__ == '__main__':
+ main()
diff --git a/demos/gtk-demo/demos/menus.py b/demos/gtk-demo/demos/menus.py
new file mode 100644
index 00000000..2e3a41b4
--- /dev/null
+++ b/demos/gtk-demo/demos/menus.py
@@ -0,0 +1,122 @@
+#!/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 = "Menus"
+description = """There are several widgets involved in displaying menus. The GtkMenuBar widget is a menu bar, which normally appears horizontally at the top of an application, but can also be layed out vertically. The GtkMenu widget is the actual menu that pops up. Both GtkMenuBar and GtkMenu are subclasses of GtkMenuShell; a GtkMenuShell contains menu items (GtkMenuItem). Each menu item contains text and/or images and can be selected by the user.
+There are several kinds of menu item, including plain GtkMenuItem, GtkCheckMenuItem which can be checked/unchecked, GtkRadioMenuItem which is a check menu item that's in a mutually exclusive group, GtkSeparatorMenuItem which is a separator bar, GtkTearoffMenuItem which allows a GtkMenu to be torn off, and GtkImageMenuItem which can place a GtkImage or other widget next to the menu text.
+A GtkMenuItem can have a submenu, which is simply a GtkMenu to pop up when the menu item is selected. Typically, all menu items in a menu bar have submenus.
+GtkUIManager provides a higher-level interface for creating menu bars and menus; while you can construct menus manually, most people don't do that. There's a separate demo for GtkUIManager.
+"""
+
+from gi.repository import Gtk
+
+class MenusApp:
+ def __init__(self):
+ self.window = Gtk.Window()
+ self.window.set_title('Menus')
+ self.window.connect('destroy', Gtk.main_quit)
+
+ accel_group = Gtk.AccelGroup()
+ self.window.add_accel_group(accel_group)
+ self.window.set_border_width(0)
+
+ box = Gtk.HBox()
+ self.window.add(box)
+
+ box1 = Gtk.VBox()
+ box.add(box1)
+
+ menubar = Gtk.MenuBar()
+ box1.pack_start(menubar, False, True, 0)
+
+ menu = self.create_menu(2, True)
+
+ menuitem = Gtk.MenuItem(label='test\nline2')
+ menuitem.set_submenu(self.create_menu(3, True))
+ menubar.append(menuitem)
+
+ menuitem = Gtk.MenuItem(label='foo')
+ menuitem.set_submenu(self.create_menu(4, True))
+ menuitem.set_right_justified(True)
+ menubar.append(menuitem)
+
+ box2 = Gtk.VBox(spacing=10)
+ box2.set_border_width(10)
+ box1.pack_start(box2, False, True, 0)
+
+ button = Gtk.Button('Flip')
+ button.connect('clicked', self.change_orientation, menubar)
+ box2.pack_start(button, True, True, 0)
+
+ button = Gtk.Button('Close')
+ button.connect('clicked', lambda x: self.window.destroy())
+ box2.pack_start(button, True, True, 0)
+ button.set_can_default(True)
+
+ self.window.show_all()
+
+ def create_menu(self, depth, tearoff):
+ if depth < 1:
+ return None
+
+ menu = Gtk.Menu()
+ group = None
+
+ if tearoff:
+ menuitem = Gtk.TearoffMenuItem()
+ menu.append(menuitem)
+
+ i = 0
+ j = 1
+ while i < 5:
+ label = 'item %2d - %d' % (depth, j)
+ # we should be adding this to a group but the group API
+ # isn't bindable - we need something more like the
+ # Gtk.RadioAction API
+ menuitem = Gtk.RadioMenuItem(label=label)
+ menu.append(menuitem)
+
+ if i == 3:
+ menuitem.set_sensitive(False)
+
+ menuitem.set_submenu(self.create_menu(depth - 1, True))
+
+ i += 1
+ j += 1
+
+ return menu
+
+ def change_orientation(self, button, menubar):
+ parent = menubar.get_parent()
+ orientation = parent.get_orientation()
+ parent.set_orientation(1 - orientation)
+
+ if orientation == Gtk.Orientation.VERTICAL:
+ menubar.props.pack_direction = Gtk.PackDirection.TTB
+ else:
+ menubar.props.pack_direction = Gtk.PackDirection.LTR
+
+def main(demoapp=None):
+ app = MenusApp()
+ Gtk.main()
+
+if __name__ == '__main__':
+ main()
diff --git a/demos/gtk-demo/demos/pickers.py b/demos/gtk-demo/demos/pickers.py
new file mode 100644
index 00000000..8ecedb6b
--- /dev/null
+++ b/demos/gtk-demo/demos/pickers.py
@@ -0,0 +1,74 @@
+#!/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 = "Pickers"
+description = """
+These widgets are mainly intended for use in preference dialogs. They allow to select colors, fonts, files and directories.
+"""
+
+from gi.repository import Gtk
+
+class PickersApp:
+ def __init__(self):
+ self.window = Gtk.Window(title='Pickers')
+ self.window.connect('destroy', Gtk.main_quit)
+ self.window.set_border_width(10)
+
+ table = Gtk.Table(4, 2, False)
+ table.set_col_spacing(0, 10)
+ table.set_row_spacings(3)
+ self.window.add(table)
+ table.set_border_width(10)
+
+ label = Gtk.Label('Color:')
+ label.set_alignment(0.0, 0.5)
+ picker = Gtk.ColorButton()
+ table.attach_defaults(label, 0, 1, 0, 1)
+ table.attach_defaults (picker, 1, 2, 0, 1)
+
+ label = Gtk.Label('Font:')
+ label.set_alignment(0.0, 0.5)
+ picker = Gtk.FontButton()
+ table.attach_defaults(label, 0, 1, 1, 2)
+ table.attach_defaults (picker, 1, 2, 1, 2)
+
+ label = Gtk.Label('File:')
+ label.set_alignment(0.0, 0.5)
+ picker = Gtk.FileChooserButton.new('Pick a File',
+ Gtk.FileChooserAction.OPEN)
+ table.attach_defaults(label, 0, 1, 2, 3)
+ table.attach_defaults (picker, 1, 2, 2, 3)
+
+ label = Gtk.Label('Folder:')
+ label.set_alignment(0.0, 0.5)
+ picker = Gtk.FileChooserButton.new('Pick a Folder',
+ Gtk.FileChooserAction.SELECT_FOLDER)
+ table.attach_defaults(label, 0, 1, 3, 4)
+ table.attach_defaults (picker, 1, 2, 3, 4)
+
+ self.window.show_all()
+
+def main(demoapp=None):
+ app = PickersApp()
+ Gtk.main()
+
+if __name__ == '__main__':
+ main()
diff --git a/demos/gtk-demo/demos/pixbuf.py b/demos/gtk-demo/demos/pixbuf.py
new file mode 100644
index 00000000..4a9aa581
--- /dev/null
+++ b/demos/gtk-demo/demos/pixbuf.py
@@ -0,0 +1,183 @@
+#!/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 = "Pixbufs"
+description = """A GdkPixbuf represents an image, normally in RGB or RGBA format. Pixbufs are normally used to load files from disk and perform image scaling.
+ It also shows off how to use GtkDrawingArea to do a simple animation.
+Look at the Image demo for additional pixbuf usage examples.
+"""
+
+from gi.repository import Gtk, Gdk, GdkPixbuf, GLib, GObject
+import os
+import math
+from os import path
+
+class PixbufApp:
+ FRAME_DELAY = 50
+ BACKGROUND_NAME = "background.jpg"
+ CYCLE_LEN = 60
+
+ def __init__(self):
+ self.background_width = 0
+ self.background_height = 0
+ self.background_pixbuf = None
+ self.frame = None
+ self.frame_num = 0
+ self.pixbufs = []
+ self.image_names = [
+ "apple-red.png",
+ "gnome-applets.png",
+ "gnome-calendar.png",
+ "gnome-foot.png",
+ "gnome-gmush.png",
+ "gnome-gimp.png",
+ "gnome-gsame.png",
+ "gnu-keys.png"
+ ]
+
+ self.window = Gtk.Window(title="Pixbufs")
+ self.window.set_resizable(False)
+ self.window.connect('destroy', self.cleanup_cb)
+
+ try:
+ self.load_pixbufs()
+ except GLib.Error as e:
+ dialog = Gtk.MessageDialog(None,
+ 0,
+ Gtk.MessageType.ERROR,
+ Gtk.ButtonsType.CLOSE,
+ e.message)
+
+ dialog.run()
+ dialog.destroy()
+ Gtk.main_quit()
+
+ self.window.set_size_request(self.background_width,
+ self.background_height)
+ self.frame = GdkPixbuf.Pixbuf.new(GdkPixbuf.Colorspace.RGB,
+ False,
+ 8,
+ self.background_width,
+ self.background_height)
+ self.da = Gtk.DrawingArea()
+ self.da.connect('draw', self.draw_cb)
+ self.window.add(self.da)
+ self.timeout_id = GObject.timeout_add(self.FRAME_DELAY, self.timeout_cb)
+ self.window.show_all()
+
+ def load_pixbufs(self):
+ base_path = 'data'
+ if not path.isdir(base_path):
+ base_path = path.join('demos', base_path)
+
+ img_path = path.join(base_path, self.BACKGROUND_NAME)
+ self.background_pixbuf = GdkPixbuf.Pixbuf.new_from_file(img_path)
+ self.background_height = self.background_pixbuf.get_height()
+ self.background_width = self.background_pixbuf.get_width()
+
+ for img_name in self.image_names:
+ img_path = path.join(base_path, img_name)
+ self.pixbufs.append(GdkPixbuf.Pixbuf.new_from_file(img_path))
+
+ def draw_cb(self, da, cairo_ctx):
+ Gdk.cairo_set_source_pixbuf(cairo_ctx, self.frame, 0, 0)
+ cairo_ctx.paint()
+
+ return True
+
+ def timeout_cb(self):
+ self.background_pixbuf.copy_area(0, 0,
+ self.background_width,
+ self.background_height,
+ self.frame,
+ 0, 0)
+
+ f = float(self.frame_num % self.CYCLE_LEN) / self.CYCLE_LEN
+
+ xmid = self.background_width / 2.0
+ ymid = self.background_height / 2.0
+ radius = min(xmid, ymid) / 2.0
+
+ r1 = Gdk.Rectangle()
+ r2 = Gdk.Rectangle()
+
+ i = 0
+ for pb in self.pixbufs:
+ i += 1
+ ang = 2.0 * math.pi * i / len(self.pixbufs) - f * 2.0 * math.pi
+
+ iw = pb.get_width()
+ ih = pb.get_height()
+
+ r = radius + (radius / 3.0) * math.sin(f * 2.0 * math.pi);
+
+ xpos = math.floor (xmid + r * math.cos(ang) - iw / 2.0 + 0.5)
+ ypos = math.floor (ymid + r * math.sin(ang) - ih / 2.0 + 0.5)
+
+ if i & 1:
+ k = math.sin(f * 2.0 * math.pi)
+ else:
+ k = math.cos(f * 2.0 * math.pi)
+
+ k = 2.0 * k * k
+ k = max(0.25, k)
+
+ r1.x = xpos
+ r1.y = ypos
+ r1.width = iw * k
+ r1.height = ih * k
+
+ r2.x = 0
+ r2.y = 0
+ r2.width = self.background_width
+ r2.height = self.background_height
+
+ success, dest = Gdk.rectangle_intersect(r1, r2)
+ if success:
+ alpha = 0
+ if i & 1:
+ alpha = max(127, math.fabs(255 * math.sin(f * 2.0 * math.pi)))
+ else:
+ alpha = max(127, math.fabs(255 * math.cos(f * 2.0 * math.pi)))
+
+ pb.composite(self.frame,
+ dest.x, dest.y,
+ dest.width, dest.height,
+ xpos, ypos,
+ k, k,
+ GdkPixbuf.InterpType.NEAREST,
+ alpha)
+
+ self.da.queue_draw()
+
+ self.frame_num += 1
+ return True
+
+ def cleanup_cb(self, widget):
+ GObject.source_remove(self.timeout_id)
+ Gtk.main_quit()
+
+def main(demoapp=None):
+ app = PixbufApp()
+ Gtk.main()
+
+if __name__ == '__main__':
+ main()
diff --git a/demos/gtk-demo/demos/printing.py b/demos/gtk-demo/demos/printing.py
new file mode 100644
index 00000000..d0bc6d21
--- /dev/null
+++ b/demos/gtk-demo/demos/printing.py
@@ -0,0 +1,173 @@
+#!/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 = "Printing"
+description = """
+GtkPrintOperation offers a simple API to support printing in a cross-platform way.
+"""
+
+from gi.repository import Gtk, GLib, Pango, PangoCairo
+import cairo
+import math
+import os
+from os import path
+
+class PrintingApp:
+ HEADER_HEIGHT = 10*72/25.4
+ HEADER_GAP = 3*72/25.4
+
+ def __init__(self):
+ self.operation = Gtk.PrintOperation()
+ print_data = {'filename': 'printing.py',
+ 'font_size': 12.0,
+ 'lines_per_page': 0,
+ 'lines': None,
+ 'num_lines': 0,
+ 'num_pages': 0
+ }
+
+ self.operation.connect('begin-print', self.begin_print, print_data)
+ self.operation.connect('draw-page', self.draw_page, print_data)
+ self.operation.connect('end-print', self.end_print, print_data)
+
+ self.operation.set_use_full_page(False)
+ self.operation.set_unit(Gtk.Unit.POINTS)
+ self.operation.set_embed_page_setup(True)
+
+ settings = Gtk.PrintSettings()
+
+ dir = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_DOCUMENTS)
+ if dir is None:
+ dir = GLib.get_home_dir()
+ if settings.get(Gtk.PRINT_SETTINGS_OUTPUT_FILE_FORMAT) == 'ps':
+ ext = '.ps'
+ elif settings.get(Gtk.PRINT_SETTINGS_OUTPUT_FILE_FORMAT) == 'svg':
+ ext = '.svg'
+ else:
+ ext = '.pdf'
+
+ uri = "file://%s/gtk-demo%s" % (dir, ext);
+ settings.set(Gtk.PRINT_SETTINGS_OUTPUT_URI, uri)
+ self.operation.set_print_settings(settings)
+
+ try:
+ self.operation.run(Gtk.PrintOperationAction.PRINT_DIALOG, None)
+ except GLib.Error as e:
+ dialog = Gtk.MessageDialog(None,
+ 0,
+ Gtk.MessageType.ERROR,
+ Gtk.ButtonsType.CLOSE,
+ e.message)
+
+ dialog.run()
+ dialog.destroy()
+
+ def begin_print(self, operation, print_ctx, print_data):
+ height = print_ctx.get_height() - self.HEADER_HEIGHT - self.HEADER_GAP
+ print_data['lines_per_page'] = \
+ math.floor(height / print_data['font_size'])
+
+ file_path = print_data['filename']
+ if not path.isfile(file_path):
+ file_path = path.join('demos', file_path)
+ if not path.isfile:
+ raise FileNotFoundError()
+
+ # in reality you should most likely not read the entire
+ # file into a buffer
+ source_file = open(file_path, 'r')
+ s = source_file.read()
+ print_data['lines'] = s.split('\n')
+
+ print_data['num_lines'] = len(print_data['lines'])
+ print_data['num_pages'] = \
+ (print_data['num_lines'] - 1) / print_data['lines_per_page'] + 1
+
+ operation.set_n_pages(print_data['num_pages'])
+
+ def draw_page(self, operation, print_ctx, page_num, print_data):
+ cr = print_ctx.get_cairo_context()
+ width = print_ctx.get_width()
+
+ cr.rectangle(0, 0, width, self.HEADER_HEIGHT)
+ cr.set_source_rgb(0.8, 0.8, 0.8)
+ cr.fill_preserve()
+
+ cr.set_source_rgb(0, 0, 0)
+ cr.set_line_width(1)
+ cr.stroke()
+
+ layout = print_ctx.create_pango_layout()
+ desc = Pango.FontDescription('sans 14')
+ layout.set_font_description(desc)
+
+ layout.set_text(print_data['filename'], -1)
+ (text_width, text_height) = layout.get_pixel_size()
+
+ if text_width > width:
+ layout.set_width(width)
+ layout.set_ellipsize(Pango.EllipsizeType.START);
+ (text_width, text_height) = layout.get_pixel_size(layout)
+
+ cr.move_to ((width - text_width) / 2,
+ (self.HEADER_HEIGHT - text_height) / 2)
+ PangoCairo.show_layout(cr, layout)
+
+ page_str = "%d/%d" % (page_num + 1, print_data['num_pages'])
+ layout.set_text(page_str, -1)
+
+ layout.set_width(-1)
+ (text_width, text_height) = layout.get_pixel_size()
+ cr.move_to(width - text_width - 4,
+ (self.HEADER_HEIGHT - text_height) / 2)
+ PangoCairo.show_layout(cr, layout);
+
+ layout = print_ctx.create_pango_layout()
+
+ desc = Pango.FontDescription('monospace')
+ desc.set_size(print_data['font_size'] * Pango.SCALE)
+ layout.set_font_description(desc)
+
+ cr.move_to (0, self.HEADER_HEIGHT + self.HEADER_GAP)
+ lines_pp = int(print_data['lines_per_page'])
+ num_lines = print_data['num_lines']
+ data_lines = print_data['lines']
+ font_size = print_data['font_size']
+ line = page_num * lines_pp
+
+ for i in range(lines_pp):
+ if line >= num_lines:
+ break;
+
+ layout.set_text(data_lines[line], -1)
+ PangoCairo.show_layout(cr, layout)
+ cr.rel_move_to (0, font_size)
+ line += 1
+
+ def end_print(self, operation, print_ctx, print_data):
+ pass
+
+def main(demoapp=None):
+ app = PrintingApp()
+ Gtk.main()
+
+if __name__ == '__main__':
+ main()
diff --git a/demos/gtk-demo/demos/rotatedtext.py b/demos/gtk-demo/demos/rotatedtext.py
new file mode 100644
index 00000000..af33319a
--- /dev/null
+++ b/demos/gtk-demo/demos/rotatedtext.py
@@ -0,0 +1,196 @@
+#!/usr/bin/env python
+# coding=utf-8
+# -*- 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 = "Rotated Text"
+description = """This demo shows how to use PangoCairo to draw rotated and transformed text. The right pane shows a rotated GtkLabel widget.
+In both cases, a custom PangoCairo shape renderer is installed to draw a red heard using cairo drawing operations instead of the Unicode heart character.
+"""
+
+from gi.repository import Gtk, Pango, PangoCairo, Gdk
+import cairo
+import sys
+import math
+
+# Python 2 and 3 handle UTF8 differently
+if sys.version_info < (3, 0):
+ BYTES_TEXT = "I \xe2\x99\xa5 GTK+"
+ UTF8_TEXT = unicode(BYTES_TEXT, 'UTF-8')
+ BYTES_HEART = "\xe2\x99\xa5"
+ HEART = unicode(BYTES_HEART, 'UTF-8')
+else:
+ UTF8_TEXT = "I ♥ GTK+"
+ BYTES_TEXT = bytes(UTF8_TEXT, 'utf-8')
+ HEART = "♥"
+ BYTES_HEART = bytes(HEART, 'utf-8')
+
+class RotatedTextApp:
+ RADIUS = 150
+ N_WORDS = 5
+ FONT = "Serif 18"
+
+ def __init__(self):
+
+ white = Gdk.RGBA()
+ white.red = 1.0
+ white.green = 1.0
+ white.blue = 1.0
+ white.alpha = 1.0
+
+ self.window = Gtk.Window(title="Rotated Text")
+ self.window.set_default_size(4 * self.RADIUS, 2 * self.RADIUS)
+ self.window.connect('destroy', Gtk.main_quit)
+
+ box = Gtk.HBox()
+ box.set_homogeneous(True)
+ self.window.add(box)
+
+ # add a drawing area
+ da = Gtk.DrawingArea()
+ box.add(da)
+
+ # override the background color from the theme
+ da.override_background_color(0, white)
+
+ da.connect('draw', self.rotated_text_draw)
+
+ label = Gtk.Label(UTF8_TEXT)
+ box.add(label)
+ label.set_angle(45)
+
+ # Setup some fancy stuff on the label
+ layout = label.get_layout()
+
+ PangoCairo.context_set_shape_renderer(layout.get_context(),
+ self.fancy_shape_renderer,
+ None)
+ attrs = self.create_fancy_attr_list_for_layout(layout)
+ label.set_attributes(attrs)
+
+ self.window.show_all()
+
+ def fancy_shape_renderer(self, cairo_ctx, attr, do_path):
+ x, y = cairo_ctx.get_current_point()
+ cairo_ctx.translate(x, y)
+
+ cairo_ctx.scale(float(attr.inc_rect.width) / Pango.SCALE,
+ float(attr.inc_rect.height) / Pango.SCALE)
+
+ if int(attr.data) == 0x2665: # U+2665 BLACK HEART SUIT
+ cairo_ctx.move_to(0.5, 0.0)
+ cairo_ctx.line_to(0.9, -0.4)
+ cairo_ctx.curve_to(1.1, -0.8, 0.5, -0.9, 0.5, -0.5)
+ cairo_ctx.curve_to(0.5, -0.9, -0.1, -0.8, 0.1, -0.4)
+ cairo_ctx.close_path()
+
+ if not do_path:
+ cairo_ctx.set_source_rgb(1.0, 0.0, 0.0)
+ cairo_ctx.fill()
+
+ def create_fancy_attr_list_for_layout(self, layout):
+ pango_ctx = layout.get_context()
+ metrics = pango_ctx.get_metrics(layout.get_font_description(),
+ None)
+ ascent = metrics.get_ascent()
+
+ logical_rect = Pango.Rectangle()
+ logical_rect.x = 0
+ logical_rect.width = ascent
+ logical_rect.y = -ascent
+ logical_rect.height = ascent
+
+ ink_rect = logical_rect
+
+ # Set fancy shape attributes for all hearts
+ attrs = Pango.AttrList()
+ p = BYTES_TEXT.find(BYTES_HEART)
+
+ # FIXME: attr_shape_new_with_data isn't introspectable
+ '''
+ while (p != -1):
+ attr = Pango.attr_shape_new_with_data(ink_rect,
+ logical_rect,
+ ord(HEART),
+ None)
+ attr.start_index = p
+ attr.end_index = p + len(BYTES_HEART)
+ p = UTF8_TEXT.find(HEART, attr.end_index)
+
+ attrs.insert(attr)
+ '''
+ return attrs
+
+ def rotated_text_draw (self, da, cairo_ctx):
+ # Create a cairo context and set up a transformation matrix so that the user
+ # space coordinates for the centered square where we draw are [-RADIUS, RADIUS],
+ # [-RADIUS, RADIUS].
+ # We first center, then change the scale.
+ width = da.get_allocated_width()
+ height = da.get_allocated_height()
+ device_radius = min(width, height) / 2.0
+ cairo_ctx.translate(
+ device_radius + (width - 2 * device_radius) / 2,
+ device_radius + (height - 2 * device_radius) / 2)
+ cairo_ctx.scale(device_radius / self.RADIUS,
+ device_radius / self.RADIUS)
+
+ # Create a subtle gradient source and use it.
+ pattern = cairo.LinearGradient(-self.RADIUS, -self.RADIUS,
+ self.RADIUS, self.RADIUS)
+ pattern.add_color_stop_rgb(0.0, 0.5, 0.0, 0.0)
+ pattern.add_color_stop_rgb(1.0, 0.0, 0.0, 0.5)
+ cairo_ctx.set_source(pattern)
+
+ # Create a PangoContext and set up our shape renderer
+ context = da.create_pango_context()
+ PangoCairo.context_set_shape_renderer(context,
+ self.fancy_shape_renderer,
+ None)
+
+ # Create a PangoLayout, set the text, font, and attributes */
+ layout = Pango.Layout(context=context)
+ layout.set_text(UTF8_TEXT, -1)
+ desc = Pango.FontDescription(self.FONT)
+ layout.set_font_description(desc)
+
+ attrs = self.create_fancy_attr_list_for_layout(layout)
+ layout.set_attributes(attrs)
+
+ # Draw the layout N_WORDS times in a circle */
+ for i in range(self.N_WORDS):
+ # Inform Pango to re-layout the text with the new transformation matrix
+ PangoCairo.update_layout(cairo_ctx, layout)
+
+ width, height = layout.get_pixel_size()
+ cairo_ctx.move_to(-width / 2, -self.RADIUS * 0.9)
+ PangoCairo.show_layout(cairo_ctx, layout)
+
+ # Rotate for the next turn
+ cairo_ctx.rotate(math.pi*2 / self.N_WORDS)
+
+ return False
+
+def main(demoapp=None):
+ app = RotatedTextApp()
+ Gtk.main()
+
+if __name__ == '__main__':
+ main()
diff --git a/demos/gtk-demo/gtk-demo.py b/demos/gtk-demo/gtk-demo.py
index 41edc321..f0f27bbe 100755
--- a/demos/gtk-demo/gtk-demo.py
+++ b/demos/gtk-demo/gtk-demo.py
@@ -124,7 +124,7 @@ class GtkDemoApp(object):
try:
demo = Demo(module.title, module, f)
- except AttributeError, e:
+ except AttributeError as e:
raise AttributeError('(%s): %s' % (f, e.message))
demo_list.append(demo)
@@ -146,7 +146,7 @@ class GtkDemoApp(object):
if filename.endswith('.py'):
demo_file_list.append(fullname)
- demo_file_list.sort(lambda a, b: cmp(a.lower(), b.lower()))
+ demo_file_list = sorted(demo_file_list, key=str.lower)
self.load_demos_from_list(demo_file_list, demo_list)
@@ -175,10 +175,11 @@ class GtkDemoApp(object):
Gtk.Window.set_default_icon_list(list)
def selection_cb(self, selection, model):
- (success, m, treeiter) = selection.get_selected()
- if not success:
+ sel = selection.get_selected()
+ if sel == ():
return
+ treeiter = sel[1]
demo = model.get_value(treeiter, 1)
title = demo.title
@@ -212,9 +213,10 @@ class GtkDemoApp(object):
self.source_buffer.insert(end, code)
def row_activated_cb(self, view, path, col, store):
- (success, treeiter) = store.get_iter(path)
+ treeiter = store.get_iter(path)
demo = store.get_value(treeiter, 1)
- demo.module.main(self)
+ if not demo.isdir:
+ demo.module.main(self)
def create_tree(self):
tree_store = Gtk.TreeStore(str, Demo, Pango.Style)
@@ -244,10 +246,10 @@ class GtkDemoApp(object):
text = 0,
style = 2)
+ first_iter = tree_store.get_iter_first()
+ if first_iter is not None:
+ selection.select_iter(first_iter)
-
- (success, first_iter) = tree_store.get_iter_first()
- selection.select_iter(first_iter)
selection.connect('changed', self.selection_cb, tree_store)
tree_view.connect('row_activated', self.row_activated_cb, tree_store)
@@ -288,7 +290,7 @@ class GtkDemoApp(object):
scrolled_window.add(text_view)
if is_source:
- font_desc = Pango.Font.description_from_string('monospace')
+ font_desc = Pango.FontDescription('monospace')
text_view.modify_font(font_desc)
text_view.set_wrap_mode(Gtk.WrapMode.NONE)
else: