diff options
Diffstat (limited to 'examples')
66 files changed, 6816 insertions, 481 deletions
diff --git a/examples/Makefile.am b/examples/Makefile.am deleted file mode 100644 index af9f3d7..0000000 --- a/examples/Makefile.am +++ /dev/null @@ -1 +0,0 @@ -EXTRA_DIST = properties.py signal.py option.py cairo-demo.py diff --git a/examples/Makefile.in b/examples/Makefile.in deleted file mode 100644 index 0c05343..0000000 --- a/examples/Makefile.in +++ /dev/null @@ -1,474 +0,0 @@ -# Makefile.in generated by automake 1.13.3 from Makefile.am. -# @configure_input@ - -# Copyright (C) 1994-2013 Free Software Foundation, Inc. - -# This Makefile.in is free software; the Free Software Foundation -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY, to the extent permitted by law; without -# even the implied warranty of MERCHANTABILITY or FITNESS FOR A -# PARTICULAR PURPOSE. - -@SET_MAKE@ -VPATH = @srcdir@ -am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' -am__make_running_with_option = \ - case $${target_option-} in \ - ?) ;; \ - *) echo "am__make_running_with_option: internal error: invalid" \ - "target option '$${target_option-}' specified" >&2; \ - exit 1;; \ - esac; \ - has_opt=no; \ - sane_makeflags=$$MAKEFLAGS; \ - if $(am__is_gnu_make); then \ - sane_makeflags=$$MFLAGS; \ - else \ - case $$MAKEFLAGS in \ - *\\[\ \ ]*) \ - bs=\\; \ - sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ - | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ - esac; \ - fi; \ - skip_next=no; \ - strip_trailopt () \ - { \ - flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ - }; \ - for flg in $$sane_makeflags; do \ - test $$skip_next = yes && { skip_next=no; continue; }; \ - case $$flg in \ - *=*|--*) continue;; \ - -*I) strip_trailopt 'I'; skip_next=yes;; \ - -*I?*) strip_trailopt 'I';; \ - -*O) strip_trailopt 'O'; skip_next=yes;; \ - -*O?*) strip_trailopt 'O';; \ - -*l) strip_trailopt 'l'; skip_next=yes;; \ - -*l?*) strip_trailopt 'l';; \ - -[dEDm]) skip_next=yes;; \ - -[JT]) skip_next=yes;; \ - esac; \ - case $$flg in \ - *$$target_option*) has_opt=yes; break;; \ - esac; \ - done; \ - test $$has_opt = yes -am__make_dryrun = (target_option=n; $(am__make_running_with_option)) -am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) -pkgdatadir = $(datadir)/@PACKAGE@ -pkgincludedir = $(includedir)/@PACKAGE@ -pkglibdir = $(libdir)/@PACKAGE@ -pkglibexecdir = $(libexecdir)/@PACKAGE@ -am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd -install_sh_DATA = $(install_sh) -c -m 644 -install_sh_PROGRAM = $(install_sh) -c -install_sh_SCRIPT = $(install_sh) -c -INSTALL_HEADER = $(INSTALL_DATA) -transform = $(program_transform_name) -NORMAL_INSTALL = : -PRE_INSTALL = : -POST_INSTALL = : -NORMAL_UNINSTALL = : -PRE_UNINSTALL = : -POST_UNINSTALL = : -build_triplet = @build@ -host_triplet = @host@ -subdir = examples -DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am -ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 -am__aclocal_m4_deps = $(top_srcdir)/m4/as-ac-expand.m4 \ - $(top_srcdir)/m4/jhflags.m4 $(top_srcdir)/m4/libtool.m4 \ - $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ - $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ - $(top_srcdir)/m4/python.m4 $(top_srcdir)/configure.ac -am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ - $(ACLOCAL_M4) -mkinstalldirs = $(install_sh) -d -CONFIG_HEADER = $(top_builddir)/config.h -CONFIG_CLEAN_FILES = -CONFIG_CLEAN_VPATH_FILES = -AM_V_P = $(am__v_P_@AM_V@) -am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) -am__v_P_0 = false -am__v_P_1 = : -AM_V_GEN = $(am__v_GEN_@AM_V@) -am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) -am__v_GEN_0 = @echo " GEN " $@; -am__v_GEN_1 = -AM_V_at = $(am__v_at_@AM_V@) -am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) -am__v_at_0 = @ -am__v_at_1 = -SOURCES = -DIST_SOURCES = -am__can_run_installinfo = \ - case $$AM_UPDATE_INFO_DIR in \ - n|no|NO) false;; \ - *) (install-info --version) >/dev/null 2>&1;; \ - esac -am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) -DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) -ACLOCAL = @ACLOCAL@ -ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ -AMTAR = @AMTAR@ -AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ -AR = @AR@ -AS = @AS@ -AUTOCONF = @AUTOCONF@ -AUTOHEADER = @AUTOHEADER@ -AUTOMAKE = @AUTOMAKE@ -AWK = @AWK@ -CAIRO_CFLAGS = @CAIRO_CFLAGS@ -CAIRO_LIBS = @CAIRO_LIBS@ -CC = @CC@ -CCDEPMODE = @CCDEPMODE@ -CFLAGS = @CFLAGS@ -CODE_COVERAGE_CFLAGS = @CODE_COVERAGE_CFLAGS@ -CODE_COVERAGE_ENABLED = @CODE_COVERAGE_ENABLED@ -CODE_COVERAGE_LDFLAGS = @CODE_COVERAGE_LDFLAGS@ -CPP = @CPP@ -CPPFLAGS = @CPPFLAGS@ -CYGPATH_W = @CYGPATH_W@ -DATADIR = @DATADIR@ -DEFS = @DEFS@ -DEPDIR = @DEPDIR@ -DLLTOOL = @DLLTOOL@ -DSYMUTIL = @DSYMUTIL@ -DUMPBIN = @DUMPBIN@ -ECHO_C = @ECHO_C@ -ECHO_N = @ECHO_N@ -ECHO_T = @ECHO_T@ -EGREP = @EGREP@ -EXEEXT = @EXEEXT@ -FFI_CFLAGS = @FFI_CFLAGS@ -FFI_LIBS = @FFI_LIBS@ -FGREP = @FGREP@ -GENHTML = @GENHTML@ -GIO_CFLAGS = @GIO_CFLAGS@ -GIO_LIBS = @GIO_LIBS@ -GI_CFLAGS = @GI_CFLAGS@ -GI_DATADIR = @GI_DATADIR@ -GI_LIBS = @GI_LIBS@ -GLIB_CFLAGS = @GLIB_CFLAGS@ -GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@ -GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ -GLIB_LIBS = @GLIB_LIBS@ -GLIB_MKENUMS = @GLIB_MKENUMS@ -GOBJECT_QUERY = @GOBJECT_QUERY@ -GREP = @GREP@ -INSTALL = @INSTALL@ -INSTALL_DATA = @INSTALL_DATA@ -INSTALL_PROGRAM = @INSTALL_PROGRAM@ -INSTALL_SCRIPT = @INSTALL_SCRIPT@ -INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ -INTROSPECTION_COMPILER = @INTROSPECTION_COMPILER@ -INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@ -LCOV = @LCOV@ -LD = @LD@ -LDFLAGS = @LDFLAGS@ -LIBFFI_PC = @LIBFFI_PC@ -LIBOBJS = @LIBOBJS@ -LIBS = @LIBS@ -LIBTOOL = @LIBTOOL@ -LIPO = @LIPO@ -LN_S = @LN_S@ -LTLIBOBJS = @LTLIBOBJS@ -MAKEINFO = @MAKEINFO@ -MANIFEST_TOOL = @MANIFEST_TOOL@ -MKDIR_P = @MKDIR_P@ -NM = @NM@ -NMEDIT = @NMEDIT@ -OBJDUMP = @OBJDUMP@ -OBJEXT = @OBJEXT@ -OTOOL = @OTOOL@ -OTOOL64 = @OTOOL64@ -PACKAGE = @PACKAGE@ -PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ -PACKAGE_NAME = @PACKAGE_NAME@ -PACKAGE_STRING = @PACKAGE_STRING@ -PACKAGE_TARNAME = @PACKAGE_TARNAME@ -PACKAGE_URL = @PACKAGE_URL@ -PACKAGE_VERSION = @PACKAGE_VERSION@ -PATH_SEPARATOR = @PATH_SEPARATOR@ -PKG_CONFIG = @PKG_CONFIG@ -PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ -PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ -PLATFORM = @PLATFORM@ -PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@ -PYCAIRO_LIBS = @PYCAIRO_LIBS@ -PYGOBJECT_MAJOR_VERSION = @PYGOBJECT_MAJOR_VERSION@ -PYGOBJECT_MICRO_VERSION = @PYGOBJECT_MICRO_VERSION@ -PYGOBJECT_MINOR_VERSION = @PYGOBJECT_MINOR_VERSION@ -PYTHON = @PYTHON@ -PYTHON_BASENAME = @PYTHON_BASENAME@ -PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ -PYTHON_INCLUDES = @PYTHON_INCLUDES@ -PYTHON_LIBS = @PYTHON_LIBS@ -PYTHON_LIB_LOC = @PYTHON_LIB_LOC@ -PYTHON_PLATFORM = @PYTHON_PLATFORM@ -PYTHON_PREFIX = @PYTHON_PREFIX@ -PYTHON_SO = @PYTHON_SO@ -PYTHON_VERSION = @PYTHON_VERSION@ -RANLIB = @RANLIB@ -SED = @SED@ -SET_MAKE = @SET_MAKE@ -SHELL = @SHELL@ -STRIP = @STRIP@ -THREADING_CFLAGS = @THREADING_CFLAGS@ -VERSION = @VERSION@ -WARN_CFLAGS = @WARN_CFLAGS@ -abs_builddir = @abs_builddir@ -abs_srcdir = @abs_srcdir@ -abs_top_builddir = @abs_top_builddir@ -abs_top_srcdir = @abs_top_srcdir@ -ac_ct_AR = @ac_ct_AR@ -ac_ct_CC = @ac_ct_CC@ -ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ -am__include = @am__include@ -am__leading_dot = @am__leading_dot@ -am__quote = @am__quote@ -am__tar = @am__tar@ -am__untar = @am__untar@ -bindir = @bindir@ -build = @build@ -build_alias = @build_alias@ -build_cpu = @build_cpu@ -build_os = @build_os@ -build_vendor = @build_vendor@ -builddir = @builddir@ -datadir = @datadir@ -datarootdir = @datarootdir@ -docdir = @docdir@ -dvidir = @dvidir@ -exec_prefix = @exec_prefix@ -host = @host@ -host_alias = @host_alias@ -host_cpu = @host_cpu@ -host_os = @host_os@ -host_vendor = @host_vendor@ -htmldir = @htmldir@ -includedir = @includedir@ -infodir = @infodir@ -install_sh = @install_sh@ -libdir = @libdir@ -libexecdir = @libexecdir@ -localedir = @localedir@ -localstatedir = @localstatedir@ -mandir = @mandir@ -mkdir_p = @mkdir_p@ -oldincludedir = @oldincludedir@ -pdfdir = @pdfdir@ -pkgpyexecdir = @pkgpyexecdir@ -pkgpythondir = @pkgpythondir@ -prefix = @prefix@ -program_transform_name = @program_transform_name@ -psdir = @psdir@ -pyexecdir = @pyexecdir@ -pythondir = @pythondir@ -sbindir = @sbindir@ -sharedstatedir = @sharedstatedir@ -srcdir = @srcdir@ -sysconfdir = @sysconfdir@ -target_alias = @target_alias@ -top_build_prefix = @top_build_prefix@ -top_builddir = @top_builddir@ -top_srcdir = @top_srcdir@ -EXTRA_DIST = properties.py signal.py option.py cairo-demo.py -all: all-am - -.SUFFIXES: -$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) - @for dep in $?; do \ - case '$(am__configure_deps)' in \ - *$$dep*) \ - ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ - && { if test -f $@; then exit 0; else break; fi; }; \ - exit 1;; \ - esac; \ - done; \ - echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign examples/Makefile'; \ - $(am__cd) $(top_srcdir) && \ - $(AUTOMAKE) --foreign examples/Makefile -.PRECIOUS: Makefile -Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status - @case '$?' in \ - *config.status*) \ - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ - *) \ - echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ - esac; - -$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh - -$(top_srcdir)/configure: $(am__configure_deps) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh -$(ACLOCAL_M4): $(am__aclocal_m4_deps) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh -$(am__aclocal_m4_deps): - -mostlyclean-libtool: - -rm -f *.lo - -clean-libtool: - -rm -rf .libs _libs -tags TAGS: - -ctags CTAGS: - -cscope cscopelist: - - -distdir: $(DISTFILES) - @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ - topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ - list='$(DISTFILES)'; \ - dist_files=`for file in $$list; do echo $$file; done | \ - sed -e "s|^$$srcdirstrip/||;t" \ - -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ - case $$dist_files in \ - */*) $(MKDIR_P) `echo "$$dist_files" | \ - sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ - sort -u` ;; \ - esac; \ - for file in $$dist_files; do \ - if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ - if test -d $$d/$$file; then \ - dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ - if test -d "$(distdir)/$$file"; then \ - find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ - fi; \ - if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ - cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ - find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ - fi; \ - cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ - else \ - test -f "$(distdir)/$$file" \ - || cp -p $$d/$$file "$(distdir)/$$file" \ - || exit 1; \ - fi; \ - done -check-am: all-am -check: check-am -all-am: Makefile -installdirs: -install: install-am -install-exec: install-exec-am -install-data: install-data-am -uninstall: uninstall-am - -install-am: all-am - @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am - -installcheck: installcheck-am -install-strip: - if test -z '$(STRIP)'; then \ - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ - install; \ - else \ - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ - "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ - fi -mostlyclean-generic: - -clean-generic: - -distclean-generic: - -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) - -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) - -maintainer-clean-generic: - @echo "This command is intended for maintainers to use" - @echo "it deletes files that may require special tools to rebuild." -clean: clean-am - -clean-am: clean-generic clean-libtool mostlyclean-am - -distclean: distclean-am - -rm -f Makefile -distclean-am: clean-am distclean-generic - -dvi: dvi-am - -dvi-am: - -html: html-am - -html-am: - -info: info-am - -info-am: - -install-data-am: - -install-dvi: install-dvi-am - -install-dvi-am: - -install-exec-am: - -install-html: install-html-am - -install-html-am: - -install-info: install-info-am - -install-info-am: - -install-man: - -install-pdf: install-pdf-am - -install-pdf-am: - -install-ps: install-ps-am - -install-ps-am: - -installcheck-am: - -maintainer-clean: maintainer-clean-am - -rm -f Makefile -maintainer-clean-am: distclean-am maintainer-clean-generic - -mostlyclean: mostlyclean-am - -mostlyclean-am: mostlyclean-generic mostlyclean-libtool - -pdf: pdf-am - -pdf-am: - -ps: ps-am - -ps-am: - -uninstall-am: - -.MAKE: install-am install-strip - -.PHONY: all all-am check check-am clean clean-generic clean-libtool \ - cscopelist-am ctags-am distclean distclean-generic \ - distclean-libtool distdir dvi dvi-am html html-am info info-am \ - install install-am install-data install-data-am install-dvi \ - install-dvi-am install-exec install-exec-am install-html \ - install-html-am install-info install-info-am install-man \ - install-pdf install-pdf-am install-ps install-ps-am \ - install-strip installcheck installcheck-am installdirs \ - maintainer-clean maintainer-clean-generic mostlyclean \ - mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ - tags-am uninstall uninstall-am - - -# Tell versions [3.59,3.63) of GNU make to not export all variables. -# Otherwise a system limit (for SysV at least) may be exceeded. -.NOEXPORT: diff --git a/examples/cairo-demo.py b/examples/cairo-demo.py index 7b90ec3..ee33cdd 100755 --- a/examples/cairo-demo.py +++ b/examples/cairo-demo.py @@ -126,5 +126,6 @@ def main(): win.show_all() Gtk.main() + if __name__ == '__main__': main() diff --git a/examples/demo/demo.py b/examples/demo/demo.py new file mode 100755 index 0000000..6e26994 --- /dev/null +++ b/examples/demo/demo.py @@ -0,0 +1,363 @@ +#!/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 + +import gi +gi.require_version('Gtk', '3.0') +gi.require_version('Pango', '1.0') +gi.require_version('PangoCairo', '1.0') +gi.require_version('GdkPixbuf', '2.0') +gi.require_version('GtkSource', '4') + +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 0000000..e69de29 --- /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 0000000..2b7cddc --- /dev/null +++ b/examples/demo/demos/Css/css_accordion.py @@ -0,0 +1,80 @@ +#!/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 0000000..18c3d12 --- /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 0000000..9e1b011 --- /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 0000000..e69de29 --- /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 0000000..f0c04a4 --- /dev/null +++ b/examples/demo/demos/Entry/entry_buffer.py @@ -0,0 +1,72 @@ +#!/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 0000000..107c45a --- /dev/null +++ b/examples/demo/demos/Entry/entry_completion.py @@ -0,0 +1,86 @@ +#!/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 0000000..793b81a --- /dev/null +++ b/examples/demo/demos/Entry/search_entry.py @@ -0,0 +1,254 @@ +#!/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 0000000..e69de29 --- /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 0000000..8cb71a8 --- /dev/null +++ b/examples/demo/demos/IconView/iconviewbasics.py @@ -0,0 +1,221 @@ +#!/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 0000000..85dfa93 --- /dev/null +++ b/examples/demo/demos/IconView/iconviewedit.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python +# -*- Mode: Python; py-indent-offset: 4 -*- +# vim: tabstop=4 shiftwidth=4 expandtab +# +# Copyright (C) 2010 Red Hat, Inc., John (J5) Palmieri <johnp@redhat.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +# USA + +title = "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 0000000..e69de29 --- /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 0000000..4b3daa1 --- /dev/null +++ b/examples/demo/demos/TreeView/liststore.py @@ -0,0 +1,212 @@ +#!/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 0000000..82e6d95 --- /dev/null +++ b/examples/demo/demos/TreeView/treemodel_filelist.py @@ -0,0 +1,235 @@ +#!/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 == 0: + if stat.S_ISDIR(mode): + return folderpb + else: + return filepb + elif column == 1: + return rowref + elif column == 2: + return filestat.st_size + elif column == 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 0000000..eb8e2b9 --- /dev/null +++ b/examples/demo/demos/TreeView/treemodel_filetree.py @@ -0,0 +1,280 @@ +#!/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 == 0: + if stat.S_ISDIR(mode): + return folderpb + else: + return filepb + elif column == 1: + return os.path.basename(relpath) + elif column == 2: + return filestat.st_size + elif column == 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 0000000..b129521 --- /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 0000000..e69de29 --- /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 0000000..edac467 --- /dev/null +++ b/examples/demo/demos/appwindow.py @@ -0,0 +1,408 @@ +#!/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 0000000..9e729e9 --- /dev/null +++ b/examples/demo/demos/assistant.py @@ -0,0 +1,135 @@ +#!/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 0000000..47e09a4 --- /dev/null +++ b/examples/demo/demos/builder.py @@ -0,0 +1,65 @@ +#!/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 0000000..be94984 --- /dev/null +++ b/examples/demo/demos/button_box.py @@ -0,0 +1,122 @@ +#!/usr/bin/env python +# -*- Mode: Python; py-indent-offset: 4 -*- +# vim: tabstop=4 shiftwidth=4 expandtab +# +# Copyright (C) 2010 Red Hat, Inc., John (J5) Palmieri <johnp@redhat.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +# USA + +title = "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 0000000..5a88828 --- /dev/null +++ b/examples/demo/demos/clipboard.py @@ -0,0 +1,228 @@ +#!/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 0000000..d05ca52 --- /dev/null +++ b/examples/demo/demos/colorselector.py @@ -0,0 +1,112 @@ +#!/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 0000000..1215f16 --- /dev/null +++ b/examples/demo/demos/combobox.py @@ -0,0 +1,319 @@ +#!/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 Binary files differnew file mode 100644 index 0000000..eb5885f --- /dev/null +++ b/examples/demo/demos/data/alphatest.png diff --git a/examples/demo/demos/data/apple-red.png b/examples/demo/demos/data/apple-red.png Binary files differnew file mode 100644 index 0000000..b0a24e9 --- /dev/null +++ b/examples/demo/demos/data/apple-red.png diff --git a/examples/demo/demos/data/background.jpg b/examples/demo/demos/data/background.jpg Binary files differnew file mode 100644 index 0000000..86c006a --- /dev/null +++ b/examples/demo/demos/data/background.jpg diff --git a/examples/demo/demos/data/brick.png b/examples/demo/demos/data/brick.png Binary files differnew file mode 100644 index 0000000..d413cd2 --- /dev/null +++ b/examples/demo/demos/data/brick.png diff --git a/examples/demo/demos/data/brick2.png b/examples/demo/demos/data/brick2.png Binary files differnew file mode 100644 index 0000000..cfcd079 --- /dev/null +++ b/examples/demo/demos/data/brick2.png diff --git a/examples/demo/demos/data/css_accordion.css b/examples/demo/demos/data/css_accordion.css new file mode 100644 index 0000000..a243427 --- /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 0000000..62dba7a --- /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 0000000..eb9d4d6 --- /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 0000000..5060c39 --- /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 Binary files differnew file mode 100644 index 0000000..e19d822 --- /dev/null +++ b/examples/demo/demos/data/demo.gresource diff --git a/examples/demo/demos/data/demo.gresource.xml b/examples/demo/demos/data/demo.gresource.xml new file mode 100644 index 0000000..866769f --- /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 0000000..57dd232 --- /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 Binary files differnew file mode 100644 index 0000000..ac986c8 --- /dev/null +++ b/examples/demo/demos/data/floppybuddy.gif diff --git a/examples/demo/demos/data/gnome-applets.png b/examples/demo/demos/data/gnome-applets.png Binary files differnew file mode 100644 index 0000000..8d3549e --- /dev/null +++ b/examples/demo/demos/data/gnome-applets.png diff --git a/examples/demo/demos/data/gnome-calendar.png b/examples/demo/demos/data/gnome-calendar.png Binary files differnew file mode 100644 index 0000000..889f329 --- /dev/null +++ b/examples/demo/demos/data/gnome-calendar.png diff --git a/examples/demo/demos/data/gnome-foot.png b/examples/demo/demos/data/gnome-foot.png Binary files differnew file mode 100644 index 0000000..0476658 --- /dev/null +++ b/examples/demo/demos/data/gnome-foot.png diff --git a/examples/demo/demos/data/gnome-fs-directory.png b/examples/demo/demos/data/gnome-fs-directory.png Binary files differnew file mode 100644 index 0000000..05921a6 --- /dev/null +++ b/examples/demo/demos/data/gnome-fs-directory.png diff --git a/examples/demo/demos/data/gnome-fs-regular.png b/examples/demo/demos/data/gnome-fs-regular.png Binary files differnew file mode 100644 index 0000000..0f5019c --- /dev/null +++ b/examples/demo/demos/data/gnome-fs-regular.png diff --git a/examples/demo/demos/data/gnome-gimp.png b/examples/demo/demos/data/gnome-gimp.png Binary files differnew file mode 100644 index 0000000..f6bbc6d --- /dev/null +++ b/examples/demo/demos/data/gnome-gimp.png diff --git a/examples/demo/demos/data/gnome-gmush.png b/examples/demo/demos/data/gnome-gmush.png Binary files differnew file mode 100644 index 0000000..0a4b0d0 --- /dev/null +++ b/examples/demo/demos/data/gnome-gmush.png diff --git a/examples/demo/demos/data/gnome-gsame.png b/examples/demo/demos/data/gnome-gsame.png Binary files differnew file mode 100644 index 0000000..01c0611 --- /dev/null +++ b/examples/demo/demos/data/gnome-gsame.png diff --git a/examples/demo/demos/data/gnu-keys.png b/examples/demo/demos/data/gnu-keys.png Binary files differnew file mode 100644 index 0000000..58a3377 --- /dev/null +++ b/examples/demo/demos/data/gnu-keys.png diff --git a/examples/demo/demos/data/gtk-logo-rgb.gif b/examples/demo/demos/data/gtk-logo-rgb.gif Binary files differnew file mode 100644 index 0000000..63c622b --- /dev/null +++ b/examples/demo/demos/data/gtk-logo-rgb.gif diff --git a/examples/demo/demos/data/reset.css b/examples/demo/demos/data/reset.css new file mode 100644 index 0000000..1c27a8e --- /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 0000000..47d6822 --- /dev/null +++ b/examples/demo/demos/dialogs.py @@ -0,0 +1,156 @@ +#!/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 0000000..b04c41d --- /dev/null +++ b/examples/demo/demos/drawingarea.py @@ -0,0 +1,207 @@ +#!/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 0000000..0ec149e --- /dev/null +++ b/examples/demo/demos/expander.py @@ -0,0 +1,62 @@ +#!/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 0000000..0485b7c --- /dev/null +++ b/examples/demo/demos/flowbox.py @@ -0,0 +1,752 @@ +#!/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 0000000..80c99af --- /dev/null +++ b/examples/demo/demos/images.py @@ -0,0 +1,305 @@ +#!/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 0000000..b8a1dc7 --- /dev/null +++ b/examples/demo/demos/infobars.py @@ -0,0 +1,103 @@ +#!/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 0000000..c6d331d --- /dev/null +++ b/examples/demo/demos/links.py @@ -0,0 +1,77 @@ +#!/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 0000000..7cf2e57 --- /dev/null +++ b/examples/demo/demos/menus.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 = "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 0000000..d8a9c75 --- /dev/null +++ b/examples/demo/demos/pickers.py @@ -0,0 +1,77 @@ +#!/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 0000000..c213584 --- /dev/null +++ b/examples/demo/demos/pixbuf.py @@ -0,0 +1,184 @@ +#!/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 0000000..a87bd6f --- /dev/null +++ b/examples/demo/demos/printing.py @@ -0,0 +1,180 @@ +#!/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(file_path): + raise Exception("file not found: %s" % (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 0000000..4dac189 --- /dev/null +++ b/examples/demo/demos/rotatedtext.py @@ -0,0 +1,191 @@ +#!/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 = "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 math + + +UTF8_TEXT = u"I ♥ GTK" +HEART = u"♥" +BYTES_TEXT = UTF8_TEXT.encode("utf-8") +BYTES_HEART = HEART.encode("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 0000000..4bd7684 --- /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() diff --git a/examples/properties.py b/examples/properties.py index 465b3c3..257e80a 100644 --- a/examples/properties.py +++ b/examples/properties.py @@ -13,20 +13,21 @@ class MyObject(GObject.GObject): def readonly(self): return 'readonly' + GObject.type_register(MyObject) -print "MyObject properties: ", list(MyObject.props) +print("MyObject properties: ", list(MyObject.props)) obj = MyObject() -print "obj.foo ==", obj.foo +print("obj.foo ==", obj.foo) obj.foo = 'spam' -print "obj.foo = spam" +print("obj.foo = spam") -print "obj.foo == ", obj.foo +print("obj.foo == ", obj.foo) -print "obj.boolprop == ", obj.boolprop +print("obj.boolprop == ", obj.boolprop) -print obj.readonly +print(obj.readonly) obj.readonly = 'does-not-work' |