summaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
authorChristoph Reiter <creiter@src.gnome.org>2017-12-06 17:53:51 +0100
committerChristoph Reiter <creiter@src.gnome.org>2017-12-06 18:04:50 +0100
commite4a52b9f99667df98806d0c4ee78c2909ead27e1 (patch)
tree42355f50c243a35cbfdf18107648037300c048bc /examples
parentf8ff3b5e0e769e6db1509426af28728129780529 (diff)
downloadpygobject-e4a52b9f99667df98806d0c4ee78c2909ead27e1.tar.gz
demo: Move demo into examples and dist it
We might want to install it in the future, but until then at least put it into the tarball. https://bugzilla.gnome.org/show_bug.cgi?id=735918
Diffstat (limited to 'examples')
-rw-r--r--examples/Makefile.am7
-rwxr-xr-xexamples/demo/demo.py356
-rw-r--r--examples/demo/demos/Css/__init__.py0
-rw-r--r--examples/demo/demos/Css/css_accordion.py79
-rw-r--r--examples/demo/demos/Css/css_basics.py148
-rw-r--r--examples/demo/demos/Css/css_multiplebgs.py187
-rw-r--r--examples/demo/demos/Entry/__init__.py0
-rw-r--r--examples/demo/demos/Entry/entry_buffer.py71
-rw-r--r--examples/demo/demos/Entry/entry_completion.py85
-rw-r--r--examples/demo/demos/Entry/search_entry.py253
-rw-r--r--examples/demo/demos/IconView/__init__.py0
-rw-r--r--examples/demo/demos/IconView/iconviewbasics.py220
-rw-r--r--examples/demo/demos/IconView/iconviewedit.py98
-rw-r--r--examples/demo/demos/TreeView/__init__.py0
-rw-r--r--examples/demo/demos/TreeView/liststore.py210
-rw-r--r--examples/demo/demos/TreeView/treemodel_filelist.py234
-rw-r--r--examples/demo/demos/TreeView/treemodel_filetree.py279
-rw-r--r--examples/demo/demos/TreeView/treemodel_large.py143
-rw-r--r--examples/demo/demos/__init__.py0
-rw-r--r--examples/demo/demos/appwindow.py407
-rw-r--r--examples/demo/demos/assistant.py134
-rw-r--r--examples/demo/demos/builder.py64
-rw-r--r--examples/demo/demos/button_box.py121
-rw-r--r--examples/demo/demos/clipboard.py227
-rw-r--r--examples/demo/demos/colorselector.py111
-rw-r--r--examples/demo/demos/combobox.py318
-rw-r--r--examples/demo/demos/data/alphatest.pngbin0 -> 26529 bytes
-rw-r--r--examples/demo/demos/data/apple-red.pngbin0 -> 3545 bytes
-rw-r--r--examples/demo/demos/data/background.jpgbin0 -> 22219 bytes
-rw-r--r--examples/demo/demos/data/brick.pngbin0 -> 5043 bytes
-rw-r--r--examples/demo/demos/data/brick2.pngbin0 -> 10713 bytes
-rw-r--r--examples/demo/demos/data/css_accordion.css52
-rw-r--r--examples/demo/demos/data/css_basics.css22
-rw-r--r--examples/demo/demos/data/css_multiplebgs.css136
-rw-r--r--examples/demo/demos/data/cssview.css41
-rw-r--r--examples/demo/demos/data/demo.gresourcebin0 -> 31110 bytes
-rw-r--r--examples/demo/demos/data/demo.gresource.xml18
-rw-r--r--examples/demo/demos/data/demo.ui258
-rw-r--r--examples/demo/demos/data/floppybuddy.gifbin0 -> 5216 bytes
-rw-r--r--examples/demo/demos/data/gnome-applets.pngbin0 -> 3090 bytes
-rw-r--r--examples/demo/demos/data/gnome-calendar.pngbin0 -> 2755 bytes
-rw-r--r--examples/demo/demos/data/gnome-foot.pngbin0 -> 2916 bytes
-rw-r--r--examples/demo/demos/data/gnome-fs-directory.pngbin0 -> 2044 bytes
-rw-r--r--examples/demo/demos/data/gnome-fs-regular.pngbin0 -> 1795 bytes
-rw-r--r--examples/demo/demos/data/gnome-gimp.pngbin0 -> 3410 bytes
-rw-r--r--examples/demo/demos/data/gnome-gmush.pngbin0 -> 3244 bytes
-rw-r--r--examples/demo/demos/data/gnome-gsame.pngbin0 -> 4263 bytes
-rw-r--r--examples/demo/demos/data/gnu-keys.pngbin0 -> 3852 bytes
-rw-r--r--examples/demo/demos/data/gtk-logo-rgb.gifbin0 -> 6427 bytes
-rw-r--r--examples/demo/demos/data/reset.css68
-rw-r--r--examples/demo/demos/dialogs.py155
-rw-r--r--examples/demo/demos/drawingarea.py206
-rw-r--r--examples/demo/demos/expander.py61
-rwxr-xr-xexamples/demo/demos/flowbox.py750
-rw-r--r--examples/demo/demos/images.py304
-rw-r--r--examples/demo/demos/infobars.py102
-rw-r--r--examples/demo/demos/links.py76
-rw-r--r--examples/demo/demos/menus.py133
-rw-r--r--examples/demo/demos/pickers.py76
-rw-r--r--examples/demo/demos/pixbuf.py183
-rw-r--r--examples/demo/demos/printing.py179
-rw-r--r--examples/demo/demos/rotatedtext.py198
-rw-r--r--examples/demo/demos/test.py16
63 files changed, 6785 insertions, 1 deletions
diff --git a/examples/Makefile.am b/examples/Makefile.am
index af9f3d74..c023cc01 100644
--- a/examples/Makefile.am
+++ b/examples/Makefile.am
@@ -1 +1,6 @@
-EXTRA_DIST = properties.py signal.py option.py cairo-demo.py
+EXTRA_DIST = \
+ properties.py \
+ signal.py \
+ option.py \
+ cairo-demo.py \
+ demo
diff --git a/examples/demo/demo.py b/examples/demo/demo.py
new file mode 100755
index 00000000..d67935d9
--- /dev/null
+++ b/examples/demo/demo.py
@@ -0,0 +1,356 @@
+#!/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
+
+
+import codecs
+import os
+import sys
+import textwrap
+
+from gi.repository import GLib, GObject, Pango, GdkPixbuf, Gtk, Gio
+
+try:
+ from gi.repository import GtkSource
+ GtkSource # PyFlakes
+except ImportError:
+ GtkSource = None
+
+
+DEMOROOTDIR = os.path.abspath(os.path.dirname(__file__))
+DEMOCODEDIR = os.path.join(DEMOROOTDIR, 'demos')
+sys.path.insert(0, DEMOROOTDIR)
+
+
+class Demo(GObject.GObject):
+ __gtype_name__ = 'GtkDemo'
+
+ def __init__(self, title, module, filename):
+ super(Demo, self).__init__()
+
+ self.title = title
+ self.module = module
+ self.filename = filename
+
+ @classmethod
+ def new_from_file(cls, path):
+ relpath = os.path.relpath(path, DEMOROOTDIR)
+ packagename = os.path.dirname(relpath).replace(os.sep, '.')
+ modulename = os.path.basename(relpath)[0:-3]
+
+ package = __import__(packagename, globals(), locals(), [modulename], 0)
+ module = getattr(package, modulename)
+
+ try:
+ return cls(module.title, module, path)
+ except AttributeError as e:
+ raise AttributeError('(%s): %s' % (path, e.message))
+
+
+class DemoTreeStore(Gtk.TreeStore):
+ __gtype_name__ = 'GtkDemoTreeStore'
+
+ def __init__(self, *args):
+ super(DemoTreeStore, self).__init__(str, Demo, Pango.Style)
+
+ self._parent_nodes = {}
+
+ for filename in self._list_dir(DEMOCODEDIR):
+ fullpath = os.path.join(DEMOCODEDIR, filename)
+ initfile = os.path.join(os.path.dirname(fullpath), '__init__.py')
+
+ if fullpath != initfile and os.path.isfile(initfile) and fullpath.endswith('.py'):
+ parentname = os.path.dirname(os.path.relpath(fullpath, DEMOCODEDIR))
+
+ if parentname:
+ parent = self._get_parent_node(parentname)
+ else:
+ parent = None
+
+ demo = Demo.new_from_file(fullpath)
+ self.append(parent, (demo.title, demo, Pango.Style.NORMAL))
+
+ def _list_dir(self, path):
+ demo_file_list = []
+
+ for filename in os.listdir(path):
+ fullpath = os.path.join(path, filename)
+
+ if os.path.isdir(fullpath):
+ demo_file_list.extend(self._list_dir(fullpath))
+ elif os.path.isfile(fullpath):
+ demo_file_list.append(fullpath)
+
+ return sorted(demo_file_list, key=str.lower)
+
+ def _get_parent_node(self, name):
+ if name not in self._parent_nodes.keys():
+ node = self.append(None, (name, None, Pango.Style.NORMAL))
+ self._parent_nodes[name] = node
+
+ return self._parent_nodes[name]
+
+
+class GtkDemoApp(Gtk.Application):
+ __gtype_name__ = 'GtkDemoWindow'
+
+ def __init__(self):
+ super(GtkDemoApp, self).__init__(application_id='org.gnome.pygobject.gtkdemo')
+
+ # Use a GResource to hold the CSS files. Resource bundles are created by
+ # the glib-compile-resources program shipped with Glib which takes an xml
+ # file that describes the bundle, and a set of files that the xml
+ # references. These are combined into a binary resource bundle.
+ base_path = os.path.abspath(os.path.dirname(__file__))
+ resource_path = os.path.join(base_path, 'demos/data/demo.gresource')
+ resource = Gio.Resource.load(resource_path)
+
+ # FIXME: method register() should be without the underscore
+ # FIXME: see https://bugzilla.gnome.org/show_bug.cgi?id=684319
+ # Once the resource has been globally registered it can be used
+ # throughout the application.
+ resource._register()
+
+ def on_activate(self, app):
+ self.window = Gtk.ApplicationWindow.new(self)
+ self.window.set_title('PyGObject GTK+ Code Demos')
+ self.window.set_default_size(600, 400)
+ self.setup_default_icon()
+
+ self.header_bar = Gtk.HeaderBar(show_close_button=True,
+ subtitle='Foobar')
+ self.window.set_titlebar(self.header_bar)
+
+ stack = Gtk.Stack(transition_type=Gtk.StackTransitionType.SLIDE_LEFT_RIGHT,
+ homogeneous=True)
+ switcher = Gtk.StackSwitcher(stack=stack, halign=Gtk.Align.CENTER)
+
+ self.header_bar.set_custom_title(switcher)
+
+ hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL,
+ homogeneous=False,
+ spacing=0)
+ self.window.add(hbox)
+
+ tree = self.create_tree()
+ hbox.pack_start(child=tree, expand=False, fill=False, padding=0)
+ hbox.pack_start(child=stack, expand=True, fill=True, padding=0)
+
+ text_widget, info_buffer = self.create_text_view()
+ stack.add_titled(text_widget, name='info', title='Info')
+
+ self.info_buffer = info_buffer
+ self.info_buffer.create_tag('title', font='Sans 18')
+
+ text_widget, self.source_buffer = self.create_source_view()
+ stack.add_titled(text_widget, name='source', title='Source')
+
+ self.window.show_all()
+
+ self.selection_cb(self.tree_view.get_selection(),
+ self.tree_view.get_model())
+
+ def find_file(self, base=''):
+ dir = os.path.join(DEMOCODEDIR, 'data')
+ logo_file = os.path.join(dir, 'gtk-logo-rgb.gif')
+ base_file = os.path.join(dir, base)
+
+ if (GLib.file_test(logo_file, GLib.FileTest.EXISTS) and
+ GLib.file_test(base_file, GLib.FileTest.EXISTS)):
+ return base_file
+ else:
+ filename = os.path.join(DEMOCODEDIR, base)
+
+ if GLib.file_test(filename, GLib.FileTest.EXISTS):
+ return filename
+
+ # can't find the file
+ raise IOError('Cannot find demo data file "%s"' % base)
+
+ def setup_default_icon(self):
+ filename = self.find_file('gtk-logo-rgb.gif')
+ pixbuf = GdkPixbuf.Pixbuf.new_from_file(filename)
+ transparent = pixbuf.add_alpha(True, 0xff, 0xff, 0xff)
+ list = []
+ list.append(transparent)
+ Gtk.Window.set_default_icon_list(list)
+
+ def selection_cb(self, selection, model):
+ sel = selection.get_selected()
+ if sel == ():
+ return
+
+ treeiter = sel[1]
+ title = model.get_value(treeiter, 0)
+ demo = model.get_value(treeiter, 1)
+
+ if demo is None:
+ return
+
+ # Split into paragraphs based on double newlines and use
+ # textwrap to strip out all other formatting whitespace
+ description = ''
+ for paragraph in demo.module.description.split('\n\n'):
+ description += '\n'.join(textwrap.wrap(paragraph, 99999))
+ description += '\n\n' # Add paragraphs back in
+
+ f = codecs.open(demo.filename, 'rU', 'utf-8')
+ code = f.read()
+ f.close()
+
+ # output and style the title
+ (start, end) = self.info_buffer.get_bounds()
+ self.info_buffer.delete(start, end)
+ (start, end) = self.source_buffer.get_bounds()
+ self.source_buffer.delete(start, end)
+
+ start = self.info_buffer.get_iter_at_offset(0)
+ end = start.copy()
+ self.info_buffer.insert(end, title)
+ start = end.copy()
+ start.backward_chars(len(title))
+ self.info_buffer.apply_tag_by_name('title', start, end)
+ self.info_buffer.insert(end, '\n')
+
+ # output the description
+ self.info_buffer.insert(end, description)
+
+ # output the code
+ start = self.source_buffer.get_iter_at_offset(0)
+ end = start.copy()
+ self.source_buffer.insert(end, code)
+
+ def row_activated_cb(self, view, path, col, store):
+ iter = store.get_iter(path)
+ demo = store.get_value(iter, 1)
+
+ if demo is not None:
+ store.set_value(iter, 2, Pango.Style.ITALIC)
+ try:
+ demo.module.main(self)
+ finally:
+ store.set_value(iter, 2, Pango.Style.NORMAL)
+
+ def create_tree(self):
+ tree_store = DemoTreeStore()
+ tree_view = Gtk.TreeView()
+ self.tree_view = tree_view
+ tree_view.set_model(tree_store)
+ selection = tree_view.get_selection()
+ selection.set_mode(Gtk.SelectionMode.BROWSE)
+ tree_view.set_size_request(200, -1)
+
+ cell = Gtk.CellRendererText()
+ column = Gtk.TreeViewColumn(title='Widget (double click for demo)',
+ cell_renderer=cell,
+ text=0,
+ style=2)
+
+ first_iter = tree_store.get_iter_first()
+ if first_iter is not None:
+ selection.select_iter(first_iter)
+
+ selection.connect('changed', self.selection_cb, tree_store)
+ tree_view.connect('row_activated', self.row_activated_cb, tree_store)
+
+ tree_view.append_column(column)
+
+ tree_view.expand_all()
+ tree_view.set_headers_visible(False)
+ scrolled_window = Gtk.ScrolledWindow(hadjustment=None,
+ vadjustment=None)
+ scrolled_window.set_policy(Gtk.PolicyType.NEVER,
+ Gtk.PolicyType.AUTOMATIC)
+
+ scrolled_window.add(tree_view)
+
+ label = Gtk.Label(label='Widget (double click for demo)')
+
+ box = Gtk.Notebook()
+ box.append_page(scrolled_window, label)
+
+ tree_view.grab_focus()
+
+ return box
+
+ def create_scrolled_window(self):
+ scrolled_window = Gtk.ScrolledWindow(hadjustment=None,
+ vadjustment=None)
+ scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC,
+ Gtk.PolicyType.AUTOMATIC)
+ scrolled_window.set_shadow_type(Gtk.ShadowType.IN)
+ return scrolled_window
+
+ def create_text_view(self):
+ text_view = Gtk.TextView()
+ buffer = Gtk.TextBuffer()
+
+ text_view.set_buffer(buffer)
+ text_view.set_editable(False)
+ text_view.set_cursor_visible(False)
+
+ scrolled_window = self.create_scrolled_window()
+ scrolled_window.add(text_view)
+
+ text_view.set_wrap_mode(Gtk.WrapMode.WORD)
+ text_view.set_pixels_above_lines(2)
+ text_view.set_pixels_below_lines(2)
+
+ return scrolled_window, buffer
+
+ def create_source_view(self):
+ font_desc = Pango.FontDescription('monospace 11')
+
+ if GtkSource:
+ lang_mgr = GtkSource.LanguageManager()
+ lang = lang_mgr.get_language('python')
+
+ buffer = GtkSource.Buffer()
+ buffer.set_language(lang)
+ buffer.set_highlight_syntax(True)
+
+ view = GtkSource.View()
+ view.set_buffer(buffer)
+ view.set_show_line_numbers(True)
+
+ scrolled_window = self.create_scrolled_window()
+ scrolled_window.add(view)
+
+ else:
+ scrolled_window, buffer = self.create_text_view()
+ view = scrolled_window.get_child()
+
+ view.modify_font(font_desc)
+ view.set_wrap_mode(Gtk.WrapMode.NONE)
+ return scrolled_window, buffer
+
+ def run(self, argv):
+ self.connect('activate', self.on_activate)
+ return super(GtkDemoApp, self).run(argv)
+
+
+def main(argv):
+ """Entry point for demo manager"""
+ app = GtkDemoApp()
+ return app.run(argv)
+
+
+if __name__ == '__main__':
+ SystemExit(main(sys.argv))
diff --git a/examples/demo/demos/Css/__init__.py b/examples/demo/demos/Css/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/examples/demo/demos/Css/__init__.py
diff --git a/examples/demo/demos/Css/css_accordion.py b/examples/demo/demos/Css/css_accordion.py
new file mode 100644
index 00000000..7c6f539c
--- /dev/null
+++ b/examples/demo/demos/Css/css_accordion.py
@@ -0,0 +1,79 @@
+#!/usr/bin/env python
+# -*- Mode: Python; py-indent-offset: 4 -*-
+# vim: tabstop=4 shiftwidth=4 expandtab
+#
+# Copyright (C) 2013 Gian Mario Tagliaretti <gianmt@gnome.org>
+#
+# 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 = "CSS Accordion"
+description = """
+A simple accordion demo written using CSS transitions and multiple backgrounds.
+"""
+
+
+from gi.repository import Gtk, Gio
+
+
+class CSSAccordionApp:
+ def __init__(self):
+ window = Gtk.Window()
+ window.set_title('CSS Accordion')
+ window.set_default_size(600, 300)
+ window.set_border_width(10)
+ window.connect('destroy', Gtk.main_quit)
+
+ hbox = Gtk.Box(homogeneous=False, spacing=2,
+ orientation=Gtk.Orientation.HORIZONTAL)
+ hbox.set_halign(Gtk.Align.CENTER)
+ hbox.set_valign(Gtk.Align.CENTER)
+ window.add(hbox)
+
+ for label in ('This', 'Is', 'A', 'CSS', 'Accordion', ':-)'):
+ hbox.add(Gtk.Button(label=label))
+
+ bytes = Gio.resources_lookup_data("/css_accordion/css_accordion.css", 0)
+
+ provider = Gtk.CssProvider()
+ provider.load_from_data(bytes.get_data())
+
+ self.apply_css(window, provider)
+
+ window.show_all()
+
+ def apply_css(self, widget, provider):
+ Gtk.StyleContext.add_provider(widget.get_style_context(),
+ provider,
+ Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
+
+ if isinstance(widget, Gtk.Container):
+ widget.forall(self.apply_css, provider)
+
+
+def main(demoapp=None):
+ CSSAccordionApp()
+ Gtk.main()
+
+if __name__ == '__main__':
+ import os
+ base_path = os.path.abspath(os.path.dirname(__file__))
+ resource_path = os.path.join(base_path, '../data/demo.gresource')
+ resource = Gio.Resource.load(resource_path)
+
+ # FIXME: method register() should be without the underscore
+ # FIXME: see https://bugzilla.gnome.org/show_bug.cgi?id=684319
+ resource._register()
+ main()
diff --git a/examples/demo/demos/Css/css_basics.py b/examples/demo/demos/Css/css_basics.py
new file mode 100644
index 00000000..18c3d12a
--- /dev/null
+++ b/examples/demo/demos/Css/css_basics.py
@@ -0,0 +1,148 @@
+#!/usr/bin/env python
+# -*- Mode: Python; py-indent-offset: 4 -*-
+# vim: tabstop=4 shiftwidth=4 expandtab
+#
+# Copyright (C) 2013 Gian Mario Tagliaretti <gianmt@gnome.org>
+#
+# 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 = "CSS Basics"
+description = """
+Gtk themes are written using CSS. Every widget is build of multiple items
+that you can style very similarly to a regular website.
+"""
+
+import os
+from gi.repository import Gtk, Gdk, Pango, Gio, GLib
+
+
+class CSSBasicsApp:
+ def __init__(self, demoapp):
+ self.demoapp = demoapp
+ #: Store the last successful parsing of the css so we can revert
+ #: this in case of an error.
+ self.last_good_text = ''
+ #: Set when we receive a parsing-error callback. This is needed
+ #: to handle logic after a parsing-error callback which does not raise
+ #: an exception with provider.load_from_data()
+ self.last_error_code = 0
+
+ self.window = Gtk.Window()
+ self.window.set_title('CSS Basics')
+ self.window.set_default_size(400, 300)
+ self.window.set_border_width(10)
+ self.window.connect('destroy', lambda w: Gtk.main_quit())
+
+ self.infobar = Gtk.InfoBar()
+ self.infolabel = Gtk.Label()
+ self.infobar.get_content_area().pack_start(self.infolabel, False, False, 0)
+ self.infobar.set_message_type(Gtk.MessageType.WARNING)
+
+ scrolled = Gtk.ScrolledWindow()
+ box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
+ box.pack_start(scrolled, expand=True, fill=True, padding=0)
+ box.pack_start(self.infobar, expand=False, fill=True, padding=0)
+ self.window.add(box)
+
+ provider = Gtk.CssProvider()
+
+ buffer = Gtk.TextBuffer()
+ buffer.create_tag(tag_name="warning", underline=Pango.Underline.SINGLE)
+ buffer.create_tag(tag_name="error", underline=Pango.Underline.ERROR)
+ buffer.connect("changed", self.css_text_changed, provider)
+
+ provider.connect("parsing-error", self.show_parsing_error, buffer)
+
+ textview = Gtk.TextView()
+ textview.set_buffer(buffer)
+ scrolled.add(textview)
+
+ bytes = Gio.resources_lookup_data("/css_basics/css_basics.css", 0)
+ buffer.set_text(bytes.get_data().decode('utf-8'))
+
+ self.apply_css(self.window, provider)
+ self.window.show_all()
+ self.infobar.hide()
+
+ def apply_css(self, widget, provider):
+ Gtk.StyleContext.add_provider(widget.get_style_context(),
+ provider,
+ Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
+
+ if isinstance(widget, Gtk.Container):
+ widget.forall(self.apply_css, provider)
+
+ def show_parsing_error(self, provider, section, error, buffer):
+ start = buffer.get_iter_at_line_index(section.get_start_line(),
+ section.get_start_position())
+
+ end = buffer.get_iter_at_line_index(section.get_end_line(),
+ section.get_end_position())
+
+ if error.code == Gtk.CssProviderError.DEPRECATED:
+ tag_name = "warning"
+ else:
+ tag_name = "error"
+ self.last_error_code = error.code
+
+ self.infolabel.set_text(error.message)
+ self.infobar.show_all()
+
+ buffer.apply_tag_by_name(tag_name, start, end)
+
+ def css_text_changed(self, buffer, provider):
+ start = buffer.get_start_iter()
+ end = buffer.get_end_iter()
+ buffer.remove_all_tags(start, end)
+
+ text = buffer.get_text(start, end, False).encode('utf-8')
+
+ # Ignore CSS errors as they are shown by highlighting
+ try:
+ provider.load_from_data(text)
+ except GLib.GError as e:
+ if e.domain != 'gtk-css-provider-error-quark':
+ raise e
+
+ # If the parsing-error callback is ever run (even in the case of warnings)
+ # load the last good css text that ran without any warnings. Otherwise
+ # we may have a discrepancy in "last_good_text" vs the current buffer
+ # causing section.get_start_position() to give back an invalid position
+ # for the editor buffer.
+ if self.last_error_code:
+ provider.load_from_data(self.last_good_text)
+ self.last_error_code = 0
+ else:
+ self.last_good_text = text
+ self.infobar.hide()
+
+ Gtk.StyleContext.reset_widgets(Gdk.Screen.get_default())
+
+
+def main(demoapp=None):
+ CSSBasicsApp(demoapp)
+ Gtk.main()
+
+
+if __name__ == '__main__':
+ base_path = os.path.abspath(os.path.dirname(__file__))
+ resource_path = os.path.join(base_path, '../data/demo.gresource')
+ resource = Gio.Resource.load(resource_path)
+
+ # FIXME: method register() should be without the underscore
+ # FIXME: see https://bugzilla.gnome.org/show_bug.cgi?id=684319
+ resource._register()
+ main()
diff --git a/examples/demo/demos/Css/css_multiplebgs.py b/examples/demo/demos/Css/css_multiplebgs.py
new file mode 100644
index 00000000..9e1b0115
--- /dev/null
+++ b/examples/demo/demos/Css/css_multiplebgs.py
@@ -0,0 +1,187 @@
+#!/usr/bin/env python
+# -*- Mode: Python; py-indent-offset: 4 -*-
+# vim: tabstop=4 shiftwidth=4 expandtab
+#
+# Copyright (C) 2013 Gian Mario Tagliaretti <gianmt@gnome.org>
+#
+# 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 = "CSS Theming/Multiple Backgrounds"
+description = """
+Gtk themes are written using CSS. Every widget is build of multiple items
+that you can style very similarly to a regular website.
+"""
+
+from gi.repository import Gtk, Gdk, Pango, Gio, GLib
+
+
+class CSSMultiplebgsApp:
+ def __init__(self, demoapp):
+ self.demoapp = demoapp
+ #: Store the last successful parsing of the css so we can revert
+ #: this in case of an error.
+ self.last_good_text = ''
+ #: Set when we receive a parsing-error callback. This is needed
+ #: to handle logic after a parsing-error callback which does not raise
+ #: an exception with provider.load_from_data()
+ self.last_error_code = 0
+
+ self.window = Gtk.Window()
+ self.window.set_title('CSS Multiplebgs')
+ self.window.set_default_size(400, 300)
+ self.window.set_border_width(10)
+ self.window.connect('destroy', lambda w: Gtk.main_quit())
+
+ overlay = Gtk.Overlay()
+ overlay.add_events(Gdk.EventMask.ENTER_NOTIFY_MASK |
+ Gdk.EventMask.LEAVE_NOTIFY_MASK |
+ Gdk.EventMask.POINTER_MOTION_MASK)
+
+ self.infobar = Gtk.InfoBar()
+ self.infolabel = Gtk.Label()
+ self.infobar.get_content_area().pack_start(self.infolabel, False, False, 0)
+ self.infobar.set_message_type(Gtk.MessageType.WARNING)
+
+ box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
+ box.pack_start(overlay, expand=True, fill=True, padding=0)
+ box.pack_start(self.infobar, expand=False, fill=True, padding=0)
+ self.window.add(box)
+
+ canvas = Gtk.DrawingArea()
+ canvas.set_name("canvas")
+ canvas.connect("draw", self.drawing_area_draw)
+ overlay.add(canvas)
+
+ button = Gtk.Button()
+ button.add_events(Gdk.EventMask.ENTER_NOTIFY_MASK |
+ Gdk.EventMask.LEAVE_NOTIFY_MASK |
+ Gdk.EventMask.POINTER_MOTION_MASK)
+ button.set_name("bricks-button")
+ button.set_halign(Gtk.Align.CENTER)
+ button.set_valign(Gtk.Align.CENTER)
+ button.set_size_request(250, 84)
+ overlay.add_overlay(button)
+
+ paned = Gtk.Paned(orientation=Gtk.Orientation.VERTICAL)
+ overlay.add_overlay(paned)
+
+ # We need a filler so we get a handle
+ box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
+ paned.add(box)
+
+ buffer = Gtk.TextBuffer()
+ buffer.create_tag(tag_name="warning", underline=Pango.Underline.SINGLE)
+ buffer.create_tag(tag_name="error", underline=Pango.Underline.ERROR)
+
+ provider = Gtk.CssProvider()
+
+ buffer.connect("changed", self.css_text_changed, provider)
+ provider.connect("parsing-error", self.show_parsing_error, buffer)
+
+ textview = Gtk.TextView()
+ textview.set_buffer(buffer)
+
+ scrolled = Gtk.ScrolledWindow()
+ scrolled.add(textview)
+ paned.add(scrolled)
+
+ bytes = Gio.resources_lookup_data("/css_multiplebgs/css_multiplebgs.css", 0)
+ buffer.set_text(bytes.get_data().decode('utf-8'))
+
+ self.apply_css(self.window, provider)
+ self.window.show_all()
+ self.infobar.hide()
+
+ def drawing_area_draw(self, widget, cairo_t):
+ context = widget.get_style_context()
+ Gtk.render_background(context, cairo_t, 0, 0,
+ widget.get_allocated_width(),
+ widget.get_allocated_height())
+
+ Gtk.render_frame(context, cairo_t, 0, 0,
+ widget.get_allocated_width(),
+ widget.get_allocated_height())
+
+ def apply_css(self, widget, provider):
+ Gtk.StyleContext.add_provider(widget.get_style_context(),
+ provider,
+ Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
+
+ if isinstance(widget, Gtk.Container):
+ widget.forall(self.apply_css, provider)
+
+ def show_parsing_error(self, provider, section, error, buffer):
+ start = buffer.get_iter_at_line_index(section.get_start_line(),
+ section.get_start_position())
+
+ end = buffer.get_iter_at_line_index(section.get_end_line(),
+ section.get_end_position())
+
+ if error.code == Gtk.CssProviderError.DEPRECATED:
+ tag_name = "warning"
+ else:
+ tag_name = "error"
+ self.last_error_code = error.code
+
+ self.infolabel.set_text(error.message)
+ self.infobar.show_all()
+
+ buffer.apply_tag_by_name(tag_name, start, end)
+
+ def css_text_changed(self, buffer, provider):
+ start = buffer.get_start_iter()
+ end = buffer.get_end_iter()
+ buffer.remove_all_tags(start, end)
+
+ text = buffer.get_text(start, end, False).encode('utf-8')
+
+ # Ignore CSS errors as they are shown by highlighting
+ try:
+ provider.load_from_data(text)
+ except GLib.GError as e:
+ if e.domain != 'gtk-css-provider-error-quark':
+ raise e
+
+ # If the parsing-error callback is ever run (even in the case of warnings)
+ # load the last good css text that ran without any warnings. Otherwise
+ # we may have a discrepancy in "last_good_text" vs the current buffer
+ # causing section.get_start_position() to give back an invalid position
+ # for the editor buffer.
+ if self.last_error_code:
+ provider.load_from_data(self.last_good_text)
+ self.last_error_code = 0
+ else:
+ self.last_good_text = text
+ self.infobar.hide()
+
+ Gtk.StyleContext.reset_widgets(Gdk.Screen.get_default())
+
+
+def main(demoapp=None):
+ CSSMultiplebgsApp(demoapp)
+ Gtk.main()
+
+
+if __name__ == '__main__':
+ import os
+ base_path = os.path.abspath(os.path.dirname(__file__))
+ resource_path = os.path.join(base_path, '../data/demo.gresource')
+ resource = Gio.Resource.load(resource_path)
+
+ # FIXME: method register() should be without the underscore
+ # FIXME: see https://bugzilla.gnome.org/show_bug.cgi?id=684319
+ resource._register()
+ main()
diff --git a/examples/demo/demos/Entry/__init__.py b/examples/demo/demos/Entry/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/examples/demo/demos/Entry/__init__.py
diff --git a/examples/demo/demos/Entry/entry_buffer.py b/examples/demo/demos/Entry/entry_buffer.py
new file mode 100644
index 00000000..fbfc3017
--- /dev/null
+++ b/examples/demo/demos/Entry/entry_buffer.py
@@ -0,0 +1,71 @@
+#!/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 = "Entry Buffer"
+description = """
+Gtk.EntryBuffer provides the text content in a Gtk.Entry.
+"""
+
+
+from gi.repository import Gtk
+
+
+class EntryBufferApp:
+ def __init__(self):
+ self.window = Gtk.Dialog(title='Gtk.EntryBuffer')
+ self.window.add_buttons(Gtk.STOCK_CLOSE, Gtk.ResponseType.NONE)
+ self.window.connect('response', self.destroy)
+ self.window.connect('destroy', lambda x: Gtk.main_quit())
+ self.window.set_resizable(False)
+
+ vbox = Gtk.VBox(homogeneous=False, spacing=0)
+ self.window.get_content_area().pack_start(vbox, True, True, 0)
+ vbox.set_border_width(5)
+
+ label = Gtk.Label()
+ label.set_markup('Entries share a buffer. Typing in one is reflected in the other.')
+ vbox.pack_start(label, False, False, 0)
+
+ # create a buffer
+ buffer = Gtk.EntryBuffer()
+
+ # create our first entry
+ entry = Gtk.Entry(buffer=buffer)
+ vbox.pack_start(entry, False, False, 0)
+
+ # create the second entry
+ entry = Gtk.Entry(buffer=buffer)
+ entry.set_visibility(False)
+ vbox.pack_start(entry, False, False, 0)
+
+ self.window.show_all()
+
+ def destroy(self, *args):
+ self.window.destroy()
+ Gtk.main_quit()
+
+
+def main(demoapp=None):
+ EntryBufferApp()
+ Gtk.main()
+
+if __name__ == '__main__':
+ main()
diff --git a/examples/demo/demos/Entry/entry_completion.py b/examples/demo/demos/Entry/entry_completion.py
new file mode 100644
index 00000000..2a221858
--- /dev/null
+++ b/examples/demo/demos/Entry/entry_completion.py
@@ -0,0 +1,85 @@
+#!/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 = "Entry Completion"
+description = """
+Gtk.EntryCompletion provides a mechanism for adding support for
+completion in Gtk.Entry.
+"""
+
+
+from gi.repository import Gtk
+
+
+class EntryBufferApp:
+ def __init__(self):
+ self.window = Gtk.Dialog(title='Gtk.EntryCompletion')
+ self.window.add_buttons(Gtk.STOCK_CLOSE, Gtk.ResponseType.NONE)
+ self.window.connect('response', self.destroy)
+ self.window.connect('destroy', lambda x: Gtk.main_quit())
+ self.window.set_resizable(False)
+
+ vbox = Gtk.VBox(homogeneous=False, spacing=0)
+ self.window.get_content_area().pack_start(vbox, True, True, 0)
+ vbox.set_border_width(5)
+
+ label = Gtk.Label()
+ label.set_markup('Completion demo, try writing <b>total</b> or <b>gnome</b> for example.')
+ vbox.pack_start(label, False, False, 0)
+
+ # create our entry
+ entry = Gtk.Entry()
+ vbox.pack_start(entry, False, False, 0)
+
+ # create the completion object
+ completion = Gtk.EntryCompletion()
+
+ # assign the completion to the entry
+ entry.set_completion(completion)
+
+ # create tree model and use it as the completion model
+ completion_model = self.create_completion_model()
+ completion.set_model(completion_model)
+
+ completion.set_text_column(0)
+
+ self.window.show_all()
+
+ def create_completion_model(self):
+ store = Gtk.ListStore(str)
+
+ store.append(['GNOME'])
+ store.append(['total'])
+ store.append(['totally'])
+
+ return store
+
+ def destroy(self, *args):
+ self.window.destroy()
+ Gtk.main_quit()
+
+
+def main(demoapp=None):
+ EntryBufferApp()
+ Gtk.main()
+
+if __name__ == '__main__':
+ main()
diff --git a/examples/demo/demos/Entry/search_entry.py b/examples/demo/demos/Entry/search_entry.py
new file mode 100644
index 00000000..bdf4f57d
--- /dev/null
+++ b/examples/demo/demos/Entry/search_entry.py
@@ -0,0 +1,253 @@
+#!/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, GObject
+
+(PIXBUF_COL,
+ TEXT_COL) = range(2)
+
+
+class SearchboxApp:
+ def __init__(self, demoapp):
+ self.demoapp = demoapp
+
+ self.window = Gtk.Dialog(title='Search Entry')
+ self.window.add_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(label='Find')
+ find_button.connect('clicked', self.start_search, entry)
+ notebook.append_page(find_button, None)
+ find_button.show()
+
+ cancel_button = Gtk.Button(label='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 self.finish_search_id != 0:
+ GObject.source_remove(self.finish_search_id)
+ if self.search_progress_id != 0:
+ GObject.source_remove(self.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):
+ SearchboxApp(demoapp)
+ Gtk.main()
+
+if __name__ == '__main__':
+ main()
diff --git a/examples/demo/demos/IconView/__init__.py b/examples/demo/demos/IconView/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/examples/demo/demos/IconView/__init__.py
diff --git a/examples/demo/demos/IconView/iconviewbasics.py b/examples/demo/demos/IconView/iconviewbasics.py
new file mode 100644
index 00000000..ea1ceb41
--- /dev/null
+++ b/examples/demo/demos/IconView/iconviewbasics.py
@@ -0,0 +1,220 @@
+#!/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, Gio, GdkPixbuf, Gtk
+
+
+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):
+ IconViewApp(demoapp)
+ Gtk.main()
+
+if __name__ == '__main__':
+ main()
diff --git a/examples/demo/demos/IconView/iconviewedit.py b/examples/demo/demos/IconView/iconviewedit.py
new file mode 100644
index 00000000..44f3ffea
--- /dev/null
+++ b/examples/demo/demos/IconView/iconviewedit.py
@@ -0,0 +1,98 @@
+#!/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)
+ 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):
+ IconviewEditApp()
+ Gtk.main()
+
+if __name__ == '__main__':
+ main()
diff --git a/examples/demo/demos/TreeView/__init__.py b/examples/demo/demos/TreeView/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/examples/demo/demos/TreeView/__init__.py
diff --git a/examples/demo/demos/TreeView/liststore.py b/examples/demo/demos/TreeView/liststore.py
new file mode 100644
index 00000000..bad99d3a
--- /dev/null
+++ b/examples/demo/demos/TreeView/liststore.py
@@ -0,0 +1,210 @@
+#!/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, GLib
+
+
+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(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 = GLib.timeout_add(80, self.spinner_timeout)
+
+ def window_closed(self, window, event):
+ if self.timeout != 0:
+ GLib.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):
+ ListStoreApp()
+ Gtk.main()
+
+if __name__ == '__main__':
+ main()
diff --git a/examples/demo/demos/TreeView/treemodel_filelist.py b/examples/demo/demos/TreeView/treemodel_filelist.py
new file mode 100644
index 00000000..f3c7565c
--- /dev/null
+++ b/examples/demo/demos/TreeView/treemodel_filelist.py
@@ -0,0 +1,234 @@
+#!/usr/bin/env python
+
+title = "File List (GenericTreeModel)"
+description = """
+This is a file list demo which makes use of the GenericTreeModel python
+implementation of the Gtk.TreeModel interface. This demo shows what methods
+need to be overridden to provide a valid TreeModel to a TreeView.
+"""
+
+import os
+import stat
+import time
+
+import pygtkcompat
+pygtkcompat.enable()
+pygtkcompat.enable_gtk('3.0')
+
+import gtk
+
+
+folderxpm = [
+ "17 16 7 1",
+ " c #000000",
+ ". c #808000",
+ "X c yellow",
+ "o c #808080",
+ "O c #c0c0c0",
+ "+ c white",
+ "@ c None",
+ "@@@@@@@@@@@@@@@@@",
+ "@@@@@@@@@@@@@@@@@",
+ "@@+XXXX.@@@@@@@@@",
+ "@+OOOOOO.@@@@@@@@",
+ "@+OXOXOXOXOXOXO. ",
+ "@+XOXOXOXOXOXOX. ",
+ "@+OXOXOXOXOXOXO. ",
+ "@+XOXOXOXOXOXOX. ",
+ "@+OXOXOXOXOXOXO. ",
+ "@+XOXOXOXOXOXOX. ",
+ "@+OXOXOXOXOXOXO. ",
+ "@+XOXOXOXOXOXOX. ",
+ "@+OOOOOOOOOOOOO. ",
+ "@ ",
+ "@@@@@@@@@@@@@@@@@",
+ "@@@@@@@@@@@@@@@@@"
+ ]
+folderpb = gtk.gdk.pixbuf_new_from_xpm_data(folderxpm)
+
+filexpm = [
+ "12 12 3 1",
+ " c #000000",
+ ". c #ffff04",
+ "X c #b2c0dc",
+ "X XXX",
+ "X ...... XXX",
+ "X ...... X",
+ "X . ... X",
+ "X ........ X",
+ "X . .... X",
+ "X ........ X",
+ "X . .. X",
+ "X ........ X",
+ "X . .. X",
+ "X ........ X",
+ "X X"
+ ]
+filepb = gtk.gdk.pixbuf_new_from_xpm_data(filexpm)
+
+
+class FileListModel(gtk.GenericTreeModel):
+ __gtype_name__ = 'DemoFileListModel'
+
+ column_types = (gtk.gdk.Pixbuf, str, int, str, str)
+ column_names = ['Name', 'Size', 'Mode', 'Last Changed']
+
+ def __init__(self, dname=None):
+ gtk.GenericTreeModel.__init__(self)
+ self._sort_column_id = 0
+ self._sort_order = gtk.SORT_ASCENDING
+
+ if not dname:
+ self.dirname = os.path.expanduser('~')
+ else:
+ self.dirname = os.path.abspath(dname)
+ self.files = ['..'] + [f for f in os.listdir(self.dirname)]
+ return
+
+ def get_pathname(self, path):
+ filename = self.files[path[0]]
+ return os.path.join(self.dirname, filename)
+
+ def is_folder(self, path):
+ filename = self.files[path[0]]
+ pathname = os.path.join(self.dirname, filename)
+ filestat = os.stat(pathname)
+ if stat.S_ISDIR(filestat.st_mode):
+ return True
+ return False
+
+ def get_column_names(self):
+ return self.column_names[:]
+
+ #
+ # GenericTreeModel Implementation
+ #
+ def on_get_flags(self):
+ return 0 # gtk.TREE_MODEL_ITERS_PERSIST
+
+ def on_get_n_columns(self):
+ return len(self.column_types)
+
+ def on_get_column_type(self, n):
+ return self.column_types[n]
+
+ def on_get_iter(self, path):
+ return self.files[path[0]]
+
+ def on_get_path(self, rowref):
+ return self.files.index(rowref)
+
+ def on_get_value(self, rowref, column):
+ fname = os.path.join(self.dirname, rowref)
+ try:
+ filestat = os.stat(fname)
+ except OSError:
+ return None
+ mode = filestat.st_mode
+ if column is 0:
+ if stat.S_ISDIR(mode):
+ return folderpb
+ else:
+ return filepb
+ elif column is 1:
+ return rowref
+ elif column is 2:
+ return filestat.st_size
+ elif column is 3:
+ return oct(stat.S_IMODE(mode))
+ return time.ctime(filestat.st_mtime)
+
+ def on_iter_next(self, rowref):
+ try:
+ i = self.files.index(rowref) + 1
+ return self.files[i]
+ except IndexError:
+ return None
+
+ def on_iter_children(self, rowref):
+ if rowref:
+ return None
+ return self.files[0]
+
+ def on_iter_has_child(self, rowref):
+ return False
+
+ def on_iter_n_children(self, rowref):
+ if rowref:
+ return 0
+ return len(self.files)
+
+ def on_iter_nth_child(self, rowref, n):
+ if rowref:
+ return None
+ try:
+ return self.files[n]
+ except IndexError:
+ return None
+
+ def on_iter_parent(child):
+ return None
+
+
+class GenericTreeModelExample:
+ def delete_event(self, widget, event, data=None):
+ gtk.main_quit()
+ return False
+
+ def __init__(self):
+ # Create a new window
+ self.window = gtk.Window(type=gtk.WINDOW_TOPLEVEL)
+
+ self.window.set_size_request(300, 200)
+
+ self.window.connect("delete_event", self.delete_event)
+
+ self.listmodel = FileListModel()
+
+ # create the TreeView
+ self.treeview = gtk.TreeView()
+
+ self.tvcolumns = []
+
+ # create the TreeViewColumns to display the data
+ for n, name in enumerate(self.listmodel.get_column_names()):
+ if n == 0:
+ cellpb = gtk.CellRendererPixbuf()
+ col = gtk.TreeViewColumn(name, cellpb, pixbuf=0)
+ cell = gtk.CellRendererText()
+ col.pack_start(cell, False)
+ col.add_attribute(cell, 'text', 1)
+ else:
+ cell = gtk.CellRendererText()
+ col = gtk.TreeViewColumn(name, cell, text=n + 1)
+ if n == 1:
+ cell.set_property('xalign', 1.0)
+
+ self.treeview.append_column(col)
+
+ self.treeview.connect('row-activated', self.open_file)
+
+ self.scrolledwindow = gtk.ScrolledWindow()
+ self.scrolledwindow.add(self.treeview)
+ self.window.add(self.scrolledwindow)
+ self.treeview.set_model(self.listmodel)
+ self.window.set_title(self.listmodel.dirname)
+ self.window.show_all()
+
+ def open_file(self, treeview, path, column):
+ model = treeview.get_model()
+ if model.is_folder(path):
+ pathname = model.get_pathname(path)
+ new_model = FileListModel(pathname)
+ self.window.set_title(new_model.dirname)
+ treeview.set_model(new_model)
+ return
+
+
+def main(demoapp=None):
+ demo = GenericTreeModelExample()
+ demo
+ gtk.main()
+
+if __name__ == "__main__":
+ main()
diff --git a/examples/demo/demos/TreeView/treemodel_filetree.py b/examples/demo/demos/TreeView/treemodel_filetree.py
new file mode 100644
index 00000000..0549436f
--- /dev/null
+++ b/examples/demo/demos/TreeView/treemodel_filetree.py
@@ -0,0 +1,279 @@
+#!/usr/bin/env python
+
+title = "File Tree (GenericTreeModel)"
+description = """
+This is a file list demo which makes use of the GenericTreeModel python
+implementation of the Gtk.TreeModel interface. This demo shows what methods
+need to be overridden to provide a valid TreeModel to a TreeView.
+"""
+
+import os
+import stat
+import time
+from collections import OrderedDict
+
+import pygtkcompat
+pygtkcompat.enable_gtk('3.0')
+
+import gtk
+
+
+folderxpm = [
+ "17 16 7 1",
+ " c #000000",
+ ". c #808000",
+ "X c yellow",
+ "o c #808080",
+ "O c #c0c0c0",
+ "+ c white",
+ "@ c None",
+ "@@@@@@@@@@@@@@@@@",
+ "@@@@@@@@@@@@@@@@@",
+ "@@+XXXX.@@@@@@@@@",
+ "@+OOOOOO.@@@@@@@@",
+ "@+OXOXOXOXOXOXO. ",
+ "@+XOXOXOXOXOXOX. ",
+ "@+OXOXOXOXOXOXO. ",
+ "@+XOXOXOXOXOXOX. ",
+ "@+OXOXOXOXOXOXO. ",
+ "@+XOXOXOXOXOXOX. ",
+ "@+OXOXOXOXOXOXO. ",
+ "@+XOXOXOXOXOXOX. ",
+ "@+OOOOOOOOOOOOO. ",
+ "@ ",
+ "@@@@@@@@@@@@@@@@@",
+ "@@@@@@@@@@@@@@@@@"
+ ]
+folderpb = gtk.gdk.pixbuf_new_from_xpm_data(folderxpm)
+
+filexpm = [
+ "12 12 3 1",
+ " c #000000",
+ ". c #ffff04",
+ "X c #b2c0dc",
+ "X XXX",
+ "X ...... XXX",
+ "X ...... X",
+ "X . ... X",
+ "X ........ X",
+ "X . .... X",
+ "X ........ X",
+ "X . .. X",
+ "X ........ X",
+ "X . .. X",
+ "X ........ X",
+ "X X"
+ ]
+filepb = gtk.gdk.pixbuf_new_from_xpm_data(filexpm)
+
+
+class FileTreeModel(gtk.GenericTreeModel):
+ __gtype_name__ = 'DemoFileTreeModel'
+
+ column_types = (gtk.gdk.Pixbuf, str, int, str, str)
+ column_names = ['Name', 'Size', 'Mode', 'Last Changed']
+
+ def __init__(self, dname=None):
+ gtk.GenericTreeModel.__init__(self)
+ if not dname:
+ self.dirname = os.path.expanduser('~')
+ else:
+ self.dirname = os.path.abspath(dname)
+ self.files = self.build_file_dict(self.dirname)
+ return
+
+ def build_file_dict(self, dirname):
+ """
+ :Returns:
+ A dictionary containing the files in the given dirname keyed by filename.
+ If the child filename is a sub-directory, the dict value is a dict.
+ Otherwise it will be None.
+ """
+ d = OrderedDict()
+ for fname in os.listdir(dirname):
+ try:
+ filestat = os.stat(os.path.join(dirname, fname))
+ except OSError:
+ d[fname] = None
+ else:
+ d[fname] = OrderedDict() if stat.S_ISDIR(filestat.st_mode) else None
+
+ return d
+
+ def get_node_from_treepath(self, path):
+ """
+ :Returns:
+ The node stored at the given tree path in local storage.
+ """
+ # TreePaths are a series of integer indices so just iterate through them
+ # and index values by each integer since we are using an OrderedDict
+ if path is None:
+ path = []
+ node = self.files
+ for index in path:
+ node = list(node.values())[index]
+ return node
+
+ def get_node_from_filepath(self, filepath):
+ """
+ :Returns:
+ The node stored at the given file path in local storage.
+ """
+ if not filepath:
+ return self.files
+ node = self.files
+ for key in filepath.split(os.path.sep):
+ node = node[key]
+ return node
+
+ def get_column_names(self):
+ return self.column_names[:]
+
+ #
+ # GenericTreeModel Implementation
+ #
+
+ def on_get_flags(self):
+ return 0
+
+ def on_get_n_columns(self):
+ return len(self.column_types)
+
+ def on_get_column_type(self, n):
+ return self.column_types[n]
+
+ def on_get_path(self, relpath):
+ path = []
+ node = self.files
+ for key in relpath.split(os.path.sep):
+ path.append(list(node.keys()).index(key))
+ node = node[key]
+ return path
+
+ def on_get_value(self, relpath, column):
+ fname = os.path.join(self.dirname, relpath)
+ try:
+ filestat = os.stat(fname)
+ except OSError:
+ return None
+ mode = filestat.st_mode
+ if column is 0:
+ if stat.S_ISDIR(mode):
+ return folderpb
+ else:
+ return filepb
+ elif column is 1:
+ return os.path.basename(relpath)
+ elif column is 2:
+ return filestat.st_size
+ elif column is 3:
+ return oct(stat.S_IMODE(mode))
+ return time.ctime(filestat.st_mtime)
+
+ def on_get_iter(self, path):
+ filepath = ''
+ value = self.files
+ for index in path:
+ filepath = os.path.join(filepath, list(value.keys())[index])
+ value = list(value.values())[index]
+ return filepath
+
+ def on_iter_next(self, filepath):
+ parent_path, child_path = os.path.split(filepath)
+ parent = self.get_node_from_filepath(parent_path)
+
+ # Index of filepath within its parents child list
+ sibling_names = list(parent.keys())
+ index = sibling_names.index(child_path)
+ try:
+ return os.path.join(parent_path, sibling_names[index + 1])
+ except IndexError:
+ return None
+
+ def on_iter_children(self, filepath):
+ if filepath:
+ children = list(self.get_node_from_filepath(filepath).keys())
+ if children:
+ return os.path.join(filepath, children[0])
+ elif self.files:
+ return list(self.files.keys())[0]
+
+ return None
+
+ def on_iter_has_child(self, filepath):
+ return bool(self.get_node_from_filepath(filepath))
+
+ def on_iter_n_children(self, filepath):
+ return len(self.get_node_from_filepath(filepath))
+
+ def on_iter_nth_child(self, filepath, n):
+ try:
+ child = list(self.get_node_from_filepath(filepath).keys())[n]
+ if filepath:
+ return os.path.join(filepath, child)
+ else:
+ return child
+ except IndexError:
+ return None
+
+ def on_iter_parent(self, filepath):
+ return os.path.dirname(filepath)
+
+ def on_ref_node(self, filepath):
+ value = self.get_node_from_filepath(filepath)
+ if value is not None:
+ value.update(self.build_file_dict(os.path.join(self.dirname, filepath)))
+
+ def on_unref_node(self, filepath):
+ pass
+
+
+class GenericTreeModelExample:
+ def delete_event(self, widget, event, data=None):
+ gtk.main_quit()
+ return False
+
+ def __init__(self):
+ # Create a new window
+ self.window = gtk.Window(type=gtk.WINDOW_TOPLEVEL)
+ self.window.set_size_request(300, 200)
+ self.window.connect("delete_event", self.delete_event)
+
+ self.listmodel = FileTreeModel()
+
+ # create the TreeView
+ self.treeview = gtk.TreeView()
+
+ # create the TreeViewColumns to display the data
+ column_names = self.listmodel.get_column_names()
+ self.tvcolumn = [None] * len(column_names)
+ cellpb = gtk.CellRendererPixbuf()
+ self.tvcolumn[0] = gtk.TreeViewColumn(column_names[0],
+ cellpb, pixbuf=0)
+ cell = gtk.CellRendererText()
+ self.tvcolumn[0].pack_start(cell, False)
+ self.tvcolumn[0].add_attribute(cell, 'text', 1)
+ self.treeview.append_column(self.tvcolumn[0])
+ for n in range(1, len(column_names)):
+ cell = gtk.CellRendererText()
+ if n == 1:
+ cell.set_property('xalign', 1.0)
+ self.tvcolumn[n] = gtk.TreeViewColumn(column_names[n],
+ cell, text=n + 1)
+ self.treeview.append_column(self.tvcolumn[n])
+
+ self.scrolledwindow = gtk.ScrolledWindow()
+ self.scrolledwindow.add(self.treeview)
+ self.window.add(self.scrolledwindow)
+ self.treeview.set_model(self.listmodel)
+ self.window.set_title(self.listmodel.dirname)
+ self.window.show_all()
+
+
+def main(demoapp=None):
+ demo = GenericTreeModelExample()
+ demo
+ gtk.main()
+
+if __name__ == "__main__":
+ main()
diff --git a/examples/demo/demos/TreeView/treemodel_large.py b/examples/demo/demos/TreeView/treemodel_large.py
new file mode 100644
index 00000000..b129521c
--- /dev/null
+++ b/examples/demo/demos/TreeView/treemodel_large.py
@@ -0,0 +1,143 @@
+# -*- Mode: Python; py-indent-offset: 4 -*-
+# pygobject - Python bindings for the GObject library
+# Copyright (C) 2014 Simon Feltman
+#
+# 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, see <http://www.gnu.org/licenses/>.
+
+title = "Tree Model with Large Data"
+description = """
+Implementation of the Gtk.TreeModel interface to create a custom model.
+The demo uses a fake data store (it is not backed by a Python list) and is for
+the purpose of showing how to override the TreeModel interfaces virtual methods.
+"""
+
+from gi.repository import GObject
+from gi.repository import GLib
+from gi.repository import Gtk
+
+
+class Model(GObject.Object, Gtk.TreeModel):
+ columns_types = (str, str)
+ item_count = 100000
+ item_data = 'abcdefghijklmnopqrstuvwxyz'
+
+ def __init__(self):
+ super(Model, self).__init__()
+
+ def do_get_flags(self):
+ return Gtk.TreeModelFlags.LIST_ONLY
+
+ def do_get_n_columns(self):
+ return len(self.columns_types)
+
+ def do_get_column_type(self, n):
+ return self.columns_types[n]
+
+ def do_get_iter(self, path):
+ # Return False and an empty iter when out of range
+ index = path.get_indices()[0]
+ if index < 0 or index >= self.item_count:
+ return False, None
+
+ it = Gtk.TreeIter()
+ it.user_data = index
+ return True, it
+
+ def do_get_path(self, it):
+ return Gtk.TreePath([it.user_data])
+
+ def do_get_value(self, it, column):
+ if column == 0:
+ return str(it.user_data)
+ elif column == 1:
+ return self.item_data
+
+ def do_iter_next(self, it):
+ # Return False if there is not a next item
+ next = it.user_data + 1
+ if next >= self.item_count:
+ return False
+
+ # Set the iters data and return True
+ it.user_data = next
+ return True
+
+ def do_iter_previous(self, it):
+ prev = it.user_data - 1
+ if prev < 0:
+ return False
+
+ it.user_data = prev
+ return True
+
+ def do_iter_children(self, parent):
+ # If parent is None return the first item
+ if parent is None:
+ it = Gtk.TreeIter()
+ it.user_data = 0
+ return True, it
+ return False, None
+
+ def do_iter_has_child(self, it):
+ return it is None
+
+ def do_iter_n_children(self, it):
+ # If iter is None, return the number of top level nodes
+ if it is None:
+ return self.item_count
+ return 0
+
+ def do_iter_nth_child(self, parent, n):
+ if parent is not None or n >= self.item_count:
+ return False, None
+ elif parent is None:
+ # If parent is None, return the nth iter
+ it = Gtk.TreeIter()
+ it.user_data = n
+ return True, it
+
+ def do_iter_parent(self, child):
+ return False, None
+
+
+def main(demoapp=None):
+ model = Model()
+ # Use fixed-height-mode to get better model load and display performance.
+ view = Gtk.TreeView(fixed_height_mode=True, headers_visible=False)
+ column = Gtk.TreeViewColumn()
+ column.props.sizing = Gtk.TreeViewColumnSizing.FIXED
+
+ renderer1 = Gtk.CellRendererText()
+ renderer2 = Gtk.CellRendererText()
+ column.pack_start(renderer1, expand=True)
+ column.pack_start(renderer2, expand=True)
+ column.add_attribute(renderer1, 'text', 0)
+ column.add_attribute(renderer2, 'text', 1)
+ view.append_column(column)
+
+ scrolled = Gtk.ScrolledWindow()
+ scrolled.add(view)
+
+ window = Gtk.Window(title=title)
+ window.set_size_request(480, 640)
+ window.add(scrolled)
+ window.show_all()
+ GLib.timeout_add(10, lambda *args: view.set_model(model))
+ return window
+
+
+if __name__ == "__main__":
+ window = main()
+ window.connect('destroy', Gtk.main_quit)
+ Gtk.main()
diff --git a/examples/demo/demos/__init__.py b/examples/demo/demos/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/examples/demo/demos/__init__.py
diff --git a/examples/demo/demos/appwindow.py b/examples/demo/demos/appwindow.py
new file mode 100644
index 00000000..60632f12
--- /dev/null
+++ b/examples/demo/demos/appwindow.py
@@ -0,0 +1,407 @@
+#!/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 = "Application main window"
+description = """
+Demonstrates a typical application window with menubar, toolbar, statusbar.
+"""
+
+import os
+
+from gi.repository import GdkPixbuf, Gtk
+
+
+infobar = None
+window = None
+messagelabel = None
+_demoapp = None
+
+
+def widget_destroy(widget, button):
+ widget.destroy()
+
+
+def activate_action(action, user_data=None):
+ global window
+
+ name = action.get_name()
+ _type = type(action)
+ if name == 'DarkTheme':
+ value = action.get_active()
+ settings = Gtk.Settings.get_default()
+ settings.set_property('gtk-application-prefer-dark-theme', value)
+ return
+
+ dialog = Gtk.MessageDialog(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, user_data=None):
+ global infobar
+ global messagelabel
+
+ name = current.get_name()
+ _type = type(current)
+ active = current.get_active()
+ value = current.get_current_value()
+ if active:
+ text = 'You activated radio action: "%s" of type %s.\n Current value: %d' % (name, _type, value)
+ messagelabel.set_text(text)
+ infobar.set_message_type(Gtk.MessageType(value))
+ infobar.show()
+
+
+def update_statusbar(buffer, statusbar):
+ statusbar.pop(0)
+ count = buffer.get_char_count()
+
+ iter = buffer.get_iter_at_mark(buffer.get_insert())
+ row = iter.get_line()
+ col = iter.get_line_offset()
+ msg = 'Cursor at row %d column %d - %d chars in document' % (row, col, count)
+
+ statusbar.push(0, msg)
+
+
+def mark_set_callback(buffer, new_location, mark, data):
+ update_statusbar(buffer, data)
+
+
+def about_cb(widget, user_data=None):
+ global window
+
+ authors = ['John (J5) Palmieri',
+ 'Tomeu Vizoso',
+ 'and many more...']
+
+ documentors = ['David Malcolm',
+ 'Zack Goldberg',
+ 'and many more...']
+
+ license = """
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the Gnome Library; see the file COPYING.LIB. If not,
+write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+"""
+ dirname = os.path.abspath(os.path.dirname(__file__))
+ filename = os.path.join(dirname, 'data', 'gtk-logo-rgb.gif')
+ pixbuf = GdkPixbuf.Pixbuf.new_from_file(filename)
+ transparent = pixbuf.add_alpha(True, 0xff, 0xff, 0xff)
+
+ about = Gtk.AboutDialog(parent=window,
+ program_name='GTK+ Code Demos',
+ version='0.1',
+ copyright='(C) 2010 The PyGI Team',
+ license=license,
+ website='http://live.gnome.org/PyGI',
+ comments='Program to demonstrate PyGI functions.',
+ authors=authors,
+ documenters=documentors,
+ logo=transparent,
+ title='About GTK+ Code Demos')
+
+ about.connect('response', widget_destroy)
+ about.show()
+
+
+action_entries = (
+ ("FileMenu", None, "_File"), # name, stock id, label
+ ("OpenMenu", None, "_Open"), # name, stock id, label
+ ("PreferencesMenu", None, "_Preferences"), # name, stock id, label
+ ("ColorMenu", None, "_Color"), # name, stock id, label
+ ("ShapeMenu", None, "_Shape"), # name, stock id, label
+ ("HelpMenu", None, "_Help"), # name, stock id, label
+ ("New", Gtk.STOCK_NEW, # name, stock id
+ "_New", "<control>N", # label, accelerator
+ "Create a new file", # tooltip
+ activate_action),
+ ("File1", None, # name, stock id
+ "File1", None, # label, accelerator
+ "Open first file", # tooltip
+ activate_action),
+ ("Save", Gtk.STOCK_SAVE, # name, stock id
+ "_Save", "<control>S", # label, accelerator
+ "Save current file", # tooltip
+ activate_action),
+ ("SaveAs", Gtk.STOCK_SAVE, # name, stock id
+ "Save _As...", None, # label, accelerator
+ "Save to a file", # tooltip
+ activate_action),
+ ("Quit", Gtk.STOCK_QUIT, # name, stock id
+ "_Quit", "<control>Q", # label, accelerator
+ "Quit", # tooltip
+ activate_action),
+ ("About", None, # name, stock id
+ "_About", "<control>A", # label, accelerator
+ "About", # tooltip
+ about_cb),
+ ("Logo", "demo-gtk-logo", # name, stock id
+ None, None, # label, accelerator
+ "GTK+", # tooltip
+ activate_action),
+)
+
+toggle_action_entries = (
+ ("Bold", Gtk.STOCK_BOLD, # name, stock id
+ "_Bold", "<control>B", # label, accelerator
+ "Bold", # tooltip
+ activate_action,
+ True), # is_active
+ ("DarkTheme", None, # name, stock id
+ "_Prefer Dark Theme", None, # label, accelerator
+ "Prefer Dark Theme", # tooltip
+ activate_action,
+ False), # is_active
+)
+
+(COLOR_RED,
+ COLOR_GREEN,
+ COLOR_BLUE) = range(3)
+
+color_action_entries = (
+ ("Red", None, # name, stock id
+ "_Red", "<control>R", # label, accelerator
+ "Blood", COLOR_RED), # tooltip, value
+ ("Green", None, # name, stock id
+ "_Green", "<control>G", # label, accelerator
+ "Grass", COLOR_GREEN), # tooltip, value
+ ("Blue", None, # name, stock id
+ "_Blue", "<control>B", # label, accelerator
+ "Sky", COLOR_BLUE), # tooltip, value
+)
+
+(SHAPE_SQUARE,
+ SHAPE_RECTANGLE,
+ SHAPE_OVAL) = range(3)
+
+shape_action_entries = (
+ ("Square", None, # name, stock id
+ "_Square", "<control>S", # label, accelerator
+ "Square", SHAPE_SQUARE), # tooltip, value
+ ("Rectangle", None, # name, stock id
+ "_Rectangle", "<control>R", # label, accelerator
+ "Rectangle", SHAPE_RECTANGLE), # tooltip, value
+ ("Oval", None, # name, stock id
+ "_Oval", "<control>O", # label, accelerator
+ "Egg", SHAPE_OVAL), # tooltip, value
+)
+
+ui_info = """
+<ui>
+ <menubar name='MenuBar'>
+ <menu action='FileMenu'>
+ <menuitem action='New'/>
+ <menuitem action='Open'/>
+ <menuitem action='Save'/>
+ <menuitem action='SaveAs'/>
+ <separator/>
+ <menuitem action='Quit'/>
+ </menu>
+ <menu action='PreferencesMenu'>
+ <menuitem action='DarkTheme'/>
+ <menu action='ColorMenu'>
+ <menuitem action='Red'/>
+ <menuitem action='Green'/>
+ <menuitem action='Blue'/>
+ </menu>
+ <menu action='ShapeMenu'>
+ <menuitem action='Square'/>
+ <menuitem action='Rectangle'/>
+ <menuitem action='Oval'/>
+ </menu>
+ <menuitem action='Bold'/>
+ </menu>
+ <menu action='HelpMenu'>
+ <menuitem action='About'/>
+ </menu>
+ </menubar>
+ <toolbar name='ToolBar'>
+ <toolitem action='Open'>
+ <menu action='OpenMenu'>
+ <menuitem action='File1'/>
+ </menu>
+ </toolitem>
+ <toolitem action='Quit'/>
+ <separator action='Sep1'/>
+ <toolitem action='Logo'/>
+ </toolbar>
+</ui>
+"""
+
+
+def _quit(*args):
+ Gtk.main_quit()
+
+
+def register_stock_icons():
+ """
+ This function registers our custom toolbar icons, so they can be themed.
+ It's totally optional to do this, you could just manually insert icons
+ and have them not be themeable, especially if you never expect people
+ to theme your app.
+ """
+ '''
+ item = Gtk.StockItem()
+ item.stock_id = 'demo-gtk-logo'
+ item.label = '_GTK!'
+ item.modifier = 0
+ item.keyval = 0
+ item.translation_domain = None
+
+ Gtk.stock_add(item, 1)
+ '''
+ global _demoapp
+
+ factory = Gtk.IconFactory()
+ factory.add_default()
+
+ if _demoapp is None:
+ filename = os.path.join('data', 'gtk-logo-rgb.gif')
+ else:
+ filename = _demoapp.find_file('gtk-logo-rgb.gif')
+
+ pixbuf = GdkPixbuf.Pixbuf.new_from_file(filename)
+ transparent = pixbuf.add_alpha(True, 0xff, 0xff, 0xff)
+ icon_set = Gtk.IconSet.new_from_pixbuf(transparent)
+
+ factory.add('demo-gtk-logo', icon_set)
+
+
+class ToolMenuAction(Gtk.Action):
+ __gtype_name__ = "GtkToolMenuAction"
+
+ def do_create_tool_item(self):
+ return Gtk.MenuToolButton()
+
+
+def main(demoapp=None):
+ global infobar
+ global window
+ global messagelabel
+ global _demoapp
+
+ _demoapp = demoapp
+
+ register_stock_icons()
+
+ window = Gtk.Window()
+ 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,
+ homogeneous=False)
+ window.add(table)
+
+ action_group = Gtk.ActionGroup(name='AppWindowActions')
+ open_action = ToolMenuAction(name='Open',
+ stock_id=Gtk.STOCK_OPEN,
+ label='_Open',
+ tooltip='Open a file')
+
+ action_group.add_action(open_action)
+ action_group.add_actions(action_entries)
+ action_group.add_toggle_actions(toggle_action_entries)
+ action_group.add_radio_actions(color_action_entries,
+ COLOR_RED,
+ activate_radio_action)
+ action_group.add_radio_actions(shape_action_entries,
+ SHAPE_SQUARE,
+ activate_radio_action)
+
+ merge = Gtk.UIManager()
+ merge.insert_action_group(action_group, 0)
+ window.add_accel_group(merge.get_accel_group())
+
+ merge.add_ui_from_string(ui_info)
+
+ bar = merge.get_widget('/MenuBar')
+ bar.show()
+ table.attach(bar, 0, 1, 0, 1,
+ Gtk.AttachOptions.EXPAND | Gtk.AttachOptions.FILL,
+ 0, 0, 0)
+
+ bar = merge.get_widget('/ToolBar')
+ bar.show()
+ table.attach(bar, 0, 1, 1, 2,
+ Gtk.AttachOptions.EXPAND | Gtk.AttachOptions.FILL,
+ 0, 0, 0)
+
+ infobar = Gtk.InfoBar()
+ infobar.set_no_show_all(True)
+ messagelabel = Gtk.Label()
+ messagelabel.show()
+ infobar.get_content_area().pack_start(messagelabel, True, True, 0)
+ infobar.add_button(Gtk.STOCK_OK, Gtk.ResponseType.OK)
+ infobar.connect('response', lambda a, b: Gtk.Widget.hide(a))
+
+ table.attach(infobar, 0, 1, 2, 3,
+ Gtk.AttachOptions.EXPAND | Gtk.AttachOptions.FILL,
+ 0, 0, 0)
+
+ sw = Gtk.ScrolledWindow(hadjustment=None,
+ vadjustment=None)
+ sw.set_shadow_type(Gtk.ShadowType.IN)
+ table.attach(sw, 0, 1, 3, 4,
+ Gtk.AttachOptions.EXPAND | Gtk.AttachOptions.FILL,
+ Gtk.AttachOptions.EXPAND | Gtk.AttachOptions.FILL,
+ 0, 0)
+
+ contents = Gtk.TextView()
+ contents.grab_focus()
+ sw.add(contents)
+
+ # Create statusbar
+ statusbar = Gtk.Statusbar()
+ table.attach(statusbar, 0, 1, 4, 5,
+ Gtk.AttachOptions.EXPAND | Gtk.AttachOptions.FILL,
+ 0, 0, 0)
+
+ # show text widget info in the statusbar
+ buffer = contents.get_buffer()
+ buffer.connect('changed', update_statusbar, statusbar)
+ buffer.connect('mark_set', mark_set_callback, statusbar)
+
+ update_statusbar(buffer, statusbar)
+
+ window.set_default_size(200, 200)
+ window.show_all()
+ Gtk.main()
+
+if __name__ == '__main__':
+ main()
diff --git a/examples/demo/demos/assistant.py b/examples/demo/demos/assistant.py
new file mode 100644
index 00000000..b1d8720e
--- /dev/null
+++ b/examples/demo/demos/assistant.py
@@ -0,0 +1,134 @@
+#!/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 = "Assistant"
+description = """
+Demonstrates a sample multistep assistant. Assistants are used to divide
+an operation into several simpler sequential steps, and to guide the user
+through these steps.
+"""
+
+
+from gi.repository import Gtk
+
+
+class AssistantApp:
+ def __init__(self):
+ self.assistant = Gtk.Assistant()
+ self.assistant.set_default_size(-1, 300)
+
+ self.create_page1()
+ self.create_page2()
+ self.create_page3()
+
+ self.assistant.connect('cancel', self.on_close_cancel)
+ self.assistant.connect('close', self.on_close_cancel)
+ self.assistant.connect('apply', self.on_apply)
+ self.assistant.connect('prepare', self.on_prepare)
+
+ self.assistant.show()
+
+ def on_close_cancel(self, assistant):
+ assistant.destroy()
+ Gtk.main_quit()
+
+ def on_apply(self, assistant):
+ # apply changes here; this is a fictional example so just do
+ # nothing here
+ pass
+
+ def on_prepare(self, assistant, page):
+ current_page = assistant.get_current_page()
+ n_pages = assistant.get_n_pages()
+ title = 'Sample assistant (%d of %d)' % (current_page + 1, n_pages)
+ assistant.set_title(title)
+
+ def on_entry_changed(self, widget):
+ page_number = self.assistant.get_current_page()
+ current_page = self.assistant.get_nth_page(page_number)
+ text = widget.get_text()
+
+ if text:
+ self.assistant.set_page_complete(current_page, True)
+ else:
+ self.assistant.set_page_complete(current_page, False)
+
+ def create_page1(self):
+ box = Gtk.HBox(homogeneous=False,
+ spacing=12)
+ box.set_border_width(12)
+ label = Gtk.Label(label='You must fill out this entry to continue:')
+ box.pack_start(label, False, False, 0)
+
+ entry = Gtk.Entry()
+ box.pack_start(entry, True, True, 0)
+ entry.connect('changed', self.on_entry_changed)
+
+ box.show_all()
+ self.assistant.append_page(box)
+ self.assistant.set_page_title(box, 'Page 1')
+ self.assistant.set_page_type(box, Gtk.AssistantPageType.INTRO)
+
+ pixbuf = self.assistant.render_icon(Gtk.STOCK_DIALOG_INFO,
+ Gtk.IconSize.DIALOG,
+ None)
+
+ self.assistant.set_page_header_image(box, pixbuf)
+
+ def create_page2(self):
+ box = Gtk.VBox(homogeneous=False,
+ spacing=12)
+ box.set_border_width(12)
+
+ checkbutton = Gtk.CheckButton(label='This is optional data, you may continue even if you do not check this')
+ box.pack_start(checkbutton, False, False, 0)
+
+ box.show_all()
+
+ self.assistant.append_page(box)
+ self.assistant.set_page_complete(box, True)
+ self.assistant.set_page_title(box, 'Page 2')
+
+ pixbuf = self.assistant.render_icon(Gtk.STOCK_DIALOG_INFO,
+ Gtk.IconSize.DIALOG,
+ None)
+ self.assistant.set_page_header_image(box, pixbuf)
+
+ def create_page3(self):
+ label = Gtk.Label(label='This is a confirmation page, press "Apply" to apply changes')
+ label.show()
+ self.assistant.append_page(label)
+ self.assistant.set_page_complete(label, True)
+ self.assistant.set_page_title(label, 'Confirmation')
+ self.assistant.set_page_type(label, Gtk.AssistantPageType.CONFIRM)
+
+ pixbuf = self.assistant.render_icon(Gtk.STOCK_DIALOG_INFO,
+ Gtk.IconSize.DIALOG,
+ None)
+ self.assistant.set_page_header_image(label, pixbuf)
+
+
+def main(demoapp=None):
+ AssistantApp()
+ Gtk.main()
+
+if __name__ == '__main__':
+ main()
diff --git a/examples/demo/demos/builder.py b/examples/demo/demos/builder.py
new file mode 100644
index 00000000..c557d068
--- /dev/null
+++ b/examples/demo/demos/builder.py
@@ -0,0 +1,64 @@
+#!/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 = "Builder"
+description = """
+Demonstrates an interface loaded from a XML description.
+"""
+
+
+import os
+
+from gi.repository import Gtk
+
+
+class BuilderApp:
+ def __init__(self, demoapp):
+ self.demoapp = demoapp
+
+ self.builder = Gtk.Builder()
+ if demoapp is None:
+ filename = os.path.join('data', 'demo.ui')
+ else:
+ filename = demoapp.find_file('demo.ui')
+
+ self.builder.add_from_file(filename)
+ self.builder.connect_signals(self)
+
+ window = self.builder.get_object('window1')
+ window.connect('destroy', lambda x: Gtk.main_quit())
+ window.show_all()
+
+ def about_activate(self, action):
+ about_dlg = self.builder.get_object('aboutdialog1')
+ about_dlg.run()
+ about_dlg.hide()
+
+ def quit_activate(self, action):
+ Gtk.main_quit()
+
+
+def main(demoapp=None):
+ BuilderApp(demoapp)
+ Gtk.main()
+
+if __name__ == '__main__':
+ main()
diff --git a/examples/demo/demos/button_box.py b/examples/demo/demos/button_box.py
new file mode 100644
index 00000000..8c9a0bf7
--- /dev/null
+++ b/examples/demo/demos/button_box.py
@@ -0,0 +1,121 @@
+#!/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 = "Button Boxes"
+description = """
+The Button Box widgets are used to arrange buttons with padding.
+"""
+
+
+from gi.repository import Gtk
+
+
+class ButtonBoxApp:
+ def __init__(self):
+ window = Gtk.Window()
+ window.set_title('Button Boxes')
+ window.connect('destroy', lambda x: Gtk.main_quit())
+ window.set_border_width(10)
+
+ main_vbox = Gtk.VBox(homogeneous=False, spacing=0)
+ window.add(main_vbox)
+
+ frame_horz = Gtk.Frame(label='Horizontal Button Boxes')
+ main_vbox.pack_start(frame_horz, True, True, 10)
+
+ vbox = Gtk.VBox(homogeneous=False, spacing=0)
+ vbox.set_border_width(10)
+ frame_horz.add(vbox)
+
+ vbox.pack_start(
+ self.create_bbox(True, 'Spread', 40, Gtk.ButtonBoxStyle.SPREAD),
+ True, True, 0)
+
+ vbox.pack_start(
+ self.create_bbox(True, 'Edge', 40, Gtk.ButtonBoxStyle.EDGE),
+ True, True, 5)
+
+ vbox.pack_start(
+ self.create_bbox(True, 'Start', 40, Gtk.ButtonBoxStyle.START),
+ True, True, 5)
+
+ vbox.pack_start(
+ self.create_bbox(True, 'End', 40, Gtk.ButtonBoxStyle.END),
+ True, True, 5)
+
+ frame_vert = Gtk.Frame(label='Vertical Button Boxes')
+ main_vbox.pack_start(frame_vert, True, True, 10)
+
+ hbox = Gtk.HBox(homogeneous=False, spacing=0)
+ hbox.set_border_width(10)
+ frame_vert.add(hbox)
+
+ hbox.pack_start(
+ self.create_bbox(False, 'Spread', 30, Gtk.ButtonBoxStyle.SPREAD),
+ True, True, 0)
+
+ hbox.pack_start(
+ self.create_bbox(False, 'Edge', 30, Gtk.ButtonBoxStyle.EDGE),
+ True, True, 5)
+
+ hbox.pack_start(
+ self.create_bbox(False, 'Start', 30, Gtk.ButtonBoxStyle.START),
+ True, True, 5)
+
+ hbox.pack_start(
+ self.create_bbox(False, 'End', 30, Gtk.ButtonBoxStyle.END),
+ True, True, 5)
+
+ window.show_all()
+
+ def create_bbox(self, is_horizontal, title, spacing, layout):
+ frame = Gtk.Frame(label=title)
+
+ if is_horizontal:
+ bbox = Gtk.HButtonBox()
+ else:
+ bbox = Gtk.VButtonBox()
+
+ bbox.set_border_width(5)
+ frame.add(bbox)
+
+ 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)
+ bbox.add(button)
+
+ button = Gtk.Button.new_from_stock(Gtk.STOCK_CANCEL)
+ bbox.add(button)
+
+ button = Gtk.Button.new_from_stock(Gtk.STOCK_HELP)
+ bbox.add(button)
+
+ return frame
+
+
+def main(demoapp=None):
+ ButtonBoxApp()
+ Gtk.main()
+
+if __name__ == '__main__':
+ main()
diff --git a/examples/demo/demos/clipboard.py b/examples/demo/demos/clipboard.py
new file mode 100644
index 00000000..1058d3e0
--- /dev/null
+++ b/examples/demo/demos/clipboard.py
@@ -0,0 +1,227 @@
+#!/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 = "Clipboard"
+description = """
+GtkClipboard is used for clipboard handling. This demo shows how to
+copy and paste text to and from the clipboard.
+
+It also shows how to transfer images via the clipboard or via
+drag-and-drop, and how to make clipboard contents persist after
+the application exits. Clipboard persistence requires a clipboard
+manager to run.
+"""
+
+
+from gi.repository import Gtk, Gdk
+
+
+class ClipboardApp:
+ def __init__(self):
+ self.window = Gtk.Window()
+ self.window.set_title('Clipboard demo')
+ self.window.connect('destroy', lambda w: Gtk.main_quit())
+
+ vbox = Gtk.VBox(homogeneous=False, spacing=0)
+ vbox.set_border_width(8)
+ self.window.add(vbox)
+
+ label = Gtk.Label(label='"Copy" will copy the text\nin the entry to the clipboard')
+ 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)
+
+ # create first entry
+ entry = Gtk.Entry()
+ hbox.pack_start(entry, True, True, 0)
+
+ # create button
+ button = Gtk.Button.new_from_stock(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)
+
+ # create secondary entry
+ entry = Gtk.Entry()
+ hbox.pack_start(entry, True, True, 0)
+ # create button
+ button = Gtk.Button.new_from_stock(Gtk.STOCK_PASTE)
+ hbox.pack_start(button, False, False, 0)
+ button.connect('clicked', self.paste_button_clicked, entry)
+
+ label = Gtk.Label(label='Images can be transferred via the clipboard, too')
+ 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)
+
+ # create the first image
+ image = Gtk.Image(stock=Gtk.STOCK_DIALOG_WARNING,
+ icon_size=Gtk.IconSize.BUTTON)
+
+ ebox = Gtk.EventBox()
+ ebox.add(image)
+ hbox.add(ebox)
+
+ # make ebox a drag source
+ ebox.drag_source_set(Gdk.ModifierType.BUTTON1_MASK,
+ None, Gdk.DragAction.COPY)
+ 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
+ ebox.drag_dest_set(Gtk.DestDefaults.ALL,
+ None, Gdk.DragAction.COPY)
+ ebox.drag_dest_add_image_targets()
+ ebox.connect('drag-data-received', self.drag_data_received, image)
+
+ # context menu on ebox
+ ebox.connect('button-press-event', self.button_press, image)
+
+ # create the second image
+ image = Gtk.Image(stock=Gtk.STOCK_STOP,
+ icon_size=Gtk.IconSize.BUTTON)
+
+ ebox = Gtk.EventBox()
+ ebox.add(image)
+ hbox.add(ebox)
+
+ # make ebox a drag source
+ ebox.drag_source_set(Gdk.ModifierType.BUTTON1_MASK,
+ None, Gdk.DragAction.COPY)
+ 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
+ ebox.drag_dest_set(Gtk.DestDefaults.ALL,
+ None, Gdk.DragAction.COPY)
+ ebox.drag_dest_add_image_targets()
+ ebox.connect('drag-data-received', self.drag_data_received, image)
+
+ # context menu on ebox
+ ebox.connect('button-press-event', self.button_press, image)
+
+ # tell the clipboard manager to make data persistent
+ # 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)
+
+ self.window.show_all()
+
+ def copy_button_clicked(self, button, entry):
+ # get the default clipboard
+ atom = Gdk.atom_intern('CLIPBOARD', True)
+ clipboard = entry.get_clipboard(atom)
+
+ # set the clipboard's text
+ # FIXME: don't require passing length argument
+ clipboard.set_text(entry.get_text(), -1)
+
+ def paste_received(self, clipboard, text, entry):
+ if text is not None:
+ entry.set_text(text)
+
+ def paste_button_clicked(self, button, entry):
+ # get the default clipboard
+ atom = Gdk.atom_intern('CLIPBOARD', True)
+ clipboard = entry.get_clipboard(atom)
+
+ # set the clipboard's text
+ clipboard.request_text(self.paste_received, entry)
+
+ def get_image_pixbuf(self, image):
+ # FIXME: We should hide storage types in an override
+ storage_type = image.get_storage_type()
+ if storage_type == Gtk.ImageType.PIXBUF:
+ return image.get_pixbuf()
+ elif storage_type == Gtk.ImageType.STOCK:
+ (stock_id, size) = image.get_stock()
+ return image.render_icon(stock_id, size, None)
+
+ return None
+
+ def drag_begin(self, widget, context, data):
+ pixbuf = self.get_image_pixbuf(data)
+ Gtk.drag_set_icon_pixbuf(context, pixbuf, -2, -2)
+
+ def drag_data_get(self, widget, context, selection_data, info, time, data):
+ pixbuf = self.get_image_pixbuf(data)
+ selection_data.set_pixbuf(pixbuf)
+
+ def drag_data_received(self, widget, context, x, y, selection_data, info, time, data):
+ if selection_data.get_length() > 0:
+ pixbuf = selection_data.get_pixbuf()
+ data.set_from_pixbuf(pixbuf)
+
+ def copy_image(self, item, data):
+ # get the default clipboard
+ atom = Gdk.atom_intern('CLIPBOARD', True)
+ clipboard = Gtk.Clipboard.get(atom)
+ pixbuf = self.get_image_pixbuf(data)
+
+ clipboard.set_image(pixbuf)
+
+ def paste_image(self, item, data):
+ # get the default clipboard
+ atom = Gdk.atom_intern('CLIPBOARD', True)
+ clipboard = Gtk.Clipboard.get(atom)
+ pixbuf = clipboard.wait_for_image()
+
+ if pixbuf is not None:
+ data.set_from_pixbuf(pixbuf)
+
+ def button_press(self, widget, event, data):
+ if event.button != 3:
+ return False
+
+ self.menu = Gtk.Menu()
+
+ item = Gtk.ImageMenuItem.new_from_stock(Gtk.STOCK_COPY, None)
+ item.connect('activate', self.copy_image, data)
+ item.show()
+ self.menu.append(item)
+
+ item = Gtk.ImageMenuItem.new_from_stock(Gtk.STOCK_PASTE, None)
+ item.connect('activate', self.paste_image, data)
+ item.show()
+ self.menu.append(item)
+
+ self.menu.popup(None, None, None, None, event.button, event.time)
+
+
+def main(demoapp=None):
+ ClipboardApp()
+ Gtk.main()
+
+if __name__ == '__main__':
+ main()
diff --git a/examples/demo/demos/colorselector.py b/examples/demo/demos/colorselector.py
new file mode 100644
index 00000000..3ad65eb6
--- /dev/null
+++ b/examples/demo/demos/colorselector.py
@@ -0,0 +1,111 @@
+#!/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 = "Color Selector"
+description = """
+ GtkColorSelection lets the user choose a color. GtkColorSelectionDialog is
+ a prebuilt dialog containing a GtkColorSelection.
+ """
+
+
+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.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')
+ self.window.set_border_width(8)
+ self.window.connect('destroy', lambda w: Gtk.main_quit())
+
+ vbox = Gtk.VBox(homogeneous=False,
+ spacing=8)
+ vbox.set_border_width(8)
+ self.window.add(vbox)
+
+ # create color swatch area
+ frame = Gtk.Frame()
+ frame.set_shadow_type(Gtk.ShadowType.IN)
+ vbox.pack_start(frame, True, True, 0)
+
+ self.da = Gtk.DrawingArea()
+ self.da.connect('draw', self.draw_cb)
+
+ # set a minimum size
+ self.da.set_size_request(200, 200)
+ # set the color
+ self.da.override_background_color(0, self.color)
+ frame.add(self.da)
+
+ alignment = Gtk.Alignment(xalign=1.0,
+ yalign=0.5,
+ xscale=0.0,
+ yscale=0.0)
+
+ button = Gtk.Button(label='_Change the above color',
+ use_underline=True)
+ alignment.add(button)
+ vbox.pack_start(alignment, False, False, 0)
+
+ button.connect('clicked', self.change_color_cb)
+
+ self.window.show_all()
+
+ 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_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_rgba()
+ self.da.override_background_color(0, self.color)
+
+ dialog.destroy()
+
+
+def main(demoapp=None):
+ ColorSelectorApp()
+ Gtk.main()
+
+if __name__ == '__main__':
+ main()
diff --git a/examples/demo/demos/combobox.py b/examples/demo/demos/combobox.py
new file mode 100644
index 00000000..be70d36f
--- /dev/null
+++ b/examples/demo/demos/combobox.py
@@ -0,0 +1,318 @@
+#!/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 = "Combo boxes"
+description = """
+The ComboBox widget allows to select one option out of a list.
+The ComboBoxEntry additionally allows the user to enter a value
+that is not in the list of options.
+
+How the options are displayed is controlled by cell renderers.
+ """
+
+
+from gi.repository import Gtk, Gdk, GdkPixbuf, GLib, GObject
+
+
+(PIXBUF_COL,
+ TEXT_COL) = range(2)
+
+
+class MaskEntry(Gtk.Entry):
+ __gtype_name__ = 'MaskEntry'
+
+ def __init__(self, mask=None):
+ self.mask = mask
+ super(MaskEntry, self).__init__()
+
+ 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):
+ if self.mask:
+ if not GLib.regex_match_simple(self.mask,
+ self.get_text(), 0, 0):
+ self.override_color(0, self.error_color)
+ return
+
+ self.override_color(0, self.normal_color)
+
+ def changed_cb(self, entry):
+ self.set_background()
+
+
+class ComboboxApp:
+ def __init__(self, demoapp):
+ self.demoapp = demoapp
+
+ self.window = Gtk.Window()
+ self.window.set_title('Combo boxes')
+ self.window.set_border_width(10)
+ self.window.connect('destroy', lambda w: Gtk.main_quit())
+
+ vbox = Gtk.VBox(homogeneous=False, spacing=2)
+ self.window.add(vbox)
+
+ frame = Gtk.Frame(label='Some stock icons')
+ vbox.pack_start(frame, False, False, 0)
+
+ box = Gtk.VBox(homogeneous=False, spacing=0)
+ box.set_border_width(5)
+ frame.add(box)
+
+ model = self.create_stock_icon_store()
+ combo = Gtk.ComboBox(model=model)
+ box.add(combo)
+
+ renderer = Gtk.CellRendererPixbuf()
+ combo.pack_start(renderer, False)
+
+ # FIXME: override set_attributes
+ combo.add_attribute(renderer, 'pixbuf', PIXBUF_COL)
+ combo.set_cell_data_func(renderer, self.set_sensitive, None)
+
+ renderer = Gtk.CellRendererText()
+ combo.pack_start(renderer, True)
+ combo.add_attribute(renderer, 'text', TEXT_COL)
+ combo.set_cell_data_func(renderer, self.set_sensitive, None)
+
+ combo.set_row_separator_func(self.is_separator, None)
+ combo.set_active(0)
+
+ # a combobox demonstrating trees
+ frame = Gtk.Frame(label='Where are we ?')
+ vbox.pack_start(frame, False, False, 0)
+
+ box = Gtk.VBox(homogeneous=False, spacing=0)
+ box.set_border_width(5)
+ frame.add(box)
+
+ model = self.create_capital_store()
+ combo = Gtk.ComboBox(model=model)
+ box.add(combo)
+
+ renderer = Gtk.CellRendererText()
+ combo.pack_start(renderer, True)
+ 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')
+ 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)
+
+ box = Gtk.VBox(homogeneous=False, spacing=0)
+ box.set_border_width(5)
+ frame.add(box)
+
+ 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)$')
+
+ 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()
+
+ combo.bind_property('active-id',
+ entry, 'text',
+ GObject.BindingFlags.BIDIRECTIONAL)
+
+ box.add(entry)
+ self.window.show_all()
+
+ def strip_underscore(self, s):
+ return s.replace('_', '')
+
+ def create_stock_icon_store(self):
+ stock_id = (Gtk.STOCK_DIALOG_WARNING,
+ Gtk.STOCK_STOP,
+ Gtk.STOCK_NEW,
+ Gtk.STOCK_CLEAR,
+ None,
+ Gtk.STOCK_OPEN)
+
+ cellview = Gtk.CellView()
+ store = Gtk.ListStore(GdkPixbuf.Pixbuf, str)
+
+ for id in stock_id:
+ if id is not None:
+ pixbuf = cellview.render_icon(id, Gtk.IconSize.BUTTON, None)
+ item = Gtk.stock_lookup(id)
+ label = self.strip_underscore(item.label)
+ store.append((pixbuf, label))
+ else:
+ store.append((None, 'separator'))
+
+ return store
+
+ def set_sensitive(self, cell_layout, cell, tree_model, treeiter, data):
+ """
+ A GtkCellLayoutDataFunc that demonstrates how one can control
+ sensitivity of rows. This particular function does nothing
+ useful and just makes the second row insensitive.
+ """
+
+ path = tree_model.get_path(treeiter)
+ indices = path.get_indices()
+
+ sensitive = not(indices[0] == 1)
+
+ cell.set_property('sensitive', sensitive)
+
+ def is_separator(self, model, treeiter, data):
+ """
+ A GtkTreeViewRowSeparatorFunc that demonstrates how rows can be
+ rendered as separators. This particular function does nothing
+ useful and just turns the fourth row into a separator.
+ """
+
+ path = model.get_path(treeiter)
+
+ indices = path.get_indices()
+ result = (indices[0] == 4)
+
+ return result
+
+ def create_capital_store(self):
+ capitals = (
+ {'group': 'A - B', 'capital': None},
+ {'group': None, 'capital': 'Albany'},
+ {'group': None, 'capital': 'Annapolis'},
+ {'group': None, 'capital': 'Atlanta'},
+ {'group': None, 'capital': 'Augusta'},
+ {'group': None, 'capital': 'Austin'},
+ {'group': None, 'capital': 'Baton Rouge'},
+ {'group': None, 'capital': 'Bismarck'},
+ {'group': None, 'capital': 'Boise'},
+ {'group': None, 'capital': 'Boston'},
+ {'group': 'C - D', 'capital': None},
+ {'group': None, 'capital': 'Carson City'},
+ {'group': None, 'capital': 'Charleston'},
+ {'group': None, 'capital': 'Cheyeene'},
+ {'group': None, 'capital': 'Columbia'},
+ {'group': None, 'capital': 'Columbus'},
+ {'group': None, 'capital': 'Concord'},
+ {'group': None, 'capital': 'Denver'},
+ {'group': None, 'capital': 'Des Moines'},
+ {'group': None, 'capital': 'Dover'},
+ {'group': 'E - J', 'capital': None},
+ {'group': None, 'capital': 'Frankfort'},
+ {'group': None, 'capital': 'Harrisburg'},
+ {'group': None, 'capital': 'Hartford'},
+ {'group': None, 'capital': 'Helena'},
+ {'group': None, 'capital': 'Honolulu'},
+ {'group': None, 'capital': 'Indianapolis'},
+ {'group': None, 'capital': 'Jackson'},
+ {'group': None, 'capital': 'Jefferson City'},
+ {'group': None, 'capital': 'Juneau'},
+ {'group': 'K - O', 'capital': None},
+ {'group': None, 'capital': 'Lansing'},
+ {'group': None, 'capital': 'Lincon'},
+ {'group': None, 'capital': 'Little Rock'},
+ {'group': None, 'capital': 'Madison'},
+ {'group': None, 'capital': 'Montgomery'},
+ {'group': None, 'capital': 'Montpelier'},
+ {'group': None, 'capital': 'Nashville'},
+ {'group': None, 'capital': 'Oklahoma City'},
+ {'group': None, 'capital': 'Olympia'},
+ {'group': 'P - S', 'capital': None},
+ {'group': None, 'capital': 'Phoenix'},
+ {'group': None, 'capital': 'Pierre'},
+ {'group': None, 'capital': 'Providence'},
+ {'group': None, 'capital': 'Raleigh'},
+ {'group': None, 'capital': 'Richmond'},
+ {'group': None, 'capital': 'Sacramento'},
+ {'group': None, 'capital': 'Salem'},
+ {'group': None, 'capital': 'Salt Lake City'},
+ {'group': None, 'capital': 'Santa Fe'},
+ {'group': None, 'capital': 'Springfield'},
+ {'group': None, 'capital': 'St. Paul'},
+ {'group': 'T - Z', 'capital': None},
+ {'group': None, 'capital': 'Tallahassee'},
+ {'group': None, 'capital': 'Topeka'},
+ {'group': None, 'capital': 'Trenton'}
+ )
+
+ parent = None
+
+ store = Gtk.TreeStore(str)
+
+ for item in capitals:
+ if item['group']:
+ parent = store.append(None, (item['group'],))
+ elif item['capital']:
+ store.append(parent, (item['capital'],))
+
+ return store
+
+ def is_capital_sensistive(self, cell_layout, cell, tree_model, treeiter, data):
+ sensitive = not tree_model.iter_has_child(treeiter)
+ cell.set_property('sensitive', sensitive)
+
+ def fill_combo_entry(self, entry):
+ entry.append_text('One')
+ entry.append_text('Two')
+ entry.append_text('2\302\275')
+ entry.append_text('Three')
+
+
+def main(demoapp=None):
+ ComboboxApp(demoapp)
+ Gtk.main()
+
+if __name__ == '__main__':
+ main()
diff --git a/examples/demo/demos/data/alphatest.png b/examples/demo/demos/data/alphatest.png
new file mode 100644
index 00000000..eb5885f8
--- /dev/null
+++ b/examples/demo/demos/data/alphatest.png
Binary files differ
diff --git a/examples/demo/demos/data/apple-red.png b/examples/demo/demos/data/apple-red.png
new file mode 100644
index 00000000..b0a24e94
--- /dev/null
+++ b/examples/demo/demos/data/apple-red.png
Binary files differ
diff --git a/examples/demo/demos/data/background.jpg b/examples/demo/demos/data/background.jpg
new file mode 100644
index 00000000..86c006aa
--- /dev/null
+++ b/examples/demo/demos/data/background.jpg
Binary files differ
diff --git a/examples/demo/demos/data/brick.png b/examples/demo/demos/data/brick.png
new file mode 100644
index 00000000..d413cd28
--- /dev/null
+++ b/examples/demo/demos/data/brick.png
Binary files differ
diff --git a/examples/demo/demos/data/brick2.png b/examples/demo/demos/data/brick2.png
new file mode 100644
index 00000000..cfcd079f
--- /dev/null
+++ b/examples/demo/demos/data/brick2.png
Binary files differ
diff --git a/examples/demo/demos/data/css_accordion.css b/examples/demo/demos/data/css_accordion.css
new file mode 100644
index 00000000..a2434276
--- /dev/null
+++ b/examples/demo/demos/data/css_accordion.css
@@ -0,0 +1,52 @@
+@import url("resource://css_accordion/reset.css");
+
+* {
+ transition-property: color, background-color, border-color, background-image, padding, border-width;
+ transition-duration: 1s;
+
+ font: Sans 20px;
+}
+
+GtkWindow {
+ background: linear-gradient(153deg, #151515, #151515 5px, transparent 5px) 0 0,
+ linear-gradient(333deg, #151515, #151515 5px, transparent 5px) 10px 5px,
+ linear-gradient(153deg, #222, #222 5px, transparent 5px) 0 5px,
+ linear-gradient(333deg, #222, #222 5px, transparent 5px) 10px 10px,
+ linear-gradient(90deg, #1b1b1b, #1b1b1b 10px, transparent 10px),
+ linear-gradient(#1d1d1d, #1d1d1d 25%, #1a1a1a 25%, #1a1a1a 50%, transparent 50%, transparent 75%, #242424 75%, #242424);
+ background-color: #131313;
+ background-size: 20px 20px;
+}
+
+.button {
+ color: black;
+ background-color: #bbb;
+ border-style: solid;
+ border-width: 2px 0 2px 2px;
+ border-color: #333;
+
+ padding: 12px 4px;
+}
+
+.button:first-child {
+ border-radius: 5px 0 0 5px;
+}
+
+.button:last-child {
+ border-radius: 0 5px 5px 0;
+ border-width: 2px;
+}
+
+.button:hover {
+ padding: 12px 48px;
+ background-color: #4870bc;
+}
+
+.button *:hover {
+ color: white;
+}
+
+.button:hover:active,
+.button:active {
+ background-color: #993401;
+}
diff --git a/examples/demo/demos/data/css_basics.css b/examples/demo/demos/data/css_basics.css
new file mode 100644
index 00000000..62dba7a3
--- /dev/null
+++ b/examples/demo/demos/data/css_basics.css
@@ -0,0 +1,22 @@
+/* You can edit the text in this window to change the
+ * appearance of this Window.
+ * Be careful, if you screw it up, nothing might be visible
+ * anymore. :)
+ */
+
+/* This CSS resets all properties to their defaults values
+ * and overrides all user settings and the theme in use */
+@import url("resource://css_basics/reset.css");
+
+/* Set a very futuristic style by default */
+* {
+ color: green;
+ font-family: Monospace;
+ border: 1px solid;
+}
+
+/* Make sure selections are visible */
+:selected {
+ background-color: darkGreen;
+ color: black;
+}
diff --git a/examples/demo/demos/data/css_multiplebgs.css b/examples/demo/demos/data/css_multiplebgs.css
new file mode 100644
index 00000000..eb9d4d60
--- /dev/null
+++ b/examples/demo/demos/data/css_multiplebgs.css
@@ -0,0 +1,136 @@
+/* You can edit the text in this window to change the
+ * appearance of this Window.
+ * Be careful, if you screw it up, nothing might be visible
+ * anymore. :)
+ */
+
+/* This CSS resets all properties to their defaults values
+ * and overrides all user settings and the theme in use */
+@import url("resource://css_multiplebgs/reset.css");
+@import url("resource://css_multiplebgs/cssview.css");
+
+#canvas {
+ transition-property: background-color, background-image;
+ transition-duration: 0.5s;
+
+ background-color: #4870bc;
+}
+
+/* The gradients below are adapted versions of Lea Verou's CSS3 patterns,
+ * licensed under the MIT license:
+ * Copyright (c) 2011 Lea Verou, http://lea.verou.me/
+ *
+ * See https://github.com/LeaVerou/CSS3-Patterns-Gallery
+ */
+
+/**********
+ * Bricks *
+ **********/
+/*
+@define-color brick_hi #d42;
+@define-color brick_lo #b42;
+@define-color brick_hi_backdrop #888;
+@define-color brick_lo_backdrop #999;
+
+#canvas {
+ background-color: #999;
+ background-image: linear-gradient(205deg, @brick_lo, @brick_lo 23px, transparent 23px),
+ linear-gradient(25deg, @brick_hi, @brick_hi 23px, transparent 23px),
+ linear-gradient(205deg, @brick_lo, @brick_lo 23px, transparent 23px),
+ linear-gradient(25deg, @brick_hi, @brick_hi 23px, transparent 23px);
+ background-size: 58px 58px;
+ background-position: 0px 6px, 4px 31px, 29px 35px, 34px 2px;
+}
+
+#canvas:backdrop {
+ background-color: #444;
+ background-image: linear-gradient(205deg, @brick_lo_backdrop, @brick_lo_backdrop 23px, transparent 23px),
+ linear-gradient(25deg, @brick_hi_backdrop, @brick_hi_backdrop 23px, transparent 23px),
+ linear-gradient(205deg, @brick_lo_backdrop, @brick_lo_backdrop 23px, transparent 23px),
+ linear-gradient(25deg, @brick_hi_backdrop, @brick_hi_backdrop 23px, transparent 23px);
+ background-size: 58px 58px;
+ background-position: 0px 6px, 4px 31px, 29px 35px, 34px 2px;
+}
+*/
+
+/*
+#bricks-button {
+ background-color: #eef;
+ background-image: -gtk-scaled(url('resource://css_multiplebgs/brick.png'),url('resource://css_multiplebgs/brick2.png'));
+ background-repeat: no-repeat;
+ background-position: center;
+}
+
+*/
+/**********
+ * Tartan *
+ **********/
+/*
+@define-color tartan_bg #662e2c;
+@define-color tartan_bg_backdrop #333;
+
+#canvas {
+ background-color: @tartan_bg;
+ background-image: repeating-linear-gradient(transparent, transparent 50px, rgba(0,0,0,.4) 50px,
+ rgba(0,0,0,.4) 53px, transparent 53px, transparent 63px,
+ rgba(0,0,0,.4) 63px, rgba(0,0,0,.4) 66px, transparent 66px,
+ transparent 116px, rgba(0,0,0,.5) 116px, rgba(0,0,0,.5) 166px,
+ rgba(255,255,255,.2) 166px, rgba(255,255,255,.2) 169px, rgba(0,0,0,.5) 169px,
+ rgba(0,0,0,.5) 179px, rgba(255,255,255,.2) 179px, rgba(255,255,255,.2) 182px,
+ rgba(0,0,0,.5) 182px, rgba(0,0,0,.5) 232px, transparent 232px),
+ repeating-linear-gradient(90deg, transparent, transparent 50px, rgba(0,0,0,.4) 50px, rgba(0,0,0,.4) 53px,
+ transparent 53px, transparent 63px, rgba(0,0,0,.4) 63px, rgba(0,0,0,.4) 66px,
+ transparent 66px, transparent 116px, rgba(0,0,0,.5) 116px, rgba(0,0,0,.5) 166px,
+ rgba(255,255,255,.2) 166px, rgba(255,255,255,.2) 169px, rgba(0,0,0,.5) 169px,
+ rgba(0,0,0,.5) 179px, rgba(255,255,255,.2) 179px, rgba(255,255,255,.2) 182px,
+ rgba(0,0,0,.5) 182px, rgba(0,0,0,.5) 232px, transparent 232px),
+ repeating-linear-gradient(-55deg, transparent, transparent 1px, rgba(0,0,0,.2) 1px, rgba(0,0,0,.2) 4px,
+ transparent 4px, transparent 19px, rgba(0,0,0,.2) 19px,
+ rgba(0,0,0,.2) 24px, transparent 24px, transparent 51px, rgba(0,0,0,.2) 51px,
+ rgba(0,0,0,.2) 54px, transparent 54px, transparent 74px);
+}
+
+#canvas:backdrop {
+ background-color: @tartan_bg_backdrop;
+}
+*/
+
+/***********
+ * Stripes *
+ ***********/
+
+/*
+@define-color base_bg #4870bc;
+@define-color backdrop_bg #555;
+
+#canvas {
+ background-color: @base_bg;
+ background-image: linear-gradient(to left, transparent, rgba(255,255,255,.07) 50%, transparent 50%),
+ linear-gradient(to left, transparent, rgba(255,255,255,.13) 50%, transparent 50%),
+ linear-gradient(to left, transparent, transparent 50%, rgba(255,255,255,.17) 50%),
+ linear-gradient(to left, transparent, transparent 50%, rgba(255,255,255,.19) 50%);
+ background-size: 29px, 59px, 73px, 109px;
+}
+
+#canvas:backdrop {
+ background-color: @backdrop_bg;
+}
+*/
+
+/***************
+ * Lined Paper *
+ ***************/
+/*
+#canvas {
+ background-color: #fff;
+ background-image: linear-gradient(90deg, transparent 79px, alpha(#f98195, 0.40) 79px, #f98195 80px, alpha(#f98195, 0.40) 81px, transparent 81px),
+ linear-gradient(alpha(#77c5cf, 0.60), alpha(#77c5cf, 0.60) 1px, transparent 1px);
+ background-size: 100% 36px;
+}
+
+#canvas:backdrop {
+ background-color: #f1f2f4;
+ background-image: linear-gradient(90deg, transparent 79px, alpha(#999, 0.40) 79px, #999 80px, alpha(#999, 0.40) 81px, transparent 81px),
+ linear-gradient(alpha(#bbb, 0.60), alpha(#bbb, 0.60) 1px, transparent 1px);
+}
+*/
diff --git a/examples/demo/demos/data/cssview.css b/examples/demo/demos/data/cssview.css
new file mode 100644
index 00000000..5060c396
--- /dev/null
+++ b/examples/demo/demos/data/cssview.css
@@ -0,0 +1,41 @@
+/* Make the text editor has a nice style */
+.view {
+ color: #2e3436;
+ font: Monospace;
+ background-color: alpha(white, 0.30);
+}
+
+.view:selected {
+ color: white;
+ background-color: #4a90d9;
+}
+
+.scrollbar.trough,
+.scrollbars-junction {
+ background-color: alpha(white, 0.80);
+}
+
+.scrollbar.slider {
+ border-width: 3px;
+ border-style: solid;
+ border-radius: 10px;
+ border-color: transparent;
+ background-clip: padding-box;
+ background-color: #999;
+}
+
+.scrollbar.slider:prelight {
+ background-color: #555;
+}
+
+.pane-separator {
+ background-color: alpha(white, 0.80);
+ background-image: linear-gradient(transparent, transparent 1px, #999 1px, #999 4px, transparent 4px);
+ background-size: 40px auto;
+ background-repeat: no-repeat;
+ background-position: center;
+}
+
+.pane-separator:prelight {
+ background-image: linear-gradient(transparent, transparent 1px, #555 1px, #555 4px, transparent 4px);
+}
diff --git a/examples/demo/demos/data/demo.gresource b/examples/demo/demos/data/demo.gresource
new file mode 100644
index 00000000..e19d822d
--- /dev/null
+++ b/examples/demo/demos/data/demo.gresource
Binary files differ
diff --git a/examples/demo/demos/data/demo.gresource.xml b/examples/demo/demos/data/demo.gresource.xml
new file mode 100644
index 00000000..866769f5
--- /dev/null
+++ b/examples/demo/demos/data/demo.gresource.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+ <gresource prefix="/css_accordion">
+ <file>css_accordion.css</file>
+ <file>reset.css</file>
+ </gresource>
+ <gresource prefix="/css_basics">
+ <file>css_basics.css</file>
+ <file>reset.css</file>
+ </gresource>
+ <gresource prefix="/css_multiplebgs">
+ <file>css_multiplebgs.css</file>
+ <file>brick.png</file>
+ <file>brick2.png</file>
+ <file>cssview.css</file>
+ <file>reset.css</file>
+ </gresource>
+</gresources>
diff --git a/examples/demo/demos/data/demo.ui b/examples/demo/demos/data/demo.ui
new file mode 100644
index 00000000..57dd2325
--- /dev/null
+++ b/examples/demo/demos/data/demo.ui
@@ -0,0 +1,258 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<interface domain="gtk20">
+ <object class="GtkListStore" id="liststore1">
+ <columns>
+ <column type="gchararray"/>
+ <column type="gchararray"/>
+ <column type="gint"/>
+ <column type="gchararray"/>
+ </columns>
+ <data>
+ <row>
+ <col id="0" translatable="yes">John</col>
+ <col id="1" translatable="yes">Doe</col>
+ <col id="2">25</col>
+ <col id="3" translatable="yes">This is the John Doe row</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">Mary</col>
+ <col id="1" translatable="yes">Unknown</col>
+ <col id="2">50</col>
+ <col id="3" translatable="yes">This is the Mary Unknown row</col>
+ </row>
+ </data>
+ </object>
+ <object class="GtkUIManager" id="uimanager">
+ <child>
+ <object class="GtkActionGroup" id="DefaultActions">
+ <child>
+ <object class="GtkAction" id="Copy">
+ <property name="name">Copy</property>
+ <property name="tooltip" translatable="yes">Copy selected object into the clipboard</property>
+ <property name="stock_id">gtk-copy</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkAction" id="Cut">
+ <property name="name">Cut</property>
+ <property name="tooltip" translatable="yes">Cut selected object into the clipboard</property>
+ <property name="stock_id">gtk-cut</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkAction" id="EditMenu">
+ <property name="name">EditMenu</property>
+ <property name="label" translatable="yes">_Edit</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkAction" id="FileMenu">
+ <property name="name">FileMenu</property>
+ <property name="label" translatable="yes">_File</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkAction" id="New">
+ <property name="name">New</property>
+ <property name="tooltip" translatable="yes">Create a new file</property>
+ <property name="stock_id">gtk-new</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkAction" id="Open">
+ <property name="name">Open</property>
+ <property name="tooltip" translatable="yes">Open a file</property>
+ <property name="stock_id">gtk-open</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkAction" id="Paste">
+ <property name="name">Paste</property>
+ <property name="tooltip" translatable="yes">Paste object from the Clipboard</property>
+ <property name="stock_id">gtk-paste</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkAction" id="Quit">
+ <property name="name">Quit</property>
+ <property name="tooltip" translatable="yes">Quit the program</property>
+ <property name="stock_id">gtk-quit</property>
+ <signal handler="quit_activate" name="activate"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkAction" id="Save">
+ <property name="name">Save</property>
+ <property name="is_important">True</property>
+ <property name="tooltip" translatable="yes">Save a file</property>
+ <property name="stock_id">gtk-save</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkAction" id="SaveAs">
+ <property name="name">SaveAs</property>
+ <property name="tooltip" translatable="yes">Save with a different name</property>
+ <property name="stock_id">gtk-save-as</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkAction" id="HelpMenu">
+ <property name="name">HelpMenu</property>
+ <property name="label" translatable="yes">_Help</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkAction" id="About">
+ <property name="name">About</property>
+ <property name="stock_id">gtk-about</property>
+ <signal handler="about_activate" name="activate"/>
+ </object>
+ <accelerator key="F1"/>
+ </child>
+ </object>
+ </child>
+ <ui>
+ <menubar name="menubar1">
+ <menu action="FileMenu" name="FileMenu">
+ <menuitem action="New" name="New"/>
+ <menuitem action="Open" name="Open"/>
+ <menuitem action="Save" name="Save"/>
+ <menuitem action="SaveAs" name="SaveAs"/>
+ <separator/>
+ <menuitem action="Quit" name="Quit"/>
+ </menu>
+ <menu action="EditMenu">
+ <menuitem action="Copy" name="Copy"/>
+ <menuitem action="Cut" name="Cut"/>
+ <menuitem action="Paste" name="Paste"/>
+ </menu>
+ <menu action="HelpMenu" name="HelpMenu">
+ <menuitem action="About" name="About"/>
+ </menu>
+ </menubar>
+ <toolbar name="toolbar1">
+ <toolitem action="New" name="New"/>
+ <toolitem action="Open" name="Open"/>
+ <toolitem action="Save" name="Save"/>
+ <separator/>
+ <toolitem action="Copy" name="Copy"/>
+ <toolitem action="Cut" name="Cut"/>
+ <toolitem action="Paste" name="Paste"/>
+ </toolbar>
+ </ui>
+ </object>
+ <object class="GtkAboutDialog" id="aboutdialog1">
+ <property name="program-name" translatable="yes">GtkBuilder demo</property>
+ <accessibility>
+ <relation target="window1" type="subwindow-of"/>
+ </accessibility>
+ </object>
+ <object class="GtkWindow" id="window1">
+ <property name="default_height">250</property>
+ <property name="default_width">440</property>
+ <property name="title">GtkBuilder demo</property>
+ <child>
+ <object class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <child>
+ <object constructor="uimanager" class="GtkMenuBar" id="menubar1">
+ <property name="visible">True</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="a11y-menubar">
+ <property name="AtkObject::accessible-name">The menubar</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <object constructor="uimanager" class="GtkToolbar" id="toolbar1">
+ <property name="visible">True</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="a11y-toolbar">
+ <property name="AtkObject::accessible-name">The toolbar</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow1">
+ <property name="hscrollbar_policy">automatic</property>
+ <property name="shadow_type">in</property>
+ <property name="visible">True</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <child>
+ <object class="GtkTreeView" id="treeview1">
+ <property name="visible">True</property>
+ <property name="model">liststore1</property>
+ <property name="tooltip-column">3</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="a11y-treeview">
+ <property name="AtkObject::accessible-name">Name list</property>
+ <property name="AtkObject::accessible-description">
+ A list of person with name, surname and age columns
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="column1">
+ <property name="title">Name</property>
+ <child>
+ <object class="GtkCellRendererText" id="renderer1"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="column2">
+ <property name="title">Surname</property>
+ <child>
+ <object class="GtkCellRendererText" id="renderer2"/>
+ <attributes>
+ <attribute name="text">1</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="column3">
+ <property name="title">Age</property>
+ <child>
+ <object class="GtkCellRendererText" id="renderer3"/>
+ <attributes>
+ <attribute name="text">2</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <accessibility>
+ <action action_name="move-cursor" description="Move the cursor to select another person."/>
+ </accessibility>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkStatusbar" id="statusbar1">
+ <property name="visible">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/examples/demo/demos/data/floppybuddy.gif b/examples/demo/demos/data/floppybuddy.gif
new file mode 100644
index 00000000..ac986c8e
--- /dev/null
+++ b/examples/demo/demos/data/floppybuddy.gif
Binary files differ
diff --git a/examples/demo/demos/data/gnome-applets.png b/examples/demo/demos/data/gnome-applets.png
new file mode 100644
index 00000000..8d3549e9
--- /dev/null
+++ b/examples/demo/demos/data/gnome-applets.png
Binary files differ
diff --git a/examples/demo/demos/data/gnome-calendar.png b/examples/demo/demos/data/gnome-calendar.png
new file mode 100644
index 00000000..889f329a
--- /dev/null
+++ b/examples/demo/demos/data/gnome-calendar.png
Binary files differ
diff --git a/examples/demo/demos/data/gnome-foot.png b/examples/demo/demos/data/gnome-foot.png
new file mode 100644
index 00000000..04766585
--- /dev/null
+++ b/examples/demo/demos/data/gnome-foot.png
Binary files differ
diff --git a/examples/demo/demos/data/gnome-fs-directory.png b/examples/demo/demos/data/gnome-fs-directory.png
new file mode 100644
index 00000000..05921a66
--- /dev/null
+++ b/examples/demo/demos/data/gnome-fs-directory.png
Binary files differ
diff --git a/examples/demo/demos/data/gnome-fs-regular.png b/examples/demo/demos/data/gnome-fs-regular.png
new file mode 100644
index 00000000..0f5019c8
--- /dev/null
+++ b/examples/demo/demos/data/gnome-fs-regular.png
Binary files differ
diff --git a/examples/demo/demos/data/gnome-gimp.png b/examples/demo/demos/data/gnome-gimp.png
new file mode 100644
index 00000000..f6bbc6d3
--- /dev/null
+++ b/examples/demo/demos/data/gnome-gimp.png
Binary files differ
diff --git a/examples/demo/demos/data/gnome-gmush.png b/examples/demo/demos/data/gnome-gmush.png
new file mode 100644
index 00000000..0a4b0d04
--- /dev/null
+++ b/examples/demo/demos/data/gnome-gmush.png
Binary files differ
diff --git a/examples/demo/demos/data/gnome-gsame.png b/examples/demo/demos/data/gnome-gsame.png
new file mode 100644
index 00000000..01c06115
--- /dev/null
+++ b/examples/demo/demos/data/gnome-gsame.png
Binary files differ
diff --git a/examples/demo/demos/data/gnu-keys.png b/examples/demo/demos/data/gnu-keys.png
new file mode 100644
index 00000000..58a33770
--- /dev/null
+++ b/examples/demo/demos/data/gnu-keys.png
Binary files differ
diff --git a/examples/demo/demos/data/gtk-logo-rgb.gif b/examples/demo/demos/data/gtk-logo-rgb.gif
new file mode 100644
index 00000000..63c622b9
--- /dev/null
+++ b/examples/demo/demos/data/gtk-logo-rgb.gif
Binary files differ
diff --git a/examples/demo/demos/data/reset.css b/examples/demo/demos/data/reset.css
new file mode 100644
index 00000000..1c27a8e7
--- /dev/null
+++ b/examples/demo/demos/data/reset.css
@@ -0,0 +1,68 @@
+/* @import this colorsheet to get the default values for every property.
+ * This is useful when writing special CSS tests that should not be
+ * inluenced by themes - not even the default ones.
+ * Keep in mind that the output will be very ugly and not look like
+ * anything GTK.
+ * Also, when adding new style properties, please add them here.
+ */
+
+* {
+ color: inherit;
+ font-size: inherit;
+ background-color: initial;
+ font-family: inherit;
+ font-style: inherit;
+ font-variant: inherit;
+ font-weight: inherit;
+ text-shadow: inherit;
+ icon-shadow: inherit;
+ box-shadow: initial;
+ margin-top: initial;
+ margin-left: initial;
+ margin-bottom: initial;
+ margin-right: initial;
+ padding-top: initial;
+ padding-left: initial;
+ padding-bottom: initial;
+ padding-right: initial;
+ border-top-style: initial;
+ border-top-width: initial;
+ border-left-style: initial;
+ border-left-width: initial;
+ border-bottom-style: initial;
+ border-bottom-width: initial;
+ border-right-style: initial;
+ border-right-width: initial;
+ border-top-left-radius: initial;
+ border-top-right-radius: initial;
+ border-bottom-right-radius: initial;
+ border-bottom-left-radius: initial;
+ outline-style: initial;
+ outline-width: initial;
+ outline-offset: initial;
+ background-clip: initial;
+ background-origin: initial;
+ background-size: initial;
+ background-position: initial;
+ border-top-color: initial;
+ border-right-color: initial;
+ border-bottom-color: initial;
+ border-left-color: initial;
+ outline-color: initial;
+ background-repeat: initial;
+ background-image: initial;
+ border-image-source: initial;
+ border-image-repeat: initial;
+ border-image-slice: initial;
+ border-image-width: initial;
+ transition-property: initial;
+ transition-duration: initial;
+ transition-timing-function: initial;
+ transition-delay: initial;
+ engine: initial;
+ gtk-key-bindings: initial;
+
+ -GtkWidget-focus-line-width: 0;
+ -GtkWidget-focus-padding: 0;
+ -GtkNotebook-initial-gap: 0;
+}
diff --git a/examples/demo/demos/dialogs.py b/examples/demo/demos/dialogs.py
new file mode 100644
index 00000000..4c3e34ad
--- /dev/null
+++ b/examples/demo/demos/dialogs.py
@@ -0,0 +1,155 @@
+#!/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
+
+
+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(title='Interactive Dialog',
+ transient_for=self.window,
+ modal=True,
+ destroy_with_parent=True)
+ dialog.add_buttons(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(n_rows=2, n_columns=2, homogeneous=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(transient_for=self.window,
+ modal=True,
+ destroy_with_parent=True,
+ message_type=Gtk.MessageType.INFO,
+ buttons=Gtk.ButtonsType.OK,
+ text="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):
+ DialogsApp()
+ Gtk.main()
+
+if __name__ == '__main__':
+ main()
diff --git a/examples/demo/demos/drawingarea.py b/examples/demo/demos/drawingarea.py
new file mode 100644
index 00000000..aea18b57
--- /dev/null
+++ b/examples/demo/demos/drawingarea.py
@@ -0,0 +1,206 @@
+#!/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 = "Drawing Area"
+description = """
+GtkDrawingArea is a blank area where you can draw custom displays
+of various kinds.
+
+This demo has two drawing areas. The checkerboard area shows
+how you can just draw something; all you have to do is write
+a signal handler for expose_event, as shown here.
+
+The "scribble" area is a bit more advanced, and shows how to handle
+events such as button presses and mouse motion. Click the mouse
+and drag in the scribble area to draw squiggles. Resize the window
+to clear the area.
+"""
+
+
+import cairo
+
+from gi.repository import Gtk, Gdk
+
+
+class DrawingAreaApp:
+ def __init__(self):
+ self.sureface = None
+
+ window = Gtk.Window()
+ window.set_title(title)
+ window.connect('destroy', lambda x: Gtk.main_quit())
+ window.set_border_width(8)
+
+ vbox = Gtk.VBox(homogeneous=False, spacing=8)
+ window.add(vbox)
+
+ # create checkerboard area
+ label = Gtk.Label()
+ label.set_markup('<u>Checkerboard pattern</u>')
+ vbox.pack_start(label, False, False, 0)
+
+ frame = Gtk.Frame()
+ frame.set_shadow_type(Gtk.ShadowType.IN)
+ vbox.pack_start(frame, True, True, 0)
+
+ da = Gtk.DrawingArea()
+ da.set_size_request(100, 100)
+ frame.add(da)
+ da.connect('draw', self.checkerboard_draw_event)
+
+ # create scribble area
+ label = Gtk.Label()
+ label.set_markup('<u>Scribble area</u>')
+ vbox.pack_start(label, False, False, 0)
+
+ frame = Gtk.Frame()
+ frame.set_shadow_type(Gtk.ShadowType.IN)
+ vbox.pack_start(frame, True, True, 0)
+
+ da = Gtk.DrawingArea()
+ da.set_size_request(100, 100)
+ frame.add(da)
+ da.connect('draw', self.scribble_draw_event)
+ da.connect('configure-event', self.scribble_configure_event)
+
+ # event signals
+ da.connect('motion-notify-event', self.scribble_motion_notify_event)
+ da.connect('button-press-event', self.scribble_button_press_event)
+
+ # Ask to receive events the drawing area doesn't normally
+ # subscribe to
+ da.set_events(da.get_events()
+ | Gdk.EventMask.LEAVE_NOTIFY_MASK
+ | Gdk.EventMask.BUTTON_PRESS_MASK
+ | Gdk.EventMask.POINTER_MOTION_MASK
+ | Gdk.EventMask.POINTER_MOTION_HINT_MASK)
+
+ window.show_all()
+
+ def checkerboard_draw_event(self, da, cairo_ctx):
+
+ # 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.
+ check_size = 10
+ spacing = 2
+
+ xcount = 0
+ i = spacing
+ width = da.get_allocated_width()
+ height = da.get_allocated_height()
+
+ while i < width:
+ j = spacing
+ ycount = xcount % 2 # start with even/odd depending on row
+ while j < height:
+ if ycount % 2:
+ cairo_ctx.set_source_rgb(0.45777, 0, 0.45777)
+ else:
+ 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
+
+ i += check_size + spacing
+ xcount += 1
+
+ return True
+
+ def scribble_draw_event(self, da, cairo_ctx):
+
+ 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()
+ 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):
+
+ 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.surface is 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.
+ # If you don't call gdk_window_get_pointer() you'll only get
+ # a single motion event. The reason is that we specified
+ # GDK_POINTER_MOTION_HINT_MASK to gtk_widget_set_events().
+ # If we hadn't specified that, we could just use event->x, event->y
+ # as the pointer location. But we'd also get deluged in events.
+ # By requesting the next event as we handle the current one,
+ # we avoid getting a huge number of events faster than we
+ # can cope.
+
+ (window, x, y, state) = event.window.get_pointer()
+
+ if state & Gdk.ModifierType.BUTTON1_MASK:
+ self.draw_brush(da, x, y)
+
+ return True
+
+ def scribble_button_press_event(self, da, event):
+ if self.surface is None: # paranoia check, in case we haven't gotten a configure event
+ return False
+
+ if event.button == 1:
+ self.draw_brush(da, event.x, event.y)
+
+ return True
+
+
+def main(demoapp=None):
+ DrawingAreaApp()
+ Gtk.main()
+
+if __name__ == '__main__':
+ main()
diff --git a/examples/demo/demos/expander.py b/examples/demo/demos/expander.py
new file mode 100644
index 00000000..eb5149fa
--- /dev/null
+++ b/examples/demo/demos/expander.py
@@ -0,0 +1,61 @@
+#!/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
+
+
+class ExpanderApp:
+ def __init__(self):
+ self.window = Gtk.Dialog(title="GtkExpander")
+ self.window.add_buttons(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(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(label='Details can be shown or hidden')
+ expander.add(label)
+
+ self.window.show_all()
+
+
+def main(demoapp=None):
+ ExpanderApp()
+ Gtk.main()
+
+if __name__ == '__main__':
+ main()
diff --git a/examples/demo/demos/flowbox.py b/examples/demo/demos/flowbox.py
new file mode 100755
index 00000000..f28a7334
--- /dev/null
+++ b/examples/demo/demos/flowbox.py
@@ -0,0 +1,750 @@
+#!/usr/bin/env python
+# -*- Mode: Python; py-indent-offset: 4 -*-
+# vim: tabstop=4 shiftwidth=4 expandtab
+#
+# Copyright (C) 2014 Gian Mario Tagliaretti <gianmt@gnome.org>
+#
+# 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 = "FlowBox"
+description = """
+A FlowBox allows flexible and responsive grids which reflow as needed and
+support sorting and filtering. The children of a GtkFlowBox are regular widgets.
+"""
+
+from gi.repository import Gtk, Gdk
+
+
+class FlowBoxApp:
+ def __init__(self):
+ window = Gtk.Window()
+ window.connect('destroy', lambda x: Gtk.main_quit())
+ window.set_border_width(10)
+ window.set_default_size(600, 400)
+
+ header = Gtk.HeaderBar(title="Flow Box")
+ header.set_subtitle("Sample FlowBox app")
+ header.props.show_close_button = True
+
+ window.set_titlebar(header)
+
+ scrolled = Gtk.ScrolledWindow()
+ scrolled.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
+
+ flowbox = Gtk.FlowBox()
+ flowbox.set_valign(Gtk.Align.START)
+ flowbox.set_max_children_per_line(30)
+ flowbox.set_selection_mode(Gtk.SelectionMode.NONE)
+
+ self.create_flowbox(flowbox)
+
+ scrolled.add(flowbox)
+
+ window.add(scrolled)
+ window.show_all()
+
+ def color_swatch_new(self, str_color):
+ rgba = Gdk.RGBA()
+ rgba.parse(str_color)
+
+ text_rgba = Gdk.RGBA() # default is white
+ if max(rgba.red, rgba.green, rgba.blue) > 0.6:
+ text_rgba.parse('black')
+
+ label = Gtk.Label(label=str_color)
+ label.override_background_color(0, rgba)
+ label.override_color(0, text_rgba)
+ return label
+
+ def create_flowbox(self, flowbox):
+ colors = [
+ 'AliceBlue',
+ 'AntiqueWhite',
+ 'AntiqueWhite1',
+ 'AntiqueWhite2',
+ 'AntiqueWhite3',
+ 'AntiqueWhite4',
+ 'aqua',
+ 'aquamarine',
+ 'aquamarine1',
+ 'aquamarine2',
+ 'aquamarine3',
+ 'aquamarine4',
+ 'azure',
+ 'azure1',
+ 'azure2',
+ 'azure3',
+ 'azure4',
+ 'beige',
+ 'bisque',
+ 'bisque1',
+ 'bisque2',
+ 'bisque3',
+ 'bisque4',
+ 'black',
+ 'BlanchedAlmond',
+ 'blue',
+ 'blue1',
+ 'blue2',
+ 'blue3',
+ 'blue4',
+ 'BlueViolet',
+ 'brown',
+ 'brown1',
+ 'brown2',
+ 'brown3',
+ 'brown4',
+ 'burlywood',
+ 'burlywood1',
+ 'burlywood2',
+ 'burlywood3',
+ 'burlywood4',
+ 'CadetBlue',
+ 'CadetBlue1',
+ 'CadetBlue2',
+ 'CadetBlue3',
+ 'CadetBlue4',
+ 'chartreuse',
+ 'chartreuse1',
+ 'chartreuse2',
+ 'chartreuse3',
+ 'chartreuse4',
+ 'chocolate',
+ 'chocolate1',
+ 'chocolate2',
+ 'chocolate3',
+ 'chocolate4',
+ 'coral',
+ 'coral1',
+ 'coral2',
+ 'coral3',
+ 'coral4',
+ 'CornflowerBlue',
+ 'cornsilk',
+ 'cornsilk1',
+ 'cornsilk2',
+ 'cornsilk3',
+ 'cornsilk4',
+ 'crimson',
+ 'cyan',
+ 'cyan1',
+ 'cyan2',
+ 'cyan3',
+ 'cyan4',
+ 'DarkBlue',
+ 'DarkCyan',
+ 'DarkGoldenrod',
+ 'DarkGoldenrod1',
+ 'DarkGoldenrod2',
+ 'DarkGoldenrod3',
+ 'DarkGoldenrod4',
+ 'DarkGray',
+ 'DarkGreen',
+ 'DarkGrey',
+ 'DarkKhaki',
+ 'DarkMagenta',
+ 'DarkOliveGreen',
+ 'DarkOliveGreen1',
+ 'DarkOliveGreen2',
+ 'DarkOliveGreen3',
+ 'DarkOliveGreen4',
+ 'DarkOrange',
+ 'DarkOrange1',
+ 'DarkOrange2',
+ 'DarkOrange3',
+ 'DarkOrange4',
+ 'DarkOrchid',
+ 'DarkOrchid1',
+ 'DarkOrchid2',
+ 'DarkOrchid3',
+ 'DarkOrchid4',
+ 'DarkRed',
+ 'DarkSalmon',
+ 'DarkSeaGreen',
+ 'DarkSeaGreen1',
+ 'DarkSeaGreen2',
+ 'DarkSeaGreen3',
+ 'DarkSeaGreen4',
+ 'DarkSlateBlue',
+ 'DarkSlateGray',
+ 'DarkSlateGray1',
+ 'DarkSlateGray2',
+ 'DarkSlateGray3',
+ 'DarkSlateGray4',
+ 'DarkSlateGrey',
+ 'DarkTurquoise',
+ 'DarkViolet',
+ 'DeepPink',
+ 'DeepPink1',
+ 'DeepPink2',
+ 'DeepPink3',
+ 'DeepPink4',
+ 'DeepSkyBlue',
+ 'DeepSkyBlue1',
+ 'DeepSkyBlue2',
+ 'DeepSkyBlue3',
+ 'DeepSkyBlue4',
+ 'DimGray',
+ 'DimGrey',
+ 'DodgerBlue',
+ 'DodgerBlue1',
+ 'DodgerBlue2',
+ 'DodgerBlue3',
+ 'DodgerBlue4',
+ 'firebrick',
+ 'firebrick1',
+ 'firebrick2',
+ 'firebrick3',
+ 'firebrick4',
+ 'FloralWhite',
+ 'ForestGreen',
+ 'fuchsia',
+ 'gainsboro',
+ 'GhostWhite',
+ 'gold',
+ 'gold1',
+ 'gold2',
+ 'gold3',
+ 'gold4',
+ 'goldenrod',
+ 'goldenrod1',
+ 'goldenrod2',
+ 'goldenrod3',
+ 'goldenrod4',
+ 'gray',
+ 'gray0',
+ 'gray1',
+ 'gray2',
+ 'gray3',
+ 'gray4',
+ 'gray5',
+ 'gray6',
+ 'gray7',
+ 'gray8',
+ 'gray9',
+ 'gray10',
+ 'gray11',
+ 'gray12',
+ 'gray13',
+ 'gray14',
+ 'gray15',
+ 'gray16',
+ 'gray17',
+ 'gray18',
+ 'gray19',
+ 'gray20',
+ 'gray21',
+ 'gray22',
+ 'gray23',
+ 'gray24',
+ 'gray25',
+ 'gray26',
+ 'gray27',
+ 'gray28',
+ 'gray29',
+ 'gray30',
+ 'gray31',
+ 'gray32',
+ 'gray33',
+ 'gray34',
+ 'gray35',
+ 'gray36',
+ 'gray37',
+ 'gray38',
+ 'gray39',
+ 'gray40',
+ 'gray41',
+ 'gray42',
+ 'gray43',
+ 'gray44',
+ 'gray45',
+ 'gray46',
+ 'gray47',
+ 'gray48',
+ 'gray49',
+ 'gray50',
+ 'gray51',
+ 'gray52',
+ 'gray53',
+ 'gray54',
+ 'gray55',
+ 'gray56',
+ 'gray57',
+ 'gray58',
+ 'gray59',
+ 'gray60',
+ 'gray61',
+ 'gray62',
+ 'gray63',
+ 'gray64',
+ 'gray65',
+ 'gray66',
+ 'gray67',
+ 'gray68',
+ 'gray69',
+ 'gray70',
+ 'gray71',
+ 'gray72',
+ 'gray73',
+ 'gray74',
+ 'gray75',
+ 'gray76',
+ 'gray77',
+ 'gray78',
+ 'gray79',
+ 'gray80',
+ 'gray81',
+ 'gray82',
+ 'gray83',
+ 'gray84',
+ 'gray85',
+ 'gray86',
+ 'gray87',
+ 'gray88',
+ 'gray89',
+ 'gray90',
+ 'gray91',
+ 'gray92',
+ 'gray93',
+ 'gray94',
+ 'gray95',
+ 'gray96',
+ 'gray97',
+ 'gray98',
+ 'gray99',
+ 'gray100',
+ 'green',
+ 'green1',
+ 'green2',
+ 'green3',
+ 'green4',
+ 'GreenYellow',
+ 'grey',
+ 'grey0',
+ 'grey1',
+ 'grey2',
+ 'grey3',
+ 'grey4',
+ 'grey5',
+ 'grey6',
+ 'grey7',
+ 'grey8',
+ 'grey9',
+ 'grey10',
+ 'grey11',
+ 'grey12',
+ 'grey13',
+ 'grey14',
+ 'grey15',
+ 'grey16',
+ 'grey17',
+ 'grey18',
+ 'grey19',
+ 'grey20',
+ 'grey21',
+ 'grey22',
+ 'grey23',
+ 'grey24',
+ 'grey25',
+ 'grey26',
+ 'grey27',
+ 'grey28',
+ 'grey29',
+ 'grey30',
+ 'grey31',
+ 'grey32',
+ 'grey33',
+ 'grey34',
+ 'grey35',
+ 'grey36',
+ 'grey37',
+ 'grey38',
+ 'grey39',
+ 'grey40',
+ 'grey41',
+ 'grey42',
+ 'grey43',
+ 'grey44',
+ 'grey45',
+ 'grey46',
+ 'grey47',
+ 'grey48',
+ 'grey49',
+ 'grey50',
+ 'grey51',
+ 'grey52',
+ 'grey53',
+ 'grey54',
+ 'grey55',
+ 'grey56',
+ 'grey57',
+ 'grey58',
+ 'grey59',
+ 'grey60',
+ 'grey61',
+ 'grey62',
+ 'grey63',
+ 'grey64',
+ 'grey65',
+ 'grey66',
+ 'grey67',
+ 'grey68',
+ 'grey69',
+ 'grey70',
+ 'grey71',
+ 'grey72',
+ 'grey73',
+ 'grey74',
+ 'grey75',
+ 'grey76',
+ 'grey77',
+ 'grey78',
+ 'grey79',
+ 'grey80',
+ 'grey81',
+ 'grey82',
+ 'grey83',
+ 'grey84',
+ 'grey85',
+ 'grey86',
+ 'grey87',
+ 'grey88',
+ 'grey89',
+ 'grey90',
+ 'grey91',
+ 'grey92',
+ 'grey93',
+ 'grey94',
+ 'grey95',
+ 'grey96',
+ 'grey97',
+ 'grey98',
+ 'grey99',
+ 'grey100',
+ 'honeydew',
+ 'honeydew1',
+ 'honeydew2',
+ 'honeydew3',
+ 'honeydew4',
+ 'HotPink',
+ 'HotPink1',
+ 'HotPink2',
+ 'HotPink3',
+ 'HotPink4',
+ 'IndianRed',
+ 'IndianRed1',
+ 'IndianRed2',
+ 'IndianRed3',
+ 'IndianRed4',
+ 'indigo',
+ 'ivory',
+ 'ivory1',
+ 'ivory2',
+ 'ivory3',
+ 'ivory4',
+ 'khaki',
+ 'khaki1',
+ 'khaki2',
+ 'khaki3',
+ 'khaki4',
+ 'lavender',
+ 'LavenderBlush',
+ 'LavenderBlush1',
+ 'LavenderBlush2',
+ 'LavenderBlush3',
+ 'LavenderBlush4',
+ 'LawnGreen',
+ 'LemonChiffon',
+ 'LemonChiffon1',
+ 'LemonChiffon2',
+ 'LemonChiffon3',
+ 'LemonChiffon4',
+ 'LightBlue',
+ 'LightBlue1',
+ 'LightBlue2',
+ 'LightBlue3',
+ 'LightBlue4',
+ 'LightCoral',
+ 'LightCyan',
+ 'LightCyan1',
+ 'LightCyan2',
+ 'LightCyan3',
+ 'LightCyan4',
+ 'LightGoldenrod',
+ 'LightGoldenrod1',
+ 'LightGoldenrod2',
+ 'LightGoldenrod3',
+ 'LightGoldenrod4',
+ 'LightGoldenrodYellow',
+ 'LightGray',
+ 'LightGreen',
+ 'LightGrey',
+ 'LightPink',
+ 'LightPink1',
+ 'LightPink2',
+ 'LightPink3',
+ 'LightPink4',
+ 'LightSalmon',
+ 'LightSalmon1',
+ 'LightSalmon2',
+ 'LightSalmon3',
+ 'LightSalmon4',
+ 'LightSeaGreen',
+ 'LightSkyBlue',
+ 'LightSkyBlue1',
+ 'LightSkyBlue2',
+ 'LightSkyBlue3',
+ 'LightSkyBlue4',
+ 'LightSlateBlue',
+ 'LightSlateGray',
+ 'LightSlateGrey',
+ 'LightSteelBlue',
+ 'LightSteelBlue1',
+ 'LightSteelBlue2',
+ 'LightSteelBlue3',
+ 'LightSteelBlue4',
+ 'LightYellow',
+ 'LightYellow1',
+ 'LightYellow2',
+ 'LightYellow3',
+ 'LightYellow4',
+ 'lime',
+ 'LimeGreen',
+ 'linen',
+ 'magenta',
+ 'magenta1',
+ 'magenta2',
+ 'magenta3',
+ 'magenta4',
+ 'maroon',
+ 'maroon1',
+ 'maroon2',
+ 'maroon3',
+ 'maroon4',
+ 'MediumAquamarine',
+ 'MediumBlue',
+ 'MediumOrchid',
+ 'MediumOrchid1',
+ 'MediumOrchid2',
+ 'MediumOrchid3',
+ 'MediumOrchid4',
+ 'MediumPurple',
+ 'MediumPurple1',
+ 'MediumPurple2',
+ 'MediumPurple3',
+ 'MediumPurple4',
+ 'MediumSeaGreen',
+ 'MediumSlateBlue',
+ 'MediumSpringGreen',
+ 'MediumTurquoise',
+ 'MediumVioletRed',
+ 'MidnightBlue',
+ 'MintCream',
+ 'MistyRose',
+ 'MistyRose1',
+ 'MistyRose2',
+ 'MistyRose3',
+ 'MistyRose4',
+ 'moccasin',
+ 'NavajoWhite',
+ 'NavajoWhite1',
+ 'NavajoWhite2',
+ 'NavajoWhite3',
+ 'NavajoWhite4',
+ 'navy',
+ 'NavyBlue',
+ 'OldLace',
+ 'olive',
+ 'OliveDrab',
+ 'OliveDrab1',
+ 'OliveDrab2',
+ 'OliveDrab3',
+ 'OliveDrab4',
+ 'orange',
+ 'orange1',
+ 'orange2',
+ 'orange3',
+ 'orange4',
+ 'OrangeRed',
+ 'OrangeRed1',
+ 'OrangeRed2',
+ 'OrangeRed3',
+ 'OrangeRed4',
+ 'orchid',
+ 'orchid1',
+ 'orchid2',
+ 'orchid3',
+ 'orchid4',
+ 'PaleGoldenrod',
+ 'PaleGreen',
+ 'PaleGreen1',
+ 'PaleGreen2',
+ 'PaleGreen3',
+ 'PaleGreen4',
+ 'PaleTurquoise',
+ 'PaleTurquoise1',
+ 'PaleTurquoise2',
+ 'PaleTurquoise3',
+ 'PaleTurquoise4',
+ 'PaleVioletRed',
+ 'PaleVioletRed1',
+ 'PaleVioletRed2',
+ 'PaleVioletRed3',
+ 'PaleVioletRed4',
+ 'PapayaWhip',
+ 'PeachPuff',
+ 'PeachPuff1',
+ 'PeachPuff2',
+ 'PeachPuff3',
+ 'PeachPuff4',
+ 'peru',
+ 'pink',
+ 'pink1',
+ 'pink2',
+ 'pink3',
+ 'pink4',
+ 'plum',
+ 'plum1',
+ 'plum2',
+ 'plum3',
+ 'plum4',
+ 'PowderBlue',
+ 'purple',
+ 'purple1',
+ 'purple2',
+ 'purple3',
+ 'purple4',
+ 'red',
+ 'red1',
+ 'red2',
+ 'red3',
+ 'red4',
+ 'RosyBrown',
+ 'RosyBrown1',
+ 'RosyBrown2',
+ 'RosyBrown3',
+ 'RosyBrown4',
+ 'RoyalBlue',
+ 'RoyalBlue1',
+ 'RoyalBlue2',
+ 'RoyalBlue3',
+ 'RoyalBlue4',
+ 'SaddleBrown',
+ 'salmon',
+ 'salmon1',
+ 'salmon2',
+ 'salmon3',
+ 'salmon4',
+ 'SandyBrown',
+ 'SeaGreen',
+ 'SeaGreen1',
+ 'SeaGreen2',
+ 'SeaGreen3',
+ 'SeaGreen4',
+ 'seashell',
+ 'seashell1',
+ 'seashell2',
+ 'seashell3',
+ 'seashell4',
+ 'sienna',
+ 'sienna1',
+ 'sienna2',
+ 'sienna3',
+ 'sienna4',
+ 'silver',
+ 'SkyBlue',
+ 'SkyBlue1',
+ 'SkyBlue2',
+ 'SkyBlue3',
+ 'SkyBlue4',
+ 'SlateBlue',
+ 'SlateBlue1',
+ 'SlateBlue2',
+ 'SlateBlue3',
+ 'SlateBlue4',
+ 'SlateGray',
+ 'SlateGray1',
+ 'SlateGray2',
+ 'SlateGray3',
+ 'SlateGray4',
+ 'SlateGrey',
+ 'snow',
+ 'snow1',
+ 'snow2',
+ 'snow3',
+ 'snow4',
+ 'SpringGreen',
+ 'SpringGreen1',
+ 'SpringGreen2',
+ 'SpringGreen3',
+ 'SpringGreen4',
+ 'SteelBlue',
+ 'SteelBlue1',
+ 'SteelBlue2',
+ 'SteelBlue3',
+ 'SteelBlue4',
+ 'tan',
+ 'tan1',
+ 'tan2',
+ 'tan3',
+ 'tan4',
+ 'teal',
+ 'thistle',
+ 'thistle1',
+ 'thistle2',
+ 'thistle3',
+ 'thistle4',
+ 'tomato',
+ 'tomato1',
+ 'tomato2',
+ 'tomato3',
+ 'tomato4',
+ 'turquoise',
+ 'turquoise1',
+ 'turquoise2',
+ 'turquoise3',
+ 'turquoise4',
+ 'violet',
+ 'VioletRed',
+ 'VioletRed1',
+ 'VioletRed2',
+ 'VioletRed3',
+ 'VioletRed4',
+ 'wheat',
+ 'wheat1',
+ 'wheat2',
+ 'wheat3',
+ 'wheat4',
+ 'white',
+ 'WhiteSmoke',
+ 'yellow',
+ 'yellow1',
+ 'yellow2',
+ 'yellow3',
+ 'yellow4',
+ 'YellowGreen'
+ ]
+
+ for color in colors:
+ button = self.color_swatch_new(color)
+ flowbox.add(button)
+
+def main(demoapp=None):
+ FlowBoxApp()
+ Gtk.main()
+
+if __name__ == '__main__':
+ main()
diff --git a/examples/demo/demos/images.py b/examples/demo/demos/images.py
new file mode 100644
index 00000000..3dac4ccd
--- /dev/null
+++ b/examples/demo/demos/images.py
@@ -0,0 +1,304 @@
+#!/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.
+"""
+
+import os
+from os import path
+
+from gi.repository import Gtk, Gdk, GdkPixbuf, GLib, Gio, GObject
+
+
+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 = os.path.abspath(os.path.dirname(__file__))
+ self.base_path = os.path.join(self.base_path, 'data')
+ filename = os.path.join(self.base_path, 'gtk-logo-rgb.gif')
+ pixbuf = GdkPixbuf.Pixbuf.new_from_file(filename)
+ transparent = pixbuf.add_alpha(True, 0xff, 0xff, 0xff)
+ image = Gtk.Image.new_from_pixbuf(transparent)
+ 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.new_with_mnemonic('_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:
+ GLib.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):
+ ImagesApp()
+ Gtk.main()
+
+if __name__ == '__main__':
+ main()
diff --git a/examples/demo/demos/infobars.py b/examples/demo/demos/infobars.py
new file mode 100644
index 00000000..c36de2b5
--- /dev/null
+++ b/examples/demo/demos/infobars.py
@@ -0,0 +1,102 @@
+#!/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(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(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(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(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(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(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(transient_for=self.window,
+ modal=True,
+ destroy_with_parent=True,
+ message_type=Gtk.MessageType.INFO,
+ buttons=Gtk.ButtonsType.OK,
+ text='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):
+ InfobarApp()
+ Gtk.main()
+
+if __name__ == '__main__':
+ main()
diff --git a/examples/demo/demos/links.py b/examples/demo/demos/links.py
new file mode 100644
index 00000000..e907abdf
--- /dev/null
+++ b/examples/demo/demos/links.py
@@ -0,0 +1,76 @@
+#!/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(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(transient_for=parent,
+ destroy_with_parent=True,
+ message_type=Gtk.MessageType.INFO,
+ buttons=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):
+ LinksApp()
+ Gtk.main()
+
+if __name__ == '__main__':
+ main()
diff --git a/examples/demo/demos/menus.py b/examples/demo/demos/menus.py
new file mode 100644
index 00000000..fbc2daa5
--- /dev/null
+++ b/examples/demo/demos/menus.py
@@ -0,0 +1,133 @@
+#!/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)
+
+ 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(label='Flip')
+ button.connect('clicked', self.change_orientation, menubar)
+ box2.pack_start(button, True, True, 0)
+
+ button = Gtk.Button(label='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()
+
+ 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):
+ MenusApp()
+ Gtk.main()
+
+if __name__ == '__main__':
+ main()
diff --git a/examples/demo/demos/pickers.py b/examples/demo/demos/pickers.py
new file mode 100644
index 00000000..4d5347f9
--- /dev/null
+++ b/examples/demo/demos/pickers.py
@@ -0,0 +1,76 @@
+#!/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(n_rows=4, n_columns=2, homogeneous=False)
+ table.set_col_spacing(0, 10)
+ table.set_row_spacings(3)
+ self.window.add(table)
+ table.set_border_width(10)
+
+ label = Gtk.Label(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(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(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(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):
+ PickersApp()
+ Gtk.main()
+
+if __name__ == '__main__':
+ main()
diff --git a/examples/demo/demos/pixbuf.py b/examples/demo/demos/pixbuf.py
new file mode 100644
index 00000000..4f3329be
--- /dev/null
+++ b/examples/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
+import os
+import math
+
+
+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 = GLib.timeout_add(self.FRAME_DELAY, self.timeout_cb)
+ self.window.show_all()
+
+ def load_pixbufs(self):
+ base_path = os.path.abspath(os.path.dirname(__file__))
+ base_path = os.path.join(base_path, 'data')
+ img_path = os.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 = os.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):
+ GLib.source_remove(self.timeout_id)
+ Gtk.main_quit()
+
+
+def main(demoapp=None):
+ PixbufApp()
+ Gtk.main()
+
+if __name__ == '__main__':
+ main()
diff --git a/examples/demo/demos/printing.py b/examples/demo/demos/printing.py
new file mode 100644
index 00000000..b476e30b
--- /dev/null
+++ b/examples/demo/demos/printing.py
@@ -0,0 +1,179 @@
+#!/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 math
+import os
+
+
+class PrintingApp:
+ HEADER_HEIGHT = 10 * 72 / 25.4
+ HEADER_GAP = 3 * 72 / 25.4
+
+ def __init__(self):
+ self.operation = Gtk.PrintOperation()
+ print_data = {'filename': os.path.abspath(__file__),
+ '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)
+
+ def run(self, parent=None):
+ result = self.operation.run(Gtk.PrintOperationAction.PRINT_DIALOG, parent)
+
+ if result == Gtk.PrintOperationResult.ERROR:
+ message = self.operation.get_error()
+
+ dialog = Gtk.MessageDialog(parent,
+ 0,
+ Gtk.MessageType.ERROR,
+ Gtk.ButtonsType.CLOSE,
+ message)
+
+ dialog.run()
+ dialog.destroy()
+
+ Gtk.main_quit()
+
+ 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 os.path.isfile(file_path):
+ file_path = os.path.join('demos', file_path)
+ if not os.path.isfile:
+ raise Exception("file not found: " % (file_path, ))
+
+ # 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.EllipsizeMode.START)
+ (text_width, text_height) = layout.get_pixel_size()
+
+ 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()
+ GLib.idle_add(app.run, demoapp.window)
+ Gtk.main()
+
+if __name__ == '__main__':
+ main()
diff --git a/examples/demo/demos/rotatedtext.py b/examples/demo/demos/rotatedtext.py
new file mode 100644
index 00000000..232f9fd3
--- /dev/null
+++ b/examples/demo/demos/rotatedtext.py
@@ -0,0 +1,198 @@
+#!/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(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
+
+ # Set fancy shape attributes for all hearts
+ attrs = Pango.AttrList()
+
+ # FIXME: attr_shape_new_with_data isn't introspectable
+ '''
+ ink_rect = logical_rect
+ p = BYTES_TEXT.find(BYTES_HEART)
+ 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):
+ RotatedTextApp()
+ Gtk.main()
+
+if __name__ == '__main__':
+ main()
diff --git a/examples/demo/demos/test.py b/examples/demo/demos/test.py
new file mode 100644
index 00000000..4bd7684c
--- /dev/null
+++ b/examples/demo/demos/test.py
@@ -0,0 +1,16 @@
+title = "Test Demo"
+description = "Dude this is a test"
+
+
+from gi.repository import Gtk
+
+
+def _quit(*args):
+ Gtk.main_quit()
+
+
+def main(demoapp=None):
+ window = Gtk.Window()
+ window.connect_after('destroy', _quit)
+ window.show_all()
+ Gtk.main()