summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDieter Verfaillie <dieterv@optionexplicit.be>2011-03-24 10:42:17 +0100
committerDieter Verfaillie <dieterv@optionexplicit.be>2011-03-24 10:42:17 +0100
commitfef1e60f6e20022fac7f1f1268004b6a022491dd (patch)
tree52f4cbe44fbe4e220c6e1ab0209ae21d63ab5c3b
parentad5dabefef5f232d27f3b190ce3f871119ad3677 (diff)
parent27496e0e86e7d8798caf019fd09af5c6a30ec633 (diff)
downloadpygobject-fef1e60f6e20022fac7f1f1268004b6a022491dd.tar.gz
Merge branch 'pygobject-2-28' into windows
-rw-r--r--.gitignore39
-rw-r--r--Makefile.am29
-rw-r--r--NEWS458
-rw-r--r--README.win3213
-rwxr-xr-xautogen.sh3
-rw-r--r--codegen/.gitignore2
-rw-r--r--codegen/Makefile.am2
-rw-r--r--codegen/argtypes.py8
-rw-r--r--codegen/definitions.py14
-rwxr-xr-xcodegen/docextract_to_xml.py2
-rw-r--r--configure.ac26
-rw-r--r--demos/gtk-demo/demos/Entry/search_entry.py252
-rw-r--r--demos/gtk-demo/demos/Icon View/__init__.py0
-rw-r--r--demos/gtk-demo/demos/Icon View/iconviewbasics.py218
-rw-r--r--demos/gtk-demo/demos/Icon View/iconviewedit.py101
-rw-r--r--demos/gtk-demo/demos/Tree View/__init__.py0
-rw-r--r--demos/gtk-demo/demos/Tree View/liststore.py205
-rw-r--r--demos/gtk-demo/demos/appwindow.py64
-rw-r--r--demos/gtk-demo/demos/assistant.py3
-rw-r--r--demos/gtk-demo/demos/builder.py3
-rw-r--r--demos/gtk-demo/demos/button_box.py10
-rw-r--r--demos/gtk-demo/demos/clipboard.py40
-rw-r--r--demos/gtk-demo/demos/colorselector.py46
-rw-r--r--demos/gtk-demo/demos/combobox.py83
-rw-r--r--demos/gtk-demo/demos/dialogs.py153
-rw-r--r--demos/gtk-demo/demos/drawingarea.py147
-rw-r--r--demos/gtk-demo/demos/expander.py60
-rw-r--r--demos/gtk-demo/demos/images.py312
-rw-r--r--demos/gtk-demo/demos/infobars.py99
-rw-r--r--demos/gtk-demo/demos/links.py74
-rw-r--r--demos/gtk-demo/demos/menus.py122
-rw-r--r--demos/gtk-demo/demos/pickers.py74
-rw-r--r--demos/gtk-demo/demos/pixbuf.py183
-rw-r--r--demos/gtk-demo/demos/printing.py173
-rw-r--r--demos/gtk-demo/demos/rotatedtext.py196
-rwxr-xr-xdemos/gtk-demo/gtk-demo.py22
-rw-r--r--docs/.gitignore7
-rw-r--r--docs/Makefile.am2
-rw-r--r--dsextras.py279
-rw-r--r--examples/Makefile.am2
-rw-r--r--gi/.gitignore40
-rw-r--r--gi/Makefile.am9
-rw-r--r--gi/__init__.py30
-rw-r--r--gi/gimodule.c276
-rw-r--r--gi/importer.py1
-rw-r--r--gi/module.py103
-rw-r--r--gi/overrides/GIMarshallingTests.py4
-rw-r--r--gi/overrides/GLib.py322
-rw-r--r--gi/overrides/Gdk.py184
-rw-r--r--gi/overrides/Gio.py215
-rw-r--r--gi/overrides/Gtk.py634
-rw-r--r--gi/overrides/Makefile.am6
-rw-r--r--gi/overrides/Pango.py51
-rw-r--r--gi/overrides/__init__.py66
-rw-r--r--gi/pygi-argument.c371
-rw-r--r--gi/pygi-argument.h2
-rw-r--r--gi/pygi-closure.c77
-rw-r--r--gi/pygi-foreign-cairo.c12
-rw-r--r--gi/pygi-foreign-gvariant.c4
-rw-r--r--gi/pygi-foreign-gvariant.h2
-rw-r--r--gi/pygi-foreign.c2
-rw-r--r--gi/pygi-info.c197
-rw-r--r--gi/pygi-info.h7
-rw-r--r--gi/pygi-invoke.c177
-rw-r--r--gi/pygi-invoke.h3
-rw-r--r--gi/pygi-private.h1
-rw-r--r--gi/pygi-property.c8
-rw-r--r--gi/pygi-repository.c23
-rw-r--r--gi/pygi-signal-closure.c245
-rw-r--r--gi/pygi-signal-closure.h46
-rw-r--r--gi/pygi.h40
-rw-r--r--gi/repository/Makefile.am4
-rw-r--r--gi/tests/Makefile.am2
-rw-r--r--gi/types.py205
-rw-r--r--gio/gfile.override5
-rw-r--r--git.mk200
-rw-r--r--glib/Makefile.am4
-rw-r--r--glib/glibmodule.c5
-rw-r--r--glib/pyglib-private.h3
-rw-r--r--glib/pyglib-python-compat.h8
-rw-r--r--glib/pyglib.c62
-rw-r--r--glib/pygmaincontext.c22
-rw-r--r--glib/pygmaincontext.h2
-rw-r--r--glib/pygmainloop.c5
-rw-r--r--glib/pygoptioncontext.c21
-rw-r--r--glib/pygoptioncontext.h2
-rw-r--r--glib/pygoptiongroup.c26
-rw-r--r--glib/pygoptiongroup.h2
-rw-r--r--glib/pygsource.c2
-rw-r--r--gobject/.gitignore3
-rw-r--r--gobject/Makefile.am4
-rw-r--r--gobject/__init__.py6
-rw-r--r--gobject/gobjectmodule.c55
-rw-r--r--gobject/propertyhelper.py31
-rw-r--r--gobject/pygflags.c15
-rw-r--r--gobject/pygobject-private.h13
-rw-r--r--gobject/pygobject.c125
-rw-r--r--gobject/pygobject.h2
-rw-r--r--gobject/pygpointer.c2
-rw-r--r--gobject/pygtype.c96
-rw-r--r--m4/python.m436
-rw-r--r--pygi-convert.sh201
-rw-r--r--pygobject-2.0.pc.in1
-rw-r--r--pygobject_postinstall.py61
-rwxr-xr-xsetup.py247
-rw-r--r--tests/.gitignore2
-rw-r--r--tests/Makefile.am40
-rw-r--r--tests/compathelper.py19
-rw-r--r--tests/org.gnome.test.gschema.xml25
-rw-r--r--tests/python.supp387
-rw-r--r--tests/runtests-windows.py47
-rw-r--r--tests/runtests.py8
-rw-r--r--tests/test-floating.c66
-rw-r--r--tests/test-floating.h41
-rw-r--r--tests/test_everything.py112
-rw-r--r--tests/test_gdbus.py218
-rw-r--r--tests/test_gi.py252
-rw-r--r--tests/test_gio.py16
-rw-r--r--tests/test_gobject.py158
-rw-r--r--tests/test_gresolver.py5
-rw-r--r--tests/test_overrides.py1044
-rw-r--r--tests/test_properties.py56
-rw-r--r--tests/testhelpermodule.c107
123 files changed, 9370 insertions, 1395 deletions
diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index d9f27b12..00000000
--- a/.gitignore
+++ /dev/null
@@ -1,39 +0,0 @@
-# Autotools-generated files.
-aclocal.m4
-autom4te.cache
-compile
-config.guess
-config.h.in
-config.lt
-config.sub
-configure
-depcomp
-install-sh
-ltmain.sh
-m4
-Makefile.in
-missing
-py-compile
-
-# Configuration-generated files.
-config.h
-config.log
-config.status
-libtool
-Makefile
-PKG-INFO
-stamp-h1
-
-# Build-generated files.
-*.la
-*.lo
-*.o
-*.pc
-*.pyc
-*.so
-.deps
-.libs
-
-# Other generated files.
-*~
-*.orig
diff --git a/Makefile.am b/Makefile.am
index 6fe43c60..b9b9ed3f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -27,6 +27,33 @@ EXTRA_DIST = \
m4/jhflags.m4 \
m4/python.m4
+MAINTAINERCLEANFILES = \
+ $(srcdir)/INSTALL \
+ $(srcdir)/aclocal.m4 \
+ $(srcdir)/autoscan.log \
+ $(srcdir)/compile \
+ $(srcdir)/config.guess \
+ $(srcdir)/config.h.in \
+ $(srcdir)/config.sub \
+ $(srcdir)/configure.scan \
+ $(srcdir)/depcomp \
+ $(srcdir)/install-sh \
+ $(srcdir)/ltmain.sh \
+ $(srcdir)/missing \
+ $(srcdir)/mkinstalldirs \
+ $(srcdir)/omf.make \
+ $(srcdir)/xmldocs.make \
+ $(srcdir)/gtk-doc.make \
+ $(srcdir)/ChangeLog \
+ $(srcdir)/py-compile \
+ $(srcdir)/m4/libtool.m4 \
+ $(srcdir)/m4/ltoptions.m4 \
+ $(srcdir)/m4/ltsugar.m4 \
+ $(srcdir)/m4/ltversion.m4 \
+ $(srcdir)/m4/lt~obsolete.m4 \
+ `find "$(srcdir)" -type f -name Makefile.in -print` \
+ `find "$(srcdir)" -type f -name "*.pyc" -print`
+
BUILT_EXTRA_DIST = \
ChangeLog
@@ -131,3 +158,5 @@ check.gdb:
check.valgrind:
cd tests && $(MAKE) check.valgrind
+
+-include $(top_srcdir)/git.mk
diff --git a/NEWS b/NEWS
index fc43753d..1c7e4bb6 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,461 @@
+2.28.2 22-Mar-2011
+ - fix static ABI for setting string gvalues from python objects (John (J
+ - Fix GSchema tests for separate build tree (Martin Pitt)
+ - GIO tests: Fix remaining test case for separate build tree (Martin Pit
+ - GIO tests: Fix for separate build tree (Martin Pitt)
+
+2.28.1 21-Mar-2011
+ - pygi-convert.sh remove gobject tests, GObject works now (John Stowers)
+ - pygi-convert.sh add GObject.xxx and webkit (John Stowers)
+ - [gi] marshal raw closures (John (J5) Palmieri)
+ - Revert "Deduce PYTHON_LIBS in addition to PYTHON_INCLUDES" (Martin Pit
+ - setup.py: fix user_access_control option (Dieter Verfaillie)
+ - [gi-overrides] fix marshalling pygobjects in treemodels (John (J5) Pal
+ - [gi] Respect the MessageType for Gtk.MessageDialog (Martin Pitt)
+ - [gi] Do not require signature for D-BUS methods without arguments (Mar
+ - [gi-demos] add pickers demo (John (J5) Palmieri)
+ - [gi-demos] add menu demo (John (J5) Palmieri)
+ - [gi-overrides] TreeViewColumn.set_cell_data_func func_data can be None
+ - [gi-demos] dont try and run demos that represent directories (John Sto
+ - [gi-overrides] fix exception block so it works in Python 2.5 (John (J5
+
+2.28.0 08-Mar-2011 (stable)
+ - [gi-demos] some python 3 compat fixes (John (J5) Palmieri)
+ - [gi-demos] catch the correct error class (John (J5) Palmieri)
+ - Try not to sink objects returned by C functions. (Steve Frécinaux)
+ - Do not leak python references when using the gobject.property() helper. (Steve Frécinaux)
+ - [gi] fix try except blocks so they work in Python 2.5 (John (J5) Palmieri)
+ - handle uchar as bytes, not strings in python 3 (John (J5) Palmieri)
+ - [gi-overrides] handle unichar gvalues when setting treemodels (John (J5) Palmieri)
+ - [gi-overrides] special case python 2 keywords that crept in (John (J5) Palmieri)
+ - check for the py3 _thread module in configure.ac if thread is not found (John (J5) Palmieri)
+ - [gi-demos] add iconview demo (John (J5) Palmieri)
+ - [gi] wrap the keyword argument in a dict so we don't break Python 2.5 (John (J5) Palmieri)
+ - [gi-demos] add the combobox with string ids section to the demos (John (J5) Palmieri)
+ - [gi-overrides] add an override for Gdk.RGBA (John (J5) Palmieri)
+ - [gi-demos] fix up search-entry to reflect annotations fixed in Gtk+ master (John (J5) Palmieri)
+ - [gi-demos] add search entry demo (John (J5) Palmieri)
+ - [gi] wrap map in a list for Python 3 compat (John (J5) Palmieri)
+ - [gi-demos] fix up the validation combobox (John (J5) Palmieri)
+ - add overridesdir variable in the .pc file for 3rd party overrides (John (J5) Palmieri)
+ - [gi] remove unref for closures since they are floating objects that get sunk (John (J5) Palmieri)
+ - setup.py: Set bdist_wininst user-access-control property (Dieter Verfaillie)
+ - Fix uninitialized variable in gi.require_version() (Martin Pitt)
+ - Run tests with LC_MESSAGES="C" (Martin Pitt)
+ - [gi-overrides] override Gtk.stock_lookup to not return success (John (J5) Palmieri)
+
+2.27.91 28-Feb-2011 (2.28 pre-release)
+ - [gi-tests] use Gdk.test_simulate_button instead of emitting event ourselves (John (J5) Palmieri)
+ - [gi-tests] tests for EventButton override. (Laszlo Pandy)
+ - Skip interfaces when checking for conflicts in the MRO (Tomeu Vizoso)
+ - [gi-overrides] Add event methods to all event union members (John (J5) Palmieri)
+ - [gi] check to see if object is a member of a union when validating paramaters (John (J5) Palmieri)
+ - [gi] Remove DyanmicModule.load() to _load() to prevent overriding GI attrs. (Laszlo Pandy)
+ - Test case with John's fix for crash with C arrays and a GError is set. (Laszlo Pandy)
+ - [gi-overrides] fix setting rows in treeview to accept None as a value (John (J5) Palmieri)
+ - [gi] Add value_name for enum and flags from introspection "c:identifier" (if attr is available). (Laszlo Pandy)
+ - Don't force loading of DynamicModule until set in sys.modules (Laszlo Pandy)
+ - Fix flags with multiple names for the same value. (Laszlo Pandy)
+ - [gi-demos] add liststore demo (John (J5) Palmieri)
+ - [gi-demos] run through the demos and remove the FIXMEs that have been fixed (John (J5) Palmieri)
+ - Load typelibs at import time, add gi.require_version() (Tomeu Vizoso)
+ - use GValue support to marshal GtkTreeModel values correctly (John (J5) Palmieri)
+ - [gi] pass raw GValues instead of trying to marshal them (John (J5) Palmieri)
+ - [gi-demos] add icon view edit and drag-and-drop demo (John (J5) Palmieri)
+ - [gi] Register GType for non-GType enums and flags at runtime. (Laszlo Pandy)
+ - [gi-demos] add info bars demo (John (J5) Palmieri)
+ - tests/runtests.py: Add missing "import sys" (Martin Pitt)
+ - [gi] Add Pythonic gdbus method invocation (Martin Pitt)
+ - Skip GError out parameters in Python closure. (Laszlo Pandy)
+ - [gi-demos] added rotate text demo (John (J5) Palmieri)
+ - [gi-demos] add images demo (John (J5) Palmieri)
+ - [gi-demos] add pixbuf demo (John (J5) Palmieri)
+ - [gi-demos] remove fixmes from print demo, fixed in pango (John (J5) Palmieri)
+ - [gi-demos] add printing demo (John (J5) Palmieri)
+ - [gi-overrides] add cursor overrides (John (J5) Palmieri)
+ - [gi-demos] add the links demo (John (J5) Palmieri)
+ - [gi-demos] add expander demo (John (J5) Palmieri)
+ - [gi-overrides] use pop instead of del and add extra tests for Gtk.Table kwargs (John (J5) Palmieri)
+ - [tests] Separate processes for GI and static binding tests. (Laszlo Pandy)
+ - [GI] Remove implicit loading of gi module preserve the code path for static bindings. (Laszlo Pandy)
+ - [gi-demos] add dialogs demo (John (J5) Palmieri)
+ - [gi-overrides] fix typo in GtkTable constructor (John (J5) Palmieri)
+ - [gi-demos] keep popup menu from destroying itself by holding a ref in app class (John (J5) Palmieri)
+ - [gi-overrides] add a Gtk.Menu override for the popup method (John (J5) Palmieri)
+ - [gi-demos] fix the about dialog in appwindow demo (John (J5) Palmieri)
+ - [gi-demos] fix clipboard demo so DnD works (John (J5) Palmieri)
+ - [gi-demos] fix clipboard demo to reflect new API (John (J5) Palmieri)
+ - [gi-demo] Fix color dialog demo to run with new draw, style and color apis (John (J5) Palmieri)
+ - [gi-demos] fix most of the combobox app (John (J5) Palmieri)
+ - Use PyGI type conversion (to fix foreign types) for signal callbacks. (Laszlo Pandy)
+ - [gi-demos] fix drawingarea app to use the new draw api (John (J5) Palmieri)
+ - [gi-overrides] for Gtk 3 alias Gdk.Rectangle to cairo.RectangleInt (John (J5) Palmieri)
+ - [gi-overrides] let user set the proper property names in Gtk.Table (John (J5) Palmieri)
+ - [gi-demos] get appwindow demo working again (John (J5) Palmieri)
+ - [gi-demos] fixed use of tree_iter_get (John (J5) Palmieri)
+
+2.27.90 11-Feb-2011 (2.28 pre-release)
+ - fix build to correctly use python-config (John (J5) Palmieri)
+ - Run gio tests separately when enabled (Martin Pitt)
+ - Revert "Remove gio static bindings" (Martin Pitt)
+ - Decrease the refcount for GInitiallyUnowned constructors. (Steve Frécinaux)
+ - Ensure the sink functions are only ran once. (Steve Frécinaux)
+ - Revert "Fix wrong refcount when calling introspected widget constructors" (Steve Frécinaux)
+ - Revert "Fix reference leaks for GInitiallyUnowned objects" (Steve Frécinaux)
+ - Run test suite under dbus-launch (Martin Pitt)
+ - Fix test_gdbus.py to be Python3 friendly (Martin Pitt)
+ - [gi] Provide comfortable GSettings API (Martin Pitt)
+ - Fix vfunc search bug when using GInterfaces and a do_* method. (Laszlo Pandy)
+ - [GI] Add tests for Gtk.Widget.drag_* methods. (Laszlo Pandy)
+ - [python 3] use the right syntaxis to raise exceptions (Ignacio Casal Quinteiro)
+ - [gi] return PYGLIB_MODULE_ERROR_RETURN on error and use pygobject_init (Ignacio Casal Quinteiro)
+ - [gi] return PYGLIB_MODULE_ERROR_RETURN on error (Ignacio Casal Quinteiro)
+ - Fix wrong refcount when calling introspected widget constructors (Steve Frécinaux)
+ - Gdk.Window: Map the standard constructor to the *new* constructor (Simon Schampijer)
+ - Ship tests/org.gnome.test.gschema.xml in dist tarballs (Martin Pitt)
+ - [gi] Add GSettings tests (Martin Pitt)
+ - [gi] Provide GtkTextBuffer.insert_with_tags_by_name() (Martin Pitt)
+ - [gi] Support tag names in GtkTextBuffer.insert_with_tags() (Martin Pitt)
+ - Add MAINTAINERCLEANFILES (Ignacio Casal Quinteiro)
+ - Remove .gitignore files and use git.mk (Ignacio Casal Quinteiro)
+ - pygi-convert.sh: Convert Pango.TabAlign.* (Martin Pitt)
+ - pygi-convert.sh: Drop window -> get_window() conversion (Martin Pitt)
+ - pygi-convert.sh: Don't convert self.window assignments (Martin Pitt)
+ - Fix leaked python reference in python-defined subclasses (Steve Frécinaux)
+ - Add some tests for the number of python refs held at creation time (Steve Frécinaux)
+ - Factor out parameter marshalling from construction functions. (Steve Frécinaux)
+ - [gi] in python 3 an array of uint8 can be bytes but not string (John (J5) Palmieri)
+ - [gi] fix Gio.FileEnumerator to reflect the Python 3 iter protocol (John (J5) Palmieri)
+ - [gi] python 3 fixes (John (J5) Palmieri)
+ - [gi] fix try/except blocks using depricated raise format (John (J5) Palmieri)
+ - [gi] Add docstring to GLib.Variant constructor (Martin Pitt)
+ - [gi] update gdbus test cases for previous GVariant change (Martin Pitt)
+ - [gi] Accept only a single object in GLib.Variant constructor (Martin Pitt)
+ - Speed up _setup_native_vfuncs() (Laszlo Pandy)
+ - Speed up class creation: rewrite _setup_vfuncs() to be much more efficient. (Laszlo Pandy)
+ - pygi-convert.sh: Convert gtk.UI_MANAGER_* (Sebastian Pölsterl)
+ - pygi-convert.sh: Convert gdk.GRAB_* (Sebastian Pölsterl)
+ - [gi] set the gtype GValue correctly (Ignacio Casal Quinteiro)
+ - [gi] use the right argument type for callback (Ignacio Casal Quinteiro)
+ - [gi] Add test cases for GDBus client operations (Martin Pitt)
+ - [gi] Add Variant construction/unpack support for boxed Variants (Martin Pitt)
+ - Merge branch 'windows-setup-fixes' (Dieter Verfaillie)
+ - pygi-convert.sh: GdkPixbuf methods (Thomas Hindoe Paaboel Andersen)
+ - pygi-convert.sh: Gdk.COLORSPACE_RGB (Thomas Hindoe Paaboel Andersen)
+ - [gi] Support nested objects and empty sequences in GLib.Variant building (Martin Pitt)
+ - Uncomment test_gi.TestInterfaceClash (Tomeu Vizoso)
+ - Fix reference leaks for GInitiallyUnowned objects (Steve Frécinaux)
+ - Add tests for refcount of a GObject owned by a library (Steve Frécinaux)
+ - Add a test to check for regular object reference count (Steve Frécinaux)
+ - [gi] Update TreeView.enable_model_drag_{source,dest} to current GTK (Martin Pitt)
+ - Fix a typo in a private symbol name. (Steve Frécinaux)
+ - pygi-convert.sh: Convert glib.source_remove() (Martin Pitt)
+ - Fix typo in previous commit to actually convert glib.GError (Martin Pitt)
+ - pygi-convert.sh: Move some glib bits which are better handled by gobject (Martin Pitt)
+ - Modify override for Gtk.Adjustment to allow position or keyword arguments in __init__(). (Laszlo Pandy)
+ - [gi] Fix small typo in previous commit (Martin Pitt)
+ - [gi] Add pythonic iterator and indexing for string GVariants (Martin Pitt)
+ - Construct structs using default API constructor (Tomeu Vizoso)
+ - pygi-convert.sh: Migrate Gdk.Cursor constructor, and some cursor names (Martin Pitt)
+ - pygi-convert.sh: Handle .window attributes (Martin Pitt)
+ - Also deal with foreign boxed structs (Tomeu Vizoso)
+ - [gi] Convert GErrors to GObject.GError exceptions, and throw them upon returning from calling the C function. (Laszlo Pandy)
+ - pygi-convert.sh: Don't convert glib -> GLib for now (Martin Pitt)
+ - Link libregress.so to GIO_LIBS again (Tomeu Vizoso)
+ - Fix attributes 2BUTTON_PRESS and 3BUTTON_PRESS of Gdk.EventType. (Laszlo Pandy)
+ - [gi] Fixed typo in exception (Sebastian Pölsterl)
+ - [gi] Enable handling of Gdk.EventType.2BUTTON_PRESS and 3BUTTON_PRESS (Sebastian Pölsterl)
+ - Revert "Fix Pango FontDescription override" (Martin Pitt)
+ - Python iterator interface support for GFileEnumerator. (Tony Young)
+ - Remove gio static bindings (Tomeu Vizoso)
+ - [gi] set length when marshalling guint8 erases (Ignacio Casal Quinteiro)
+ - Convert Gdk.Pixbuf to GdkPixbuf.Pixbuf (Sebastian Pölsterl)
+ - Disable calls to PyGILState_* when threads are disabled (Arnaud Charlet)
+ - pygi-convert.sh: Do not comment out set_cell_data_func() calls; these should be ported properly (Martin Pitt)
+ - pygi-convert.sh: Fix match for adding missing imports (Martin Pitt)
+ - pygi-convert.sh: Fix Gtk.Label handling to be idempotent (Martin Pitt)
+ - Remove trailing whitespace from gi/overrides/Gtk.py (Laszlo Pandy)
+ - Fix Pango FontDescription override (Martin Pitt)
+ - tests: Respect existing $GI_TYPELIB_PATH (Martin Pitt)
+ - Merge branch 'value' (Sebastian Pölsterl)
+ - GTK overrides: Do type conversion to column types of ListStore and TreeStore in set_value (Sebastian Pölsterl)
+ - Always register a new GType when a GObject class is subclassed (Steve Frécinaux)
+ - Raise required versions of GLib and GObject-Introspection (Simon van der Linden)
+ - pygi-convert.sh: Handle keysyms (Martin Pitt)
+ - GLib overrides: Add test case for array variant building (Martin Pitt)
+ - Remove cairo.RectangleInt from the foreign module (Tomeu Vizoso)
+ - Dont try to guess the transfer if its a boxed (Tomeu Vizoso)
+ - The tags can be Empty not None. (Ignacio Casal Quinteiro)
+ - Add Pythonic iterators and indexing to GVariant (Martin Pitt)
+ - Add GLib.Variant.unpack() (Martin Pitt)
+ - Add override for gtk_text_buffer_insert_with_tags (Ignacio Casal Quinteiro)
+ - Deduce PYTHON_LIBS in addition to PYTHON_INCLUDES (Simon van der Linden)
+ - Kill JD_CHECK_PYTHON_HEADERS (Simon van der Linden)
+ - Revert "Override Gtk.Box.pack_start and pack_end to set default values to be compliant with pygtk" (Sebastian Pölsterl)
+ - Revert "Override Gtk.CellLayout.pack_start and pack_end to add default values to be compliant with pygtk" (Sebastian Pölsterl)
+ - Revert "Override Gtk.TreeViewColumn.pack_start, pack_end and set_cell_data_func to add default values to be compliant with pygtk" (Sebastian Pölsterl)
+ - pygi-convert.sh: Handle gtk.combo_box_new_text() (Martin Pitt)
+ - Override TreeSortable.set_sort_func and set_default_sort_func to add default values to be pygtk compliant (Sebastian Pölsterl)
+ - Override Gtk.TreeViewColumn.pack_start, pack_end and set_cell_data_func to add default values to be compliant with pygtk (Sebastian Pölsterl)
+ - Override Gtk.CellLayout.pack_start and pack_end to add default values to be compliant with pygtk (Sebastian Pölsterl)
+ - Override Gtk.Paned pack1 and pack2 to add default values to be compliant with pygtk (Sebastian Pölsterl)
+ - Override Gtk.Box.pack_start and pack_end to set default values to be compliant with pygtk (Sebastian Pölsterl)
+ - Handle GObject subclasses in the property helper. (Steve Frécinaux)
+ - Fix handling of unicode for GtkTreeModels (Martin Pitt)
+ - In IntrospectionModule and DynamicModule classes, make all instance attributes start with an underscore. (Laszlo Pandy)
+ - Amend previous enum wrapping commit to remove redundant setting of __info__ attribute. (Laszlo Pandy)
+ - pygi-convert.sh: Handle GdkPixbuf.InterpType (Martin Pitt)
+ - Fix wrapping of enums: Create new Python type for each non-gtype enum. (Laszlo Pandy)
+ - Use g_vfunc_info_invoke for chaining up in vfuncs (Tomeu Vizoso)
+ - Move pyglib_{main_context, option_context, option_group}_new into _PyGLib_API (Simon van der Linden)
+ - pygi-convert.sh: Handle Gdk.DragAction (Martin Pitt)
+ - pygi-convert.sh: Generalize Gtk.Settings migration (Martin Pitt)
+ - pygi-convert.sh: Don't change the name of "glib" submodules (Martin Pitt)
+ - Plug another memory leak (Paolo Borelli)
+ - Plug a small memory leak. (Paolo Borelli)
+ - Override Table.attach() to behave like pygtk (Paolo Borelli)
+ - pygi-convert.sh: Convert Pango.WrapMode (Martin Pitt)
+ - pygi-convert.sh: Don't change the name of "gtk" submodules (Martin Pitt)
+ - Fix the __dir__() methods on DynamicModule and IntrospectionModule (Laszlo Pandy)
+ - pygi-convert.sh: handle ReliefStyle (Paolo Borelli)
+ - setup.py: fix the provides keyword argument (Dieter Verfaillie)
+ - setup.py: use the same spaces-less format for all setup() parameters (Dieter Verfaillie)
+ - Add a __repr__() method to DynamicModule. (Laszlo Pandy)
+ - Go back to using getattr() in DynamicModule.__getattr__ (Tomeu Vizoso)
+ - Change __dir__() to report all the attributes that __getattr__ supports (Laszlo Pandy)
+ - Bump the minimum gio dependency (Emilio Pozuelo Monfort)
+ - Add test for incorrect attributes in Gdk.Event (Tomeu Vizoso)
+ - Don't call getattr again in gi.overrides.Gdk.Event.__getattr__ (Simon van der Linden)
+ - Release allocated array of arguments when handling closures (Mike Gorse)
+ - Release GIValueInfo when checking an enum argument (Mike Gorse)
+ - Respect different type lengths when assigning out-argument pointers. (Eitan Isaacson)
+ - Fix stupid name clash (Tomeu Vizoso)
+ - Add /usr/share to XDG_DATA_DIRS when running the tests (Tomeu Vizoso)
+ - Comment out tests that require SRV lookups (Tomeu Vizoso)
+ - Use suppresion file when running valgrind (Tomeu Vizoso)
+ - Fix warnings. (Ignacio Casal Quinteiro)
+ - Allow comparing Gtk.TreePath to None (Jesse van den Kieboom)
+ - handle unicode objects in properties (John (J5) Palmieri)
+ - dsextras.py: check if gcc is there when platform is win32 and compiler is mingw32 (Dieter Verfaillie)
+ - dsextras.py: be consistent in how distutils imports are done (Dieter Verfaillie)
+ - dsextras.py: add have_gcc() function (Dieter Verfaillie)
+ - dsextras.py: use distutils.spawn.find_executable for have_pkgconfig() (Dieter Verfaillie)
+ - setup.py: fix another case of use True/False instead of 1/0 (Dieter Verfaillie)
+ - pygi-convert.sh: improve GtkSourceView conversion (Paolo Borelli)
+ - pygi-convert.sh: Gtk.DialogFlags conversion (Paolo Borelli)
+ - Doc Extractor: Print the gtk-doc blocks sorted by function name. (José Alburquerque)
+ - pygi-convert.sh: add more Gtk conversions and sort (Paolo Borelli)
+ - pygi-convert.sh: convert Atk (Paolo Borelli)
+ - pygi-convert.sh: convert a few more Gio types (Paolo Borelli)
+ - pygi-convert.sh: more GLib conversion (Paolo Borelli)
+ - pygi-convert.sh: remove two cases handled by overrides (Paolo Borelli)
+ - Override Gtk.ScrolledWindow constructor (Paolo Borelli)
+ - pygi-convert.sh: Fix 'find' syntax (Paolo Borelli)
+ - pygi-convert.sh: start handling Gio and GLib (Paolo Borelli)
+ - pygi-convert.sh: convert Gdk.ScrollDirection. (Paolo Borelli)
+ - Override Pango.Layout constructor. (Paolo Borelli)
+ - Remove Pango.FontDescription() conversion. (Paolo Borelli)
+ - Override GtkAction and GtkRadioAction constructors. (Paolo Borelli)
+ - Override Adjustment constructor to behave like pygtk (Dmitrijs Ledkovs)
+ - add secondary_text apis to MessageDialog (John (J5) Palmieri)
+ - [gi] get rid of some debug prints and fix error messages (John (J5) Palmieri)
+ - Fix demo for override changes. (Paolo Borelli)
+ - Override Pango.FontDescription. (Paolo Borelli)
+ - Stop checking that all vfuncs are implemented (Tomeu Vizoso)
+ - Fix usage of TreeIter api that is now an override. (Paolo Borelli)
+ - Fix Gtk.Label(label="Foo") (Paolo Borelli)
+ - Fix typo when raising an exception (Paolo Borelli)
+ - pygi-convert.sh: Added more conversions (Sebastian Pölsterl)
+ - Override LinkButton constructor to make 'uri' mandatory (Paolo Borelli)
+ - Container should be iterable. (Dmitry Morozov)
+ - No need to import Gdk (Paolo Borelli)
+ - Remove semicolumns (Paolo Borelli)
+ - [gi] make sure Gtk.Button override passes all keywords to parent constructor (John (J5) Palmieri)
+ - Fix cut&paste error in the Label override (Paolo Borelli)
+ - pygi-convert.sh: handle TextWindowType (Paolo Borelli)
+ - Override Label constructor to behave like pygtk (Paolo Borelli)
+ - Override GtkTable constructor to behave like pygtk (Paolo Borelli)
+ - pygi-convert.sh: convert MovementStep (Paolo Borelli)
+ - Update Gdk overrides to work with latest Gtk+ 3 (Paolo Borelli)
+ - Gtk: add an override for Gtk.main_quit (Johan Dahlin)
+ - [gi] handle subtypes when inserting into tree models (John (J5) Palmieri)
+ - Override TreeSelection.select_path and TreeView.scroll_to_cell (Paolo Borelli)
+ - Override TreePath.__new__ (Paolo Borelli)
+ - Override Container to behave like a sequence (Paolo Borelli)
+ - refactor Jonathan Matthew recurse vfunc patch so it applys and clean up a bit (John (J5) Palmieri)
+ - Recurse up through base classes when setting up vfuncs (Jonathan Matthew)
+ - add a profiling torture test for when we fix up invoke (John (J5) Palmieri)
+ - moved dynamic and base modules outside of gtk-2.0 directory (John (J5) Palmieri)
+ - add test for inout argument count (John (J5) Palmieri)
+ - [gi] add check for UNICHAR (John (J5) Palmieri)
+ - Support gunichar (Paolo Borelli)
+ - pygi-convert.sh: gtk.accel_map -> Gtk.AccelMap._ (Paolo Borelli)
+ - pygi-convert.sh: handle "from gtk import gdk" (Paolo Borelli)
+ - pygi-convert.sh: add some Pango special cases (Paolo Borelli)
+ - Override TextIter (begins|ends|toggles)_tag() (Paolo Borelli)
+ - Override TextBuffer.set_text() to make length optional (Paolo Borelli)
+ - Override TextBuffer.create_mark() (Paolo Borelli)
+ - Fix TextBuffer.get_selection_bounds() override (Paolo Borelli)
+ - [gi] fix ActionGroup constructor to allow other keyword properties to be set (John (J5) Palmieri)
+ - [gi] require the name parameter when creatin a Gtk.ActionGroup (John (J5) Palmieri)
+ - Override UIManager.insert_action_group (Paolo Borelli)
+ - Override TreeModel.get() to return a tuple (Paolo Borelli)
+ - Make TreeSelection.get_selected_rows compatible with PyGtk (Paolo Borelli)
+ - [gi] switch to using sequences/tuples when marshalling cairo_rectangle_int_t (John (J5) Palmieri)
+ - [gi] overrides for treeview Drag and Drop (John (J5) Palmieri)
+ - [gi] when encountering guint8 arrays treat them as byte arrays (John (J5) Palmieri)
+ - pygi-convert.sh: Add pynotify -> Notify (Martin Pitt)
+ - pygi-convert.sh: Remove sugar specifics, and allow command line file list (Martin Pitt)
+ - pygi-convert.sh: Cover Message and Buttons types (Martin Pitt)
+ - [gi] fix actiongroup test since actions are hashed (John (J5) Palmieri)
+ - [gi] when converting to UTF-8 accept Python Unicode objects as input (Python 2) (John (J5) Palmieri)
+ - Correct a bug in the freeing of memory in pygi-invoke.c. (Damien Caliste)
+ - update news for release (John (J5) Palmieri)
+ - Implement richcompare for GIBaseInfo (Jonathan Matthew)
+ - [gi] add the rectangle_int_t forign cairo type (John (J5) Palmieri)
+ - add a foreign type for cairo_rectangle_int_t and allow it to be caller-allocated (John (J5) Palmieri)
+ - [gi] add overrides to Gtk.Editable (John (J5) Palmieri)
+ - [gi] handle virtual invokers (John (J5) Palmieri)
+ - add overrides for the insert* apis of list_store and tree_store (John (J5) Palmieri)
+ - fix dialogs overrides which were relying on broken inheritance behavior (John (J5) Palmieri)
+ - Add a overrides registry so we can refrence overrides inside the module (John (J5) Palmieri)
+ - Merge remote branch 'dieterv/setup-fixes-for-merge' (John Stowers)
+ - setup.py: ease maintenance burden for tests installation (Dieter Verfaillie)
+ - fix inheritence issues in overrides (John (J5) Palmieri)
+ - tests: add runtests-windows.py script (Dieter Verfaillie)
+ - pygobject_postinstall.py: remove pygobject-2.0.pc treatment from postinstall as pkg-config on windows figures out the correct prefix at runtime (Dieter Verfaillie)
+ - pygobject_postinstall.py: remove shortcut creation (Dieter Verfaillie)
+ - setup.py: formatting cleanup, makes things readable (Dieter Verfaillie)
+ - setup.py: build and install tests (Dieter Verfaillie)
+ - setup.py: install documentation when available on build system (Dieter Verfaillie)
+ - setup.py: install pygobject-codegen script (Dieter Verfaillie)
+ - setup.py: install fixxref.py script (Dieter Verfaillie)
+ - setup.py: rearrange constants (Dieter Verfaillie)
+ - setup.py: check python version and pkgconig availability before anything else (Dieter Verfaillie)
+ - setup.py: simplify sys.platform != 'win32' detection and error reporting (Dieter Verfaillie)
+ - setup.py: rearrange imports (Dieter Verfaillie)
+ - README.win32: update build instructions (Dieter Verfaillie)
+ - dsextras.py: formatting cleanup, makes things readable (Dieter Verfaillie)
+ - dsextras.py: add ggc4 to MSVC compatible struct packing comment (Dieter Verfaillie)
+ - dsextras.py: use the pkgc_ functions instead of repeating pgk-config incantations all over the place (Dieter Verfaillie)
+ - dsextras.py: add pkgc_get_version and pkgc_get_defs_dir functions (Dieter Verfaillie)
+ - dsextras.py: PEP8: Comparisons to singletons like None should always be done with 'is' or 'is not', never the equality operators. (Dieter Verfaillie)
+ - dsextras.py: use True/False instead of 1/0 (Dieter Verfaillie)
+ - dsextras.py: rearrange imports (Dieter Verfaillie)
+ - Add distutils generated build/dist directories and eclipse configuration files to .gitignore (Dieter Verfaillie)
+ - [gi] add tests for calling dir on a dynamic module (John (J5) Palmieri)
+ - [gi] dir() now works for modules (Deepankar Sharma)
+ - Don't check the inner type when comparing gpointers (Simón Pena)
+ - Release GIL when calling into C functions (John (J5) Palmieri)
+ - _gi.Repository : Implement missing info bindings. (José Aliste)
+ - include Python.h so that PY_VERSION_HEX gets defined (John (J5) Palmieri)
+ - [gi] make overrides work for python 3.x protocols and alias for python 2.x (John (J5) Palmieri)
+ - Override Gtk.Widget.translate_coordinates to not return success value (Sebastian Pölsterl)
+ - Override Gtk.TreeViewColumn.cell_get_position to not return success value (Sebastian Pölsterl)
+ - Override get_path_at_pos and get_dest_row_at_pos of Gtk.TreeView to not return success value (Sebastian Pölsterl)
+ - Override Gtk.TreeSortable.get_sort_column_id to not return success value (Sebastian Pölsterl)
+ - Override forward_search and backward_search of Gtk.TextIter to not return success value (Sebastian Pölsterl)
+ - Override Gtk.TextBuffer.get_selection_bounds to not return success value (Sebastian Pölsterl)
+ - Override Gtk.RecentInfo.get_application_info to not return success value (Sebastian Pölsterl)
+ - Override Gtk.IMContext.get_surrounding to not return success value (Sebastian Pölsterl)
+ - Override get_item_at_pos, get_visible_range, get_dest_item_at_pos of Gtk.IconView to not return success value (Sebastian Pölsterl)
+ - Override Gtk.Container.get_focus_chain to not return success value (Sebastian Pölsterl)
+ - Override Gtk.ComboBox.get_active_iter to not return success value (Sebastian Pölsterl)
+ - [gi] make parameter check less strict when dealing with GValue params (John (J5) Palmieri)
+ - Shortcut removal is not needed on post-uninstall (John Stowers)
+ - Disable shortcut creation in windows installer (John Stowers)
+ - overrides for all subclasses of dialog (John (J5) Palmieri)
+ - Make TreeModel behave like in GTK-2.x (Sebastian Pölsterl)
+ - Correctly build GIO on windows (John Stowers)
+ - Require Python >= 2.6.0 for Windows build (John Stowers)
+ - Fix depreciation warning in dsextras.py (John Stowers)
+ - Fix build on windows (John Stowers)
+ - Support for GCC4 in Windows distutils build - bug 626548 (Michael Culbertson)
+ - Remove obsolete comments in dsextras.py (John Stowers)
+ - Broken dsextras.py pkg-config check error message (John Stowers)
+ - add compat functions for the deprecated PyCObject api (John (J5) Palmieri)
+ - Add __path__ attributes. (Damien Caliste)
+ - Override Gtk.TreeSelection.get_selected to not return success value. (Sebastian Pölsterl)
+ - Make row optional in Gtk.TreeStore/ListStore.append override (Vincent Untz)
+ - Revert "add compat functions for the deprecated PyCObject api" (John (J5) Palmieri)
+ - return NULL instead of -1 which fixes crash when introspection is turned off (John (J5) Palmieri)
+ - add compat functions for the deprecated PyCObject api (John (J5) Palmieri)
+ - fix commit 7fe83108 which didn't use the compat functions for string handling (John (J5) Palmieri)
+ - Python 3 fixes for dsextras and the python.m4 distribution files (John (J5) Palmieri)
+
+2.27.0 10-Nov-2010
+ - Implement richcompare for GIBaseInfo (Jonathan Matthew)
+ - [gi] add the rectangle_int_t forign cairo type (John (J5) Palmieri)
+ - add a foreign type for cairo_rectangle_int_t and allow it to be caller-allocated (John (J5) Palmieri)
+ - [gi] add overrides to Gtk.Editable (John (J5) Palmieri)
+ - [gi] handle virtual invokers (John (J5) Palmieri)
+ - add overrides for the insert* apis of list_store and tree_store (John (J5) Palmieri)
+ - fix dialogs overrides which were relying on broken inheritance behavior (John (J5) Palmieri)
+ - Add a overrides registry so we can refrence overrides inside the module (John (J5) Palmieri)
+ - Merge remote branch 'dieterv/setup-fixes-for-merge' (John Stowers)
+ - setup.py: ease maintenance burden for tests installation (Dieter Verfaillie)
+ - fix inheritence issues in overrides (John (J5) Palmieri)
+ - tests: add runtests-windows.py script (Dieter Verfaillie)
+ - pygobject_postinstall.py: remove pygobject-2.0.pc treatment from postinstall as pkg-config on windows figures out the correct prefix at runtime (Dieter Verfaillie)
+ - pygobject_postinstall.py: remove shortcut creation (Dieter Verfaillie)
+ - setup.py: formatting cleanup, makes things readable (Dieter Verfaillie)
+ - setup.py: build and install tests (Dieter Verfaillie)
+ - setup.py: install documentation when available on build system (Dieter Verfaillie)
+ - setup.py: install pygobject-codegen script (Dieter Verfaillie)
+ - setup.py: install fixxref.py script (Dieter Verfaillie)
+ - setup.py: rearrange constants (Dieter Verfaillie)
+ - setup.py: check python version and pkgconig availability before anything else (Dieter Verfaillie)
+ - setup.py: simplify sys.platform != 'win32' detection and error reporting (Dieter Verfaillie)
+ - setup.py: rearrange imports (Dieter Verfaillie)
+ - README.win32: update build instructions (Dieter Verfaillie)
+ - dsextras.py: formatting cleanup, makes things readable (Dieter Verfaillie)
+ - dsextras.py: add ggc4 to MSVC compatible struct packing comment (Dieter Verfaillie)
+ - dsextras.py: use the pkgc_ functions instead of repeating pgk-config incantations all over the place (Dieter Verfaillie)
+ - dsextras.py: add pkgc_get_version and pkgc_get_defs_dir functions (Dieter Verfaillie)
+ - dsextras.py: PEP8: Comparisons to singletons like None should always be done with 'is' or 'is not', never the equality operators. (Dieter Verfaillie)
+ - dsextras.py: use True/False instead of 1/0 (Dieter Verfaillie)
+ - dsextras.py: rearrange imports (Dieter Verfaillie)
+ - Add distutils generated build/dist directories and eclipse configuration files to .gitignore (Dieter Verfaillie)
+ - [gi] add tests for calling dir on a dynamic module (John (J5) Palmieri)
+ - [gi] dir() now works for modules (Deepankar Sharma)
+ - Don't check the inner type when comparing gpointers (Simón Pena)
+ - Release GIL when calling into C functions (John (J5) Palmieri)
+ - _gi.Repository : Implement missing info bindings. (José Aliste)
+ - include Python.h so that PY_VERSION_HEX gets defined (John (J5) Palmieri)
+ - [gi] make overrides work for python 3.x protocols and alias for python 2.x (John (J5) Palmieri)
+ - Override Gtk.Widget.translate_coordinates to not return success value (Sebastian Pölsterl)
+ - Override Gtk.TreeViewColumn.cell_get_position to not return success value (Sebastian Pölsterl)
+ - Override get_path_at_pos and get_dest_row_at_pos of Gtk.TreeView to not return success value (Sebastian Pölsterl)
+ - Override Gtk.TreeSortable.get_sort_column_id to not return success value (Sebastian Pölsterl)
+ - Override forward_search and backward_search of Gtk.TextIter to not return success value (Sebastian Pölsterl)
+ - Override Gtk.TextBuffer.get_selection_bounds to not return success value (Sebastian Pölsterl)
+ - Override Gtk.RecentInfo.get_application_info to not return success value (Sebastian Pölsterl)
+ - Override Gtk.IMContext.get_surrounding to not return success value (Sebastian Pölsterl)
+ - Override get_item_at_pos, get_visible_range, get_dest_item_at_pos of Gtk.IconView to not return success value (Sebastian Pölsterl)
+ - Override Gtk.Container.get_focus_chain to not return success value (Sebastian Pölsterl)
+ - Override Gtk.ComboBox.get_active_iter to not return success value (Sebastian Pölsterl)
+ - [gi] make parameter check less strict when dealing with GValue params (John (J5) Palmieri)
+ - Shortcut removal is not needed on post-uninstall (John Stowers)
+ - Disable shortcut creation in windows installer (John Stowers)
+ - overrides for all subclasses of dialog (John (J5) Palmieri)
+ - Make TreeModel behave like in GTK-2.x (Sebastian Pölsterl)
+ - Correctly build GIO on windows (John Stowers)
+ - Require Python >= 2.6.0 for Windows build (John Stowers)
+ - Fix depreciation warning in dsextras.py (John Stowers)
+ - Fix build on windows (John Stowers)
+ - Support for GCC4 in Windows distutils build - bug 626548 (Michael Culbertson)
+ - Remove obsolete comments in dsextras.py (John Stowers)
+ - Broken dsextras.py pkg-config check error message (John Stowers)
+ - add compat functions for the deprecated PyCObject api (John (J5) Palmieri)
+ - Add __path__ attributes. (Damien Caliste)
+ - Override Gtk.TreeSelection.get_selected to not return success value. (Sebastian Pölsterl)
+ - Make row optional in Gtk.TreeStore/ListStore.append override (Vincent Untz)
+ - Revert "add compat functions for the deprecated PyCObject api" (John (J5) Palmieri)
+ - return NULL instead of -1 which fixes crash when introspection is turned off (John (J5) Palmieri)
+ - add compat functions for the deprecated PyCObject api (John (J5) Palmieri)
+ - fix commit 7fe83108 which didn't use the compat functions for string handling (John (J5) Palmieri)
+ - Python 3 fixes for dsextras and the python.m4 distribution files (John (J5) Palmieri)
+
2.26.0 24-Sep-2010
- Wrap g_get_system_{config,data}_dirs () (John Strowers)
- fixed make check and make dist (John (J5) Palmieri)
diff --git a/README.win32 b/README.win32
index 548a3c8c..5819c8dd 100644
--- a/README.win32
+++ b/README.win32
@@ -4,21 +4,18 @@ Windows Setuptools Build Instructions
1. Install gtk+ bundle from gtk.org (to C:\GTK for example)
2. Install Python2.6
3. Install MinGW and MSYS
- 4. Add C:\GTK\bin to path (from windows)
- 5. Add the following to your msys environment variables
+ 4. Add the following to your msys environment variables
(open and append to C:\msys\1.0\etc\profile) or set for the session
- $ export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/c/GTK/lib/pkgconfig:/c/Python26/Lib/pkgconfig
- $ export PATH=$PATH:/c/Python26:/c/Python26/bin
+ $ export PATH=/c/GTK/bin:/c/Python26:/c/Python26/Scripts:$PATH
+ $ export PKG_CONFIG_PATH=/c/GTK/lib/pkgconfig:/c/Python26/Lib/pkgconfig:$PKG_CONFIG_PATH
- 6. In msys shell, build with
+ 5. In msys shell, build with
- $ python setup.py build --compiler=mingw32 --enable-threading \
- --yes-i-know-its-not-supported bdist_wininst
+ $ python setup.py build --compiler=mingw32 --enable-threading bdist_wininst --user-access-control=auto
Tested with
* gtk+-bundle_2.20.0-20100406_win32.zip
* MinGW-5.16.exe
* MSYS-1.0.11.exe
* python-2.6.5.exe
-
diff --git a/autogen.sh b/autogen.sh
index 4711ef33..6c3aa861 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -22,6 +22,9 @@ if libtoolize --version < /dev/null > /dev/null 2>&1 ; then
2.2*)
have_libtool=true
;;
+ 2.4*)
+ have_libtool=true
+ ;;
esac
fi
if $have_libtool ; then : ; else
diff --git a/codegen/.gitignore b/codegen/.gitignore
deleted file mode 100644
index f734b7ef..00000000
--- a/codegen/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-# Configuration-generated files.
-pygobject-codegen-2.0
diff --git a/codegen/Makefile.am b/codegen/Makefile.am
index cdb1e997..f9886eb5 100644
--- a/codegen/Makefile.am
+++ b/codegen/Makefile.am
@@ -29,3 +29,5 @@ codegen_PYTHON = \
reversewrapper.py
EXTRA_DIST = $(codegen_SCRIPTS) README.defs pygobject-codegen-$(PLATFORM_VERSION).in
+
+-include $(top_srcdir)/git.mk
diff --git a/codegen/argtypes.py b/codegen/argtypes.py
index b35f6ef9..46e868e2 100644
--- a/codegen/argtypes.py
+++ b/codegen/argtypes.py
@@ -79,14 +79,14 @@ class ArgType:
def write_param(self, ptype, pname, pdflt, pnull, info):
"""Add code to the WrapperInfo instance to handle
parameter."""
- raise RuntimeError, "write_param not implemented for %s" % \
- self.__class__.__name__
+ raise RuntimeError("write_param not implemented for %s"
+ % self.__class__.__name__)
def write_return(self, ptype, ownsreturn, info):
"""Adds a variable named ret of the return type to
info.varlist, and add any required code to info.codeafter to
convert the return value to a python object."""
- raise RuntimeError, "write_return not implemented for %s" % \
- self.__class__.__name__
+ raise RuntimeError("write_return not implemented for %s"
+ % self.__class__.__name__)
class NoneArg(ArgType):
def write_return(self, ptype, ownsreturn, info):
diff --git a/codegen/definitions.py b/codegen/definitions.py
index aca5adb2..bfb6faff 100644
--- a/codegen/definitions.py
+++ b/codegen/definitions.py
@@ -71,15 +71,15 @@ class Definition(object):
def __init__(self, *args):
"""Create a new defs object of this type. The arguments are the
components of the definition"""
- raise RuntimeError, "this is an abstract class"
+ raise RuntimeError("this is an abstract class")
def merge(self, old):
"""Merge in customisations from older version of definition"""
- raise RuntimeError, "this is an abstract class"
+ raise RuntimeError("this is an abstract class")
def write_defs(self, fp=sys.stdout):
"""write out this definition in defs file format"""
- raise RuntimeError, "this is an abstract class"
+ raise RuntimeError("this is an abstract class")
def guess_return_value_ownership(self):
"return 1 if caller owns return value"
@@ -407,7 +407,7 @@ class MethodDef(MethodDefBase):
for item in ('c_name', 'of_object'):
if self.__dict__[item] == None:
self.write_defs(sys.stderr)
- raise RuntimeError, "definition missing required %s" % (item,)
+ raise RuntimeError("definition missing required %s" % (item,))
def write_defs(self, fp=sys.stdout):
fp.write('(define-method ' + self.name + '\n')
@@ -491,7 +491,7 @@ class FunctionDef(Definition):
for item in ('c_name',):
if self.__dict__[item] == None:
self.write_defs(sys.stderr)
- raise RuntimeError, "definition missing required %s" % (item,)
+ raise RuntimeError("definition missing required %s" % (item,))
_method_write_defs = MethodDef.__dict__['write_defs']
@@ -513,8 +513,8 @@ class FunctionDef(Definition):
else:
param.merge(old_param)
return param
- raise RuntimeError, "could not find %s in old_parameters %r" % (
- param.pname, [p.pname for p in old.params])
+ raise RuntimeError("could not find %s in old_parameters %r" % (
+ param.pname, [p.pname for p in old.params]))
try:
self.params = map(merge_param, self.params)
except RuntimeError:
diff --git a/codegen/docextract_to_xml.py b/codegen/docextract_to_xml.py
index 7ac10531..f41c839e 100755
--- a/codegen/docextract_to_xml.py
+++ b/codegen/docextract_to_xml.py
@@ -89,7 +89,7 @@ if __name__ == '__main__':
print "<root>"
- for name, value in docs.items():
+ for name, value in sorted(docs.items()):
# Get the type of comment block ('function', 'signal' or
# 'property') (the value is a GtkDoc).
block_type = value.get_type()
diff --git a/configure.ac b/configure.ac
index 290c4de9..ce60c13e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -11,15 +11,15 @@ m4_define(python3_min_ver, 3.1)
dnl the pygobject version number
m4_define(pygobject_major_version, 2)
-m4_define(pygobject_minor_version, 26)
-m4_define(pygobject_micro_version, 0)
+m4_define(pygobject_minor_version, 28)
+m4_define(pygobject_micro_version, 2)
m4_define(pygobject_version, pygobject_major_version.pygobject_minor_version.pygobject_micro_version)
dnl versions of packages we require ...
-m4_define(introspection_required_version, 0.9.5)
-m4_define(pycairo_required_version, 1.0.2)
-m4_define(glib_required_version, 2.22.4)
-m4_define(gio_required_version, 2.22.4)
+m4_define(introspection_required_version, 0.10.2)
+m4_define(pycairo_required_version, 1.2.0)
+m4_define(glib_required_version, 2.24.0)
+m4_define(gio_required_version, 2.24.0)
m4_define(giounix_required_version, 2.22.4)
AC_INIT(pygobject, pygobject_version,
@@ -101,15 +101,9 @@ if test $build_py3k = true ; then
[AC_MSG_ERROR(too old)])
fi
-JD_CHECK_PYTHON_HEADERS(,[AC_MSG_ERROR(could not find Python headers)])
+AM_CHECK_PYTHON_HEADERS(,[AC_MSG_ERROR(could not find Python headers)])
AC_MSG_CHECKING([for PySignal_SetWakeupFd in Python.h])
-py_prefix=`$PYTHON -c "import sys; sys.stdout.write(sys.prefix)"`
-if test -x "$PYTHON-config"; then
-PYTHON_INCLUDES=`$PYTHON-config --includes 2>/dev/null`
-else
-PYTHON_INCLUDES="-I${py_prefix}/include/python${PYTHON_VERSION}"
-fi
old_CPPFLAGS=$CPPFLAGS
CPPFLAGS="-Wall -Werror $PYTHON_INCLUDES"
AC_TRY_COMPILE([#include <Python.h>],
@@ -152,7 +146,11 @@ fi
AM_CONDITIONAL(ENABLE_DOCS, test x$enable_docs != xno)
-AM_CHECK_PYMOD(thread,,,enable_thread=no)
+AM_CHECK_PYMOD(thread,,,enable_thread=check_for_py3_module)
+if test "x$enable_thread" = xcheck_for_py3_module; then
+ dnl Python 3 uses the _thread module so check for that
+ AM_CHECK_PYMOD(_thread,,,enable_thread=no)
+fi
AC_MSG_CHECKING(whether to enable threading in pygobject)
if test "x$enable_thread" != xno; then
diff --git a/demos/gtk-demo/demos/Entry/search_entry.py b/demos/gtk-demo/demos/Entry/search_entry.py
new file mode 100644
index 00000000..bf290f40
--- /dev/null
+++ b/demos/gtk-demo/demos/Entry/search_entry.py
@@ -0,0 +1,252 @@
+#!/usr/bin/env python
+# -*- Mode: Python; py-indent-offset: 4 -*-
+# vim: tabstop=4 shiftwidth=4 expandtab
+#
+# Copyright (C) 2010 Red Hat, Inc., John (J5) Palmieri <johnp@redhat.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
+# USA
+
+title = "Search Entry"
+description = """
+GtkEntry allows to display icons and progress information. This demo shows how to use these features in a search entry.
+ """
+
+from gi.repository import Gtk, Gdk, GdkPixbuf, GLib, GObject
+
+(PIXBUF_COL,
+ TEXT_COL) = range(2)
+
+class SearchboxApp:
+ def __init__(self, demoapp):
+ self.demoapp = demoapp
+
+ self.window = Gtk.Dialog('Search Entry',
+ buttons=(Gtk.STOCK_CLOSE, Gtk.ResponseType.NONE))
+
+ self.window.connect('response', lambda x, y: self.window.destroy())
+ self.window.connect('destroy', Gtk.main_quit)
+
+ content_area = self.window.get_content_area()
+
+ vbox = Gtk.VBox(spacing=5)
+ content_area.pack_start(vbox, True, True, 0)
+ vbox.set_border_width(5)
+
+ label = Gtk.Label()
+ label.set_markup('Search entry demo')
+ vbox.pack_start(label, False, False, 0)
+
+ hbox = Gtk.HBox(homogeneous=False, spacing=10)
+ hbox.set_border_width(0)
+ vbox.pack_start(hbox, True, True, 0)
+
+ # Create our entry
+ entry = Gtk.Entry()
+ hbox.pack_start(entry, False, False, 0)
+
+ # Create the find and cancel buttons
+ notebook = Gtk.Notebook()
+ self.notebook = notebook
+ notebook.set_show_tabs(False)
+ notebook.set_show_border(False)
+ hbox.pack_start(notebook, False, False, 0)
+
+ find_button = Gtk.Button('Find')
+ find_button.connect('clicked', self.start_search, entry)
+ notebook.append_page(find_button, None)
+ find_button.show()
+
+ cancel_button = Gtk.Button('Cancel')
+ cancel_button.connect('clicked', self.stop_search, entry)
+ notebook.append_page(cancel_button, None)
+ cancel_button.show()
+
+ # Set up the search icon
+ self.search_by_name(None, entry)
+
+ # Set up the clear icon
+ entry.set_icon_from_stock(Gtk.EntryIconPosition.SECONDARY,
+ Gtk.STOCK_CLEAR)
+ self.text_changed_cb(entry, None, find_button)
+
+ entry.connect('notify::text', self.text_changed_cb, find_button)
+
+ entry.connect('activate', self.activate_cb)
+
+ # Create the menu
+ menu = self.create_search_menu(entry)
+ entry.connect('icon-press', self.icon_press_cb, menu)
+
+ # FIXME: this should take None for the detach callback
+ # but our callback implementation does not allow
+ # it yet, so we pass in a noop callback
+ menu.attach_to_widget(entry, self.detach)
+
+ # add accessible alternatives for icon functionality
+ entry.connect('populate-popup', self.entry_populate_popup)
+
+ self.window.show_all()
+
+ def detach(self, *args):
+ pass
+
+ def show_find_button(self):
+ self.notebook.set_current_page(0)
+
+ def show_cancel_button(self):
+ self.notebook.set_current_page(1)
+
+ def search_progress(self, entry):
+ entry.progress_pulse()
+ return True
+
+ def search_progress_done (self, entry):
+ entry.set_progress_fraction(0.0)
+
+ def finish_search(self, button, entry):
+ self.show_find_button()
+ GObject.source_remove(self.search_progress_id)
+ self.search_progress_done(entry)
+ self.search_progress_id = 0
+
+ return False
+
+ def start_search_feedback(self, entry):
+ self.search_progress_id = GObject.timeout_add(100,
+ self.search_progress,
+ entry)
+
+ return False
+
+ def start_search(self, button, entry):
+ self.show_cancel_button()
+ self.search_progress_id = GObject.timeout_add_seconds(1,
+ self.start_search_feedback,
+ entry)
+ self.finish_search_id = GObject.timeout_add_seconds(15,
+ self.finish_search,
+ button)
+
+ def stop_search(self, button, entry):
+ GObject.source_remove(self.finish_search_id)
+ self.finish_search(button, entry)
+
+ def clear_entry_swapped(self, widget, entry):
+ self.clear_entry(entry)
+
+ def clear_entry(self, entry):
+ entry.set_text('')
+
+ def search_by_name (self, item, entry):
+ entry.set_icon_from_stock(Gtk.EntryIconPosition.PRIMARY,
+ Gtk.STOCK_FIND)
+ entry.set_icon_tooltip_text(Gtk.EntryIconPosition.PRIMARY,
+ 'Search by name\n' + \
+ 'Click here to change the search type')
+
+ def search_by_description(self, item, entry):
+ entry.set_icon_from_stock (Gtk.EntryIconPosition.PRIMARY,
+ Gtk.STOCK_EDIT)
+ entry.set_icon_tooltip_text(Gtk.EntryIconPosition.PRIMARY,
+ 'Search by description\n' + \
+ 'Click here to change the search type')
+
+ def search_by_file(self, item, entry):
+ entry.set_icon_from_stock(Gtk.EntryIconPosition.PRIMARY,
+ Gtk.STOCK_OPEN)
+ entry.set_icon_tooltip_text(Gtk.EntryIconPosition.PRIMARY,
+ 'Search by file name\n' + \
+ 'Click here to change the search type')
+
+ def create_search_menu(self, entry):
+ menu = Gtk.Menu()
+
+ item = Gtk.ImageMenuItem.new_with_mnemonic('Search by _name')
+ image = Gtk.Image.new_from_stock(Gtk.STOCK_FIND, Gtk.IconSize.MENU)
+ item.set_image(image)
+ item.set_always_show_image(True)
+ item.connect('activate', self.search_by_name, entry)
+ menu.append(item)
+
+ item = Gtk.ImageMenuItem.new_with_mnemonic('Search by _description')
+ image = Gtk.Image.new_from_stock(Gtk.STOCK_EDIT, Gtk.IconSize.MENU)
+ item.set_image(image)
+ item.set_always_show_image(True)
+ item.connect('activate', self.search_by_description, entry)
+ menu.append(item)
+
+ item = Gtk.ImageMenuItem.new_with_mnemonic('Search by _file name')
+ image = Gtk.Image.new_from_stock(Gtk.STOCK_OPEN, Gtk.IconSize.MENU)
+ item.set_image(image)
+ item.set_always_show_image(True)
+ item.connect('activate', self.search_by_name, entry)
+ menu.append(item)
+
+ menu.show_all()
+
+ return menu
+
+
+ def icon_press_cb(self, entry, position, event, menu):
+ if position == Gtk.EntryIconPosition.PRIMARY:
+ menu.popup(None, None, None, None,
+ event.button, event.time)
+ else:
+ self.clear_entry(entry)
+
+ def text_changed_cb(self, entry, pspec, button):
+ has_text = entry.get_text_length() > 0
+ entry.set_icon_sensitive(Gtk.EntryIconPosition.SECONDARY, has_text)
+ button.set_sensitive(has_text)
+
+ def activate_cb (self, entry, button):
+ if self.search_progress_id != 0:
+ return
+ self.start_search(button, entry)
+
+ def search_entry_destroyed(self, widget):
+ if finish_search_id != 0:
+ GObject.source_remove(finish_search_id)
+ if search_progress_id != 0:
+ GObject.source_remove(search_progress_id)
+
+ self.window = None
+
+ def entry_populate_popup(self, entry, menu):
+ has_text = entry.get_text_length() > 0
+
+ item = Gtk.SeparatorMenuItem()
+ item.show()
+ menu.append(item)
+
+ item = Gtk.MenuItem.new_with_mnemonic("C_lear")
+ item.show()
+ item.connect('activate', self.clear_entry_swapped, entry)
+ menu.append(item)
+ item.set_sensitive(has_text)
+
+ search_menu = self.create_search_menu(entry)
+ item = Gtk.MenuItem.new_with_label('Search by')
+ item.show()
+ item.set_submenu(search_menu)
+ menu.append (item)
+
+def main(demoapp=None):
+ app = SearchboxApp(demoapp)
+ Gtk.main()
+
+if __name__ == '__main__':
+ main()
diff --git a/demos/gtk-demo/demos/Icon View/__init__.py b/demos/gtk-demo/demos/Icon View/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/demos/gtk-demo/demos/Icon View/__init__.py
diff --git a/demos/gtk-demo/demos/Icon View/iconviewbasics.py b/demos/gtk-demo/demos/Icon View/iconviewbasics.py
new file mode 100644
index 00000000..e2cf45ce
--- /dev/null
+++ b/demos/gtk-demo/demos/Icon View/iconviewbasics.py
@@ -0,0 +1,218 @@
+#!/usr/bin/env python
+# -*- Mode: Python; py-indent-offset: 4 -*-
+# vim: tabstop=4 shiftwidth=4 expandtab
+#
+# Copyright (C) 2010 Red Hat, Inc., John (J5) Palmieri <johnp@redhat.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
+# USA
+
+title = "Icon View Basics"
+description = """
+The GtkIconView widget is used to display and manipulate icons. It uses a GtkTreeModel for data storage, so the list store example might be helpful.
+We also use the Gio.File API to get the icons for each file type.
+ """
+
+import os
+from gi.repository import GLib, Gtk, Gio, GdkPixbuf
+import glib
+
+class IconViewApp:
+ (COL_PATH,
+ COL_DISPLAY_NAME,
+ COL_PIXBUF,
+ COL_IS_DIRECTORY,
+ NUM_COLS) = range(5)
+
+ def __init__(self, demoapp):
+ self.pixbuf_lookup = {}
+
+ self.demoapp = demoapp
+
+ self.window = Gtk.Window()
+ self.window.set_title('Gtk.IconView demo')
+ self.window.set_default_size(650, 400)
+ self.window.connect('destroy', Gtk.main_quit)
+
+ vbox = Gtk.VBox()
+ self.window.add(vbox)
+
+ tool_bar = Gtk.Toolbar()
+ vbox.pack_start(tool_bar, False, False, 0)
+
+ up_button = Gtk.ToolButton(stock_id=Gtk.STOCK_GO_UP)
+ up_button.set_is_important(True)
+ up_button.set_sensitive(False)
+ tool_bar.insert(up_button, -1)
+
+ home_button = Gtk.ToolButton(stock_id=Gtk.STOCK_HOME)
+ home_button.set_is_important(True)
+ tool_bar.insert(home_button, -1)
+
+ sw = Gtk.ScrolledWindow()
+ sw.set_shadow_type(Gtk.ShadowType.ETCHED_IN)
+ sw.set_policy(Gtk.PolicyType.AUTOMATIC,
+ Gtk.PolicyType.AUTOMATIC)
+
+ vbox.pack_start(sw, True, True, 0)
+
+ # create the store and fill it with content
+ self.parent_dir = '/'
+ store = self.create_store()
+ self.fill_store(store)
+
+ icon_view = Gtk.IconView(model=store)
+ icon_view.set_selection_mode(Gtk.SelectionMode.MULTIPLE)
+ sw.add(icon_view)
+
+ # connect to the 'clicked' signal of the "Up" tool button
+ up_button.connect('clicked', self.up_clicked, store)
+
+ # connect to the 'clicked' signal of the "home" tool button
+ home_button.connect('clicked', self.home_clicked, store)
+
+ self.up_button = up_button
+ self.home_button = home_button
+
+ # we now set which model columns that correspond to the text
+ # and pixbuf of each item
+ icon_view.set_text_column(self.COL_DISPLAY_NAME)
+ icon_view.set_pixbuf_column(self.COL_PIXBUF)
+
+ # connect to the "item-activated" signal
+ icon_view.connect('item-activated', self.item_activated, store)
+ icon_view.grab_focus()
+
+ self.window.show_all()
+
+ def sort_func(self, store, a_iter, b_iter, user_data):
+ (a_name, a_is_dir) = store.get(a_iter,
+ self.COL_DISPLAY_NAME,
+ self.COL_IS_DIRECTORY)
+
+ (b_name, b_is_dir) = store.get(b_iter,
+ self.COL_DISPLAY_NAME,
+ self.COL_IS_DIRECTORY)
+
+ if a_name is None:
+ a_name = ''
+
+ if b_name is None:
+ b_name = ''
+
+ if (not a_is_dir) and b_is_dir:
+ return 1
+ elif a_is_dir and (not b_is_dir):
+ return -1
+ elif a_name > b_name:
+ return 1
+ elif a_name < b_name:
+ return -1
+ else:
+ return 0
+
+ def up_clicked(self, item, store):
+ self.parent_dir = os.path.split(self.parent_dir)[0]
+ self.fill_store(store)
+ # de-sensitize the up button if we are at the root
+ self.up_button.set_sensitive(self.parent_dir != '/')
+
+ def home_clicked(self, item, store):
+ self.parent_dir = GLib.get_home_dir()
+ self.fill_store(store)
+
+ # Sensitize the up button
+ self.up_button.set_sensitive(True)
+
+ def item_activated(self, icon_view, tree_path, store):
+ iter_ = store.get_iter(tree_path)
+ (path, is_dir) = store.get(iter_, self.COL_PATH, self.COL_IS_DIRECTORY)
+ if not is_dir:
+ return
+
+ self.parent_dir = path
+ self.fill_store(store)
+
+ self.up_button.set_sensitive(True)
+
+ def create_store(self):
+ store = Gtk.ListStore(str, str, GdkPixbuf.Pixbuf, bool)
+
+ # set sort column and function
+ store.set_default_sort_func(self.sort_func)
+ store.set_sort_column_id(-1,
+ Gtk.SortType.ASCENDING)
+
+ return store
+
+ def file_to_icon_pixbuf(self, path):
+ pixbuf = None
+
+ # get the theme icon
+ f = Gio.file_new_for_path(path)
+ info = f.query_info(Gio.FILE_ATTRIBUTE_STANDARD_ICON,
+ Gio.FileQueryInfoFlags.NONE,
+ None)
+ gicon = info.get_icon()
+
+ # check to see if it is an image format we support
+ for format in GdkPixbuf.Pixbuf.get_formats():
+ for mime_type in format.get_mime_types():
+ content_type = Gio.content_type_from_mime_type(mime_type)
+ if content_type is not None:
+ break
+
+ format_gicon = Gio.content_type_get_icon(content_type)
+ if format_gicon.equal(gicon):
+ gicon = f.icon_new()
+ break
+
+ if gicon in self.pixbuf_lookup:
+ return self.pixbuf_lookup[gicon]
+
+ if isinstance(gicon, Gio.ThemedIcon):
+ names = gicon.get_names()
+ icon_theme = Gtk.IconTheme.get_default()
+ for name in names:
+ try:
+ pixbuf = icon_theme.load_icon(name, 64, 0)
+ break
+ except glib.GError:
+ pass
+
+ self.pixbuf_lookup[gicon] = pixbuf
+
+ elif isinstance(gicon, Gio.FileIcon):
+ icon_file = gicon.get_file()
+ path = icon_file.get_path()
+ pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(path, 72, 72)
+ self.pixbuf_lookup[gicon] = pixbuf
+
+ return pixbuf
+
+ def fill_store(self, store):
+ store.clear()
+ for name in os.listdir(self.parent_dir):
+ path = os.path.join(self.parent_dir, name)
+ is_dir = os.path.isdir(path)
+ pixbuf = self.file_to_icon_pixbuf(path)
+ store.append((path, name, pixbuf, is_dir))
+
+def main(demoapp=None):
+ app = IconViewApp(demoapp)
+ Gtk.main()
+
+if __name__ == '__main__':
+ main()
diff --git a/demos/gtk-demo/demos/Icon View/iconviewedit.py b/demos/gtk-demo/demos/Icon View/iconviewedit.py
new file mode 100644
index 00000000..9233b09b
--- /dev/null
+++ b/demos/gtk-demo/demos/Icon View/iconviewedit.py
@@ -0,0 +1,101 @@
+#!/usr/bin/env python
+# -*- Mode: Python; py-indent-offset: 4 -*-
+# vim: tabstop=4 shiftwidth=4 expandtab
+#
+# Copyright (C) 2010 Red Hat, Inc., John (J5) Palmieri <johnp@redhat.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
+# USA
+
+title = "Editing and Drag-and-Drop"
+description = """
+The GtkIconView widget supports Editing and Drag-and-Drop. This example also demonstrates using the generic GtkCellLayout interface to set up cell renderers in an icon view.
+"""
+
+from gi.repository import Gtk, Gdk, GdkPixbuf
+
+class IconviewEditApp:
+ COL_TEXT = 0
+ NUM_COLS = 1
+
+ def __init__(self):
+ self.window = Gtk.Window()
+ self.window.set_title('Editing and Drag-and-Drop')
+ self.window.set_border_width(8)
+ self.window.connect('destroy', Gtk.main_quit)
+
+ store = Gtk.ListStore(str)
+ colors = ['Red', 'Green', 'Blue', 'Yellow']
+ store.clear()
+ for c in colors:
+ store.append([c])
+
+ icon_view = Gtk.IconView(model=store)
+ icon_view.set_selection_mode(Gtk.SelectionMode.SINGLE)
+ icon_view.set_item_orientation(Gtk.Orientation.HORIZONTAL)
+ icon_view.set_columns(2)
+ icon_view.set_reorderable(True)
+
+ renderer = Gtk.CellRendererPixbuf()
+ icon_view.pack_start(renderer, True)
+ icon_view.set_cell_data_func(renderer,
+ self.set_cell_color,
+ None)
+
+ renderer = Gtk.CellRendererText()
+ icon_view.pack_start(renderer, True)
+ renderer.props.editable = True
+ renderer.connect('edited', self.edited, icon_view)
+ icon_view.add_attribute(renderer, 'text', self.COL_TEXT)
+
+ self.window.add(icon_view)
+
+ self.window.show_all()
+
+ def set_cell_color (self,
+ cell_layout,
+ cell,
+ tree_model,
+ iter_,
+ icon_view):
+
+ # FIXME return single element instead of tuple
+ text = tree_model.get(iter_, self.COL_TEXT)[0]
+ color = Gdk.color_parse(text)[1]
+ pixel = 0
+ if color is not None:
+ pixel = \
+ (color.red >> 8) << 24 | \
+ (color.green >> 8) << 16 | \
+ (color.blue >> 8) << 8
+
+ pixbuf = GdkPixbuf.Pixbuf.new(GdkPixbuf.Colorspace.RGB, False, 8, 24, 24)
+ pixbuf.fill(pixel)
+
+ cell.props.pixbuf = pixbuf
+
+ def edited (self, cell, path_string, text, icon_view):
+ model = icon_view.get_model()
+ path = Gtk.TreePath(path_string)
+
+ iter_ = model.get_iter(path)
+ model.set_row(iter_, [text])
+
+def main(demoapp=None):
+ app = IconviewEditApp()
+ Gtk.main()
+
+if __name__ == '__main__':
+ main()
diff --git a/demos/gtk-demo/demos/Tree View/__init__.py b/demos/gtk-demo/demos/Tree View/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/demos/gtk-demo/demos/Tree View/__init__.py
diff --git a/demos/gtk-demo/demos/Tree View/liststore.py b/demos/gtk-demo/demos/Tree View/liststore.py
new file mode 100644
index 00000000..0be862ef
--- /dev/null
+++ b/demos/gtk-demo/demos/Tree View/liststore.py
@@ -0,0 +1,205 @@
+#!/usr/bin/env python
+# -*- Mode: Python; py-indent-offset: 4 -*-
+# vim: tabstop=4 shiftwidth=4 expandtab
+#
+# Copyright (C) 2010 Red Hat, Inc., John (J5) Palmieri <johnp@redhat.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
+# USA
+
+title = "List Store"
+description = """
+The GtkListStore is used to store data in list form, to be used later on by a GtkTreeView to display it. This demo builds a simple GtkListStore and displays it. See the Stock Browser demo for a more advanced example.
+"""
+
+from gi.repository import Gtk, GObject
+import sys
+
+class Bug:
+ def __init__(self, is_fixed, number, severity, description):
+ self.is_fixed = is_fixed
+ self.number = number
+ self.severity = severity
+ self.description = description
+
+# initial data we use to fill in the store
+data = [Bug(False, 60482, "Normal", "scrollable notebooks and hidden tabs"),
+ Bug(False, 60620, "Critical", "gdk_window_clear_area (gdkwindow-win32.c) is not thread-safe"),
+ Bug(False, 50214, "Major", "Xft support does not clean up correctly"),
+ Bug(True, 52877, "Major", "GtkFileSelection needs a refresh method. "),
+ Bug(False, 56070, "Normal", "Can't click button after setting in sensitive"),
+ Bug(True, 56355, "Normal", "GtkLabel - Not all changes propagate correctly"),
+ Bug(False, 50055, "Normal", "Rework width/height computations for TreeView"),
+ Bug(False, 58278, "Normal", "gtk_dialog_set_response_sensitive () doesn't work"),
+ Bug(False, 55767, "Normal", "Getters for all setters"),
+ Bug(False, 56925, "Normal", "Gtkcalender size"),
+ Bug(False, 56221, "Normal", "Selectable label needs right-click copy menu"),
+ Bug(True, 50939, "Normal", "Add shift clicking to GtkTextView"),
+ Bug(False, 6112, "Enhancement","netscape-like collapsable toolbars"),
+ Bug(False, 1, "Normal", "First bug :=)")]
+
+class ListStoreApp:
+ (COLUMN_FIXED,
+ COLUMN_NUMBER,
+ COLUMN_SEVERITY,
+ COLUMN_DESCRIPTION,
+ COLUMN_PULSE,
+ COLUMN_ICON,
+ COLUMN_ACTIVE,
+ COLUMN_SENSITIVE,
+ NUM_COLUMNS) = range(9)
+
+ def __init__(self):
+ self.window = Gtk.Window()
+ self.window.set_title('Gtk.ListStore Demo')
+ self.window.connect('destroy', Gtk.main_quit)
+
+ vbox = Gtk.VBox(spacing=8)
+ self.window.add(vbox)
+
+ label = Gtk.Label('This is the bug list (note: not based on real data, it would be nice to have a nice ODBC interface to bugzilla or so, though).')
+ vbox.pack_start(label, False, False, 0)
+
+ sw = Gtk.ScrolledWindow()
+ sw.set_shadow_type(Gtk.ShadowType.ETCHED_IN)
+ sw.set_policy(Gtk.PolicyType.NEVER,
+ Gtk.PolicyType.AUTOMATIC)
+ vbox.pack_start(sw, True, True, 0)
+
+ self.create_model()
+ treeview = Gtk.TreeView(model=self.model)
+ treeview.set_rules_hint(True)
+ treeview.set_search_column(self.COLUMN_DESCRIPTION)
+ sw.add(treeview)
+
+ self.add_columns(treeview)
+
+ self.window.set_default_size(280, 250)
+ self.window.show_all()
+
+ self.window.connect('delete-event', self.window_closed)
+ self.timeout = GObject.timeout_add (80, self.spinner_timeout)
+
+ def window_closed(self, window, event):
+ if self.timeout != 0:
+ GObject.source_remove(self.timeout)
+
+ def spinner_timeout(self):
+ if self.model is None:
+ return False
+
+ iter_ = self.model.get_iter_first()
+ pulse = self.model.get(iter_, self.COLUMN_PULSE)[0]
+ if pulse == 999999999:
+ pulse = 0
+ else:
+ pulse += 1
+
+ self.model.set_value(iter_, self.COLUMN_PULSE, pulse)
+ self.model.set_value(iter_, self.COLUMN_ACTIVE, True)
+
+ return True
+
+ def create_model(self):
+ self.model = Gtk.ListStore(bool,
+ GObject.TYPE_INT,
+ str,
+ str,
+ GObject.TYPE_INT,
+ str,
+ bool,
+ bool)
+
+ col = 0
+ for bug in data:
+ if col == 1 or col == 3:
+ icon_name = 'battery-critical-charging-symbolic'
+ else:
+ icon_name = ''
+ if col == 3:
+ is_sensitive = False
+ else:
+ is_sensitive = True
+
+ self.model.append([bug.is_fixed,
+ bug.number,
+ bug.severity,
+ bug.description,
+ 0,
+ icon_name,
+ False,
+ is_sensitive])
+ col += 1
+
+ def add_columns(self, treeview):
+ model = treeview.get_model()
+
+ # column for is_fixed toggle
+ renderer = Gtk.CellRendererToggle()
+ renderer.connect('toggled', self.is_fixed_toggled, model)
+
+ column = Gtk.TreeViewColumn("Fixed?", renderer,
+ active=self.COLUMN_FIXED)
+ column.set_fixed_width(50)
+ column.set_sizing(Gtk.TreeViewColumnSizing.FIXED)
+ treeview.append_column(column)
+
+ # column for severities
+ renderer = Gtk.CellRendererText()
+ column = Gtk.TreeViewColumn("Severity", renderer,
+ text=self.COLUMN_SEVERITY)
+ column.set_sort_column_id(self.COLUMN_SEVERITY)
+ treeview.append_column(column)
+
+ # column for description
+ renderer = Gtk.CellRendererText()
+ column = Gtk.TreeViewColumn("Description", renderer,
+ text=self.COLUMN_DESCRIPTION)
+ column.set_sort_column_id(self.COLUMN_DESCRIPTION)
+ treeview.append_column(column)
+
+ # column for spinner
+ renderer = Gtk.CellRendererSpinner()
+ column = Gtk.TreeViewColumn("Spinning", renderer,
+ pulse=self.COLUMN_PULSE,
+ active=self.COLUMN_ACTIVE)
+ column.set_sort_column_id(self.COLUMN_PULSE)
+ treeview.append_column(column)
+
+ # column for symbolic icon
+ renderer = Gtk.CellRendererPixbuf()
+ renderer.props.follow_state = True
+ column = Gtk.TreeViewColumn("Symbolic icon", renderer,
+ icon_name=self.COLUMN_ICON,
+ sensitive=self.COLUMN_SENSITIVE)
+ column.set_sort_column_id(self.COLUMN_ICON)
+ treeview.append_column(column)
+
+ def is_fixed_toggled(self, cell, path_str, model):
+ # get toggled iter
+ iter_ = model.get_iter(path_str)
+ is_fixed = model.get_value(iter_, self.COLUMN_FIXED)
+
+ # do something with value
+ is_fixed ^= 1
+
+ model.set_value(iter_, self.COLUMN_FIXED, is_fixed)
+
+def main(demoapp=None):
+ app = ListStoreApp()
+ Gtk.main()
+
+if __name__ == '__main__':
+ main()
diff --git a/demos/gtk-demo/demos/appwindow.py b/demos/gtk-demo/demos/appwindow.py
index 8b7c99c3..a71720f9 100644
--- a/demos/gtk-demo/demos/appwindow.py
+++ b/demos/gtk-demo/demos/appwindow.py
@@ -24,11 +24,9 @@ description = """
Demonstrates a typical application window with menubar, toolbar, statusbar.
"""
-# See FIXME's
-is_fully_bound = False
-
from gi.repository import Gtk, GdkPixbuf, Gdk
import sys, os
+import glib
global infobar
global window
@@ -38,7 +36,7 @@ global _demoapp
def widget_destroy(widget, button):
widget.destroy()
-def activate_action(action):
+def activate_action(action, user_data):
global window
name = action.get_name()
@@ -50,16 +48,15 @@ def activate_action(action):
return
- dialog = Gtk.MessageDialog(message_type=Gtk.MessageType.INFO,
+ dialog = Gtk.MessageDialog(parent=window,
+ message_type=Gtk.MessageType.INFO,
buttons=Gtk.ButtonsType.CLOSE,
text='You activated action: "%s" of type %s' % (name, _type))
- # FIXME: this should be done in the constructor
- dialog.set_transient_for(window)
dialog.connect('response', widget_destroy)
dialog.show()
-def activate_radio_action(action, current):
+def activate_radio_action(action, current, user_data):
global infobar
global messagelabel
@@ -87,29 +84,7 @@ def update_statusbar(buffer, statusbar):
def mark_set_callback(buffer, new_location, mark, data):
update_statusbar(buffer, data)
-def update_resize_grip(widget, event, statusbar):
- # FIXME: for some reason we get attribute errors in handlers
- # if we try to access flags without first evaluating
- # the parent struct
- Gdk.WindowState
-
- # FIXME: event should be a Gdk.EventWindowState but is only a Gdk.Event
- if event.window_state.changed_mask and (Gdk.WindowState.MAXIMIZED or
- Gdk.WindowState.FULLSCREEN):
-
- maximized = event.window_state.new_window_state and (Gdk.WindowState.MAXIMIZED or
- Gdk.WindowState.FULLSCREEN)
- statusbar.set_has_resize_grip(not maximized)
-
-def activate_email(about, link, data):
- text = 'send mail to %s' % (link,)
- print text
-
-def activate_url(about, link, data):
- text = 'show url %s' % (link,)
- print text
-
-def about_cb(widget):
+def about_cb(widget, data):
global window
authors = ['John (J5) Palmieri',
@@ -137,13 +112,16 @@ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
"""
filename = os.path.join('data', 'gtk-logo-rgb.gif')
- pixbuf = GdkPixbuf.Pixbuf.new_from_file(filename)
+ try:
+ pixbuf = GdkPixbuf.Pixbuf.new_from_file(filename)
+ except glib.GError:
+ filename = os.path.join('demos', filename)
+ pixbuf = GdkPixbuf.Pixbuf.new_from_file(filename)
+
transparent = pixbuf.add_alpha(True, 0xff, 0xff, 0xff)
- Gtk.AboutDialog.set_email_hook(activate_email, None)
- Gtk.AboutDialog.set_url_hook(activate_url, None)
- # FIXME: override Gtk.show_about_dialog
- # make about dailog constructor take a parent argument
- about = Gtk.AboutDialog(program_name='GTK+ Code Demos',
+ about = Gtk.AboutDialog(
+ parent=window,
+ program_name='GTK+ Code Demos',
version='0.1',
copyright='(C) 2010 The PyGI Team',
license=license,
@@ -152,12 +130,10 @@ Boston, MA 02111-1307, USA.
authors=authors,
documenters=documentors,
logo=transparent,
- title='About GTK+ Code Demos')
+ title='About GTK+ Code Demos')
- about.set_transient_for(window)
about.connect('response', widget_destroy)
- about.show()
-
+ about.run()
action_entries = (
("FileMenu", None, "_File"), # name, stock id, label
@@ -337,8 +313,8 @@ def main(demoapp=None):
window.set_title('Application Window')
window.set_icon_name('gtk-open')
window.connect_after('destroy', _quit)
- table = Gtk.Table(n_rows=1,
- n_columns=5,
+ table = Gtk.Table(rows=1,
+ columns=5,
homogeneous=False)
window.add(table)
@@ -412,8 +388,6 @@ def main(demoapp=None):
buffer.connect('changed', update_statusbar, statusbar)
buffer.connect('mark_set', mark_set_callback, statusbar)
- window.connect('window_state_event', update_resize_grip, statusbar)
-
update_statusbar(buffer, statusbar);
window.set_default_size(200, 200)
diff --git a/demos/gtk-demo/demos/assistant.py b/demos/gtk-demo/demos/assistant.py
index e1a7ecde..8285d043 100644
--- a/demos/gtk-demo/demos/assistant.py
+++ b/demos/gtk-demo/demos/assistant.py
@@ -26,9 +26,6 @@ an operation into several simpler sequential steps, and to guide the user
through these steps.
"""
-# See FIXME's
-is_fully_bound = True
-
from gi.repository import Gtk, GdkPixbuf
class AssistantApp:
diff --git a/demos/gtk-demo/demos/builder.py b/demos/gtk-demo/demos/builder.py
index 5ecf748f..15af2543 100644
--- a/demos/gtk-demo/demos/builder.py
+++ b/demos/gtk-demo/demos/builder.py
@@ -24,9 +24,6 @@ description = """
Demonstrates an interface loaded from a XML description.
"""
-# See FIXME's
-is_fully_bound = True
-
from gi.repository import Gtk, GdkPixbuf, Gdk
import os
diff --git a/demos/gtk-demo/demos/button_box.py b/demos/gtk-demo/demos/button_box.py
index 7cd8d52e..04d34196 100644
--- a/demos/gtk-demo/demos/button_box.py
+++ b/demos/gtk-demo/demos/button_box.py
@@ -24,9 +24,6 @@ description = """
The Button Box widgets are used to arrange buttons with padding.
"""
-# See FIXME's
-is_fully_bound = False
-
from gi.repository import Gtk
class ButtonBoxApp:
@@ -101,14 +98,13 @@ class ButtonBoxApp:
bbox.set_layout(layout)
bbox.set_spacing(spacing)
- # FIXME: GtkButton consturctor should take a stock_id
- button = Gtk.Button.new_from_stock(Gtk.STOCK_OK)
+ button = Gtk.Button(Gtk.STOCK_OK)
bbox.add(button)
- button = Gtk.Button.new_from_stock(Gtk.STOCK_CANCEL)
+ button = Gtk.Button(Gtk.STOCK_CANCEL)
bbox.add(button)
- button = Gtk.Button.new_from_stock(Gtk.STOCK_HELP)
+ button = Gtk.Button(Gtk.STOCK_HELP)
bbox.add(button)
return frame
diff --git a/demos/gtk-demo/demos/clipboard.py b/demos/gtk-demo/demos/clipboard.py
index fa8f2da9..759c0f72 100644
--- a/demos/gtk-demo/demos/clipboard.py
+++ b/demos/gtk-demo/demos/clipboard.py
@@ -56,16 +56,14 @@ class ClipboardApp:
entry = Gtk.Entry()
hbox.pack_start(entry, True, True, 0)
- #FIXME: have the button constuctor take a stock_id
- # create button
- button = Gtk.Button.new_from_stock(Gtk.STOCK_COPY)
+ button = Gtk.Button(Gtk.STOCK_COPY)
hbox.pack_start(button, False, False, 0)
button.connect('clicked', self.copy_button_clicked, entry)
-
label = Gtk.Label(label='"Paste" will paste the text from the clipboard to the entry')
vbox.pack_start(label, False, False, 0)
+
hbox = Gtk.HBox(homogeneous=False, spacing=4)
hbox.set_border_width(8)
vbox.pack_start(hbox, False, False, 0)
@@ -73,9 +71,7 @@ class ClipboardApp:
# create secondary entry
entry = Gtk.Entry()
hbox.pack_start(entry, True, True, 0)
- #FIXME: have the button constuctor take a stock_id
- # create button
- button = Gtk.Button.new_from_stock(Gtk.STOCK_PASTE)
+ button = Gtk.Button(Gtk.STOCK_PASTE)
hbox.pack_start(button, False, False, 0)
button.connect('clicked', self.paste_button_clicked, entry)
@@ -95,16 +91,16 @@ class ClipboardApp:
hbox.add(ebox)
# make ebox a drag source
- Gtk.drag_source_set(ebox, Gdk.ModifierType.BUTTON1_MASK,
+ ebox.drag_source_set(Gdk.ModifierType.BUTTON1_MASK,
None, Gdk.DragAction.COPY)
- Gtk.drag_source_add_image_targets(ebox)
+ ebox.drag_source_add_image_targets()
ebox.connect('drag-begin', self.drag_begin, image)
ebox.connect('drag-data-get', self.drag_data_get, image)
# accept drops on ebox
- Gtk.drag_dest_set(ebox, Gtk.DestDefaults.ALL,
+ ebox.drag_dest_set(Gtk.DestDefaults.ALL,
None, Gdk.DragAction.COPY)
- Gtk.drag_dest_add_image_targets(ebox)
+ ebox.drag_dest_add_image_targets()
ebox.connect('drag-data-received', self.drag_data_received, image)
# context menu on ebox
@@ -119,16 +115,16 @@ class ClipboardApp:
hbox.add(ebox)
# make ebox a drag source
- Gtk.drag_source_set(ebox, Gdk.ModifierType.BUTTON1_MASK,
+ ebox.drag_source_set(Gdk.ModifierType.BUTTON1_MASK,
None, Gdk.DragAction.COPY)
- Gtk.drag_source_add_image_targets(ebox)
+ ebox.drag_source_add_image_targets()
ebox.connect('drag-begin', self.drag_begin, image)
ebox.connect('drag-data-get', self.drag_data_get, image)
# accept drops on ebox
- Gtk.drag_dest_set(ebox, Gtk.DestDefaults.ALL,
+ ebox.drag_dest_set(Gtk.DestDefaults.ALL,
None, Gdk.DragAction.COPY)
- Gtk.drag_dest_add_image_targets(ebox)
+ ebox.drag_dest_add_image_targets()
ebox.connect('drag-data-received', self.drag_data_received, image)
# context menu on ebox
@@ -138,7 +134,7 @@ class ClipboardApp:
#FIXME: Allow sending strings a Atoms and convert in PyGI
atom = Gdk.atom_intern('CLIPBOARD', True)
clipboard = Gtk.Clipboard.get(atom)
- clipboard.set_can_store(None, 0)
+ clipboard.set_can_store(None)
self.window.show_all()
@@ -183,7 +179,7 @@ class ClipboardApp:
selection_data.set_pixbuf(pixbuf)
def drag_data_received(self, widget, context, x, y, selection_data, info, time, data):
- if selection_data.length > 0:
+ if selection_data.get_length() > 0:
pixbuf = selection_data.get_pixbuf()
data.set_from_pixbuf(pixbuf)
@@ -205,24 +201,24 @@ class ClipboardApp:
data.set_from_pixbuf(pixbuf)
def button_press(self, widget, event, data):
- if event.button.button != 3:
+ if event.button != 3:
return False
- menu = Gtk.Menu()
+ self.menu = Gtk.Menu()
#FIXME: default constructor should take a stock property
item = Gtk.ImageMenuItem.new_from_stock(Gtk.STOCK_COPY, None)
item.connect('activate', self.copy_image, data)
item.show()
- menu.append(item)
+ self.menu.append(item)
#FIXME: default constructor should take a stock property
item = Gtk.ImageMenuItem.new_from_stock(Gtk.STOCK_PASTE, None)
item.connect('activate', self.paste_image, data)
item.show()
- menu.append(item)
+ self.menu.append(item)
- menu.popup(None, None, None, None, 3, event.button.time)
+ self.menu.popup(None, None, None, None, event.button, event.time)
def main(demoapp=None):
app = ClipboardApp()
diff --git a/demos/gtk-demo/demos/colorselector.py b/demos/gtk-demo/demos/colorselector.py
index 514718b7..f3dd74f7 100644
--- a/demos/gtk-demo/demos/colorselector.py
+++ b/demos/gtk-demo/demos/colorselector.py
@@ -32,9 +32,11 @@ from gi.repository import Gtk, Gdk
class ColorSelectorApp:
def __init__(self):
- # FIXME: we should allow Gdk.Color to be allocated without parameters
- # Also color doesn't seem to work
- self.color = Gdk.Color(0, 65535, 0)
+ self.color = Gdk.RGBA()
+ self.color.red = 0
+ self.color.blue = 1
+ self.color.green = 0
+ self.color.alpha = 1
self.window = Gtk.Window()
self.window.set_title('Color Selection')
@@ -52,12 +54,12 @@ class ColorSelectorApp:
vbox.pack_start(frame, True, True, 0)
self.da = Gtk.DrawingArea()
- self.da.connect('expose_event', self.expose_event_cb)
+ self.da.connect('draw', self.draw_cb)
# set a minimum size
self.da.set_size_request(200, 200)
# set the color
- self.da.modify_bg(Gtk.StateType.NORMAL, self.color)
+ self.da.override_background_color(0, self.color)
frame.add(self.da)
alignment = Gtk.Alignment(xalign=1.0,
@@ -74,42 +76,28 @@ class ColorSelectorApp:
self.window.show_all()
- def expose_event_cb(self, widget, event):
- window = widget.get_window()
-
- if window:
-
- style = widget.get_style()
- x = event.expose.area.x
- y = event.expose.area.y
- width = event.expose.area.width
- height = event.expose.area.height
-
- """
- Gdk.draw_rectangle(window,
- # FIXME: accessing style attribute segfaults
- style.bg_gc[Gtk.StateType.NORMAL],
- True,
- event.expose.area.x, event.expose.area.y,
- event.expose.area.width, event.expose.area.height)
- """
- return True
+ def draw_cb(self, widget, cairo_ctx):
+ style = widget.get_style_context()
+ bg_color = style.get_background_color(0)
+ Gdk.cairo_set_source_rgba(cairo_ctx, bg_color)
+ cairo_ctx.paint()
+ return True
def change_color_cb(self, button):
dialog = Gtk.ColorSelectionDialog(title='Changing color')
dialog.set_transient_for(self.window)
colorsel = dialog.get_color_selection()
- colorsel.set_previous_color(self.color)
- colorsel.set_current_color(self.color)
+ colorsel.set_previous_rgba(self.color)
+ colorsel.set_current_rgba(self.color)
colorsel.set_has_palette(True)
response = dialog.run()
if response == Gtk.ResponseType.OK:
- self.color = colorsel.get_current_color()
- self.da.modify_bg(Gtk.StateType.NORMAL, self.color)
+ self.color = colorsel.get_current_rgba()
+ self.da.override_background_color(0, self.color)
dialog.destroy()
diff --git a/demos/gtk-demo/demos/combobox.py b/demos/gtk-demo/demos/combobox.py
index 965457ee..1869dfd4 100644
--- a/demos/gtk-demo/demos/combobox.py
+++ b/demos/gtk-demo/demos/combobox.py
@@ -31,7 +31,7 @@ How the options are displayed is controlled by cell renderers.
# See FIXME's
is_fully_bound = False
-from gi.repository import Gtk, Gdk, GdkPixbuf, GLib
+from gi.repository import Gtk, Gdk, GdkPixbuf, GLib, GObject
(PIXBUF_COL,
TEXT_COL) = range(2)
@@ -45,15 +45,24 @@ class MaskEntry(Gtk.Entry):
self.connect('changed', self.changed_cb)
+ self.error_color = Gdk.RGBA()
+ self.error_color.red = 1.0
+ self.error_color.green = 0.9
+ self.error_color_blue = 0.9
+ self.error_color.alpha = 1.0
+
+ # workaround since override_color doesn't accept None yet
+ style_ctx = self.get_style_context()
+ self.normal_color = style_ctx.get_color(0)
+
def set_background(self):
- error_color = Gdk.Color(65535, 60000, 60000)
if self.mask:
if not GLib.regex_match_simple(self.mask,
self.get_text(), 0, 0):
- self.modify_base(Gtk.StateType.NORMAL, error_color)
+ self.override_color(0, self.error_color)
return
- self.modify_base(Gtk.StateType.NORMAL, None)
+ self.override_color(0, self.normal_color)
def changed_cb(self, entry):
self.set_background()
@@ -113,13 +122,12 @@ class ComboboxApp:
combo.add_attribute(renderer, 'text', 0)
combo.set_cell_data_func(renderer, self.is_capital_sensistive, None)
- # FIXME: make new_from_indices work
- # make constructor take list or string of indices
- path = Gtk.TreePath.new_from_string('0:8')
- (success, treeiter) = model.get_iter(path)
+ path = Gtk.TreePath('0:8')
+ treeiter = model.get_iter(path)
combo.set_active_iter(treeiter)
# A GtkComboBoxEntry with validation.
+
frame = Gtk.Frame(label='Editable')
vbox.pack_start(frame, False, False, 0)
@@ -127,22 +135,68 @@ class ComboboxApp:
box.set_border_width(5)
frame.add(box)
- combo = Gtk.ComboBoxEntry.new_text()
+ combo = Gtk.ComboBoxText.new_with_entry()
self.fill_combo_entry(combo)
box.add(combo)
entry = MaskEntry(mask='^([0-9]*|One|Two|2\302\275|Three)$')
- combo.remove(combo.get_child())
+ Gtk.Container.remove(combo, combo.get_child())
combo.add(entry)
+ # A combobox with string IDs
+
+ frame = Gtk.Frame(label='String IDs')
+ vbox.pack_start(frame, False, False, 0)
+
+ box = Gtk.VBox(homogeneous=False, spacing=0)
+ box.set_border_width(5)
+ frame.add(box)
+
+ # FIXME: model is not setup when constructing Gtk.ComboBoxText()
+ # so we call new() - Gtk should fix this to setup the model
+ # in __init__, not in the constructor
+ combo = Gtk.ComboBoxText.new()
+ combo.append('never', 'Not visible')
+ combo.append('when-active', 'Visible when active')
+ combo.append('always', 'Always visible')
+ box.add(combo)
+
+ entry = Gtk.Entry()
+
+ # FIXME: a bug in PyGObject does not allow us to access dynamic
+ # methods on GObject.Object, so bind properties the hard way
+ # GObject.Object.bind_property(combo, 'active-id',
+ # entry, 'text',
+ # GObject.BindingFlags.BIDIRECTIONAL)
+ self.combo_notify_id = \
+ combo.connect('notify::active-id',
+ self.combo_active_id_changed, entry)
+ self.entry_notify_id = \
+ entry.connect('notify::text',
+ self.entry_text_changed, combo)
+
+ box.add(entry)
self.window.show_all()
+ def combo_active_id_changed(self, combo, pspec, entry):
+ entry.disconnect(self.entry_notify_id)
+ entry.set_text(combo.get_property('active-id'))
+ self.entry_notify_id = \
+ entry.connect('notify::text',
+ self.entry_text_changed, combo)
+
+ def entry_text_changed(self, entry, pspec, combo):
+ combo.disconnect(self.combo_notify_id)
+ combo.set_property('active-id', entry.get_text())
+ self.combo_notify_id = \
+ combo.connect('notify::active-id',
+ self.combo_active_id_changed, entry)
+
def strip_underscore(self, s):
return s.replace('_', '')
def create_stock_icon_store(self):
- item = Gtk.StockItem()
stock_id = (Gtk.STOCK_DIALOG_WARNING,
Gtk.STOCK_STOP,
Gtk.STOCK_NEW,
@@ -156,8 +210,7 @@ class ComboboxApp:
for id in stock_id:
if id is not None:
pixbuf = cellview.render_icon(id, Gtk.IconSize.BUTTON, None)
- # FIXME: item should be annotated (out)
- Gtk.stock_lookup(id, item)
+ item = Gtk.stock_lookup(id)
label = self.strip_underscore(item.label)
store.append((pixbuf, label))
else:
@@ -173,7 +226,7 @@ class ComboboxApp:
"""
path = tree_model.get_path(treeiter)
- indices = path.get_indices_with_depth()
+ indices = path.get_indices()
sensitive = not(indices[0] == 1)
@@ -188,7 +241,7 @@ class ComboboxApp:
path = model.get_path(treeiter)
- indices = path.get_indices_with_depth()
+ indices = path.get_indices()
result = (indices[0] == 4)
return result
diff --git a/demos/gtk-demo/demos/dialogs.py b/demos/gtk-demo/demos/dialogs.py
new file mode 100644
index 00000000..78ad5df0
--- /dev/null
+++ b/demos/gtk-demo/demos/dialogs.py
@@ -0,0 +1,153 @@
+#!/usr/bin/env python
+# -*- Mode: Python; py-indent-offset: 4 -*-
+# vim: tabstop=4 shiftwidth=4 expandtab
+#
+# Copyright (C) 2010 Red Hat, Inc., John (J5) Palmieri <johnp@redhat.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
+# USA
+
+title = "Dialog and Message Boxes"
+description = """
+Dialog widgets are used to pop up a transient window for user feedback.
+"""
+
+from gi.repository import Gtk, GdkPixbuf
+
+class DialogsApp:
+ def __init__(self):
+ self.dialog_counter = 1
+
+ self.window = Gtk.Window(title="Dialogs")
+ self.window.set_border_width(8)
+ self.window.connect('destroy', Gtk.main_quit)
+
+ frame = Gtk.Frame(label="Dialogs")
+ self.window.add(frame)
+
+ vbox = Gtk.VBox(spacing=8)
+ vbox.set_border_width(8)
+ frame.add(vbox)
+
+ # Standard message dialog
+ hbox = Gtk.HBox(spacing=8);
+ vbox.pack_start(hbox, False, False, 0)
+ button = Gtk.Button.new_with_mnemonic("_Message Dialog");
+
+ button.connect('clicked',
+ self._message_dialog_clicked)
+ hbox.pack_start(button, False, False, 0)
+
+ vbox.pack_start(Gtk.HSeparator(),
+ False, False, 0);
+
+ # Interactive dialog
+ hbox = Gtk.HBox(spacing=8);
+ vbox.pack_start(hbox, False, False, 0)
+ vbox2 = Gtk.VBox(spacing=0);
+ button = Gtk.Button.new_with_mnemonic("_Interactive Dialog");
+
+ button.connect('clicked',
+ self._interactive_dialog_clicked)
+ hbox.pack_start(vbox2, False, False, 0)
+ vbox2.pack_start(button, False, False, 0)
+
+ table = Gtk.Table(n_rows=2, n_columns=2, homogeneous=False)
+ table.set_row_spacings(4)
+ table.set_col_spacings(4)
+ hbox.pack_start(table, False, False, 0);
+
+ label = Gtk.Label.new_with_mnemonic("_Entry 1")
+ table.attach_defaults (label, 0, 1, 0, 1)
+
+ self.entry1 = Gtk.Entry()
+ table.attach_defaults(self.entry1, 1, 2, 0, 1)
+ label.set_mnemonic_widget (self.entry1)
+
+ label = Gtk.Label.new_with_mnemonic("E_ntry 2");
+
+ table.attach_defaults(label, 0, 1, 1, 2);
+
+ self.entry2 = Gtk.Entry()
+ table.attach_defaults(self.entry2, 1, 2, 1, 2)
+ label.set_mnemonic_widget(self.entry2)
+
+ self.window.show_all()
+
+ def _interactive_dialog_clicked(self, button):
+ dialog = Gtk.Dialog('Interactive Dialog',
+ self.window,
+ Gtk.DialogFlags.MODAL |
+ Gtk.DialogFlags.DESTROY_WITH_PARENT,
+ (Gtk.STOCK_OK, Gtk.ResponseType.OK,
+ "_Non-stock Button", Gtk.ResponseType.CANCEL))
+
+ content_area = dialog.get_content_area ()
+ hbox = Gtk.HBox(spacing=8)
+ hbox.set_border_width(8)
+ content_area.pack_start(hbox, False, False, 0)
+
+ stock = Gtk.Image(stock=Gtk.STOCK_DIALOG_QUESTION,
+ icon_size=Gtk.IconSize.DIALOG)
+
+ hbox.pack_start(stock, False, False, 0)
+
+ table = Gtk.Table(2, 2, False)
+ table.set_row_spacings(4)
+ table.set_col_spacings(4)
+ hbox.pack_start(table, True, True, 0)
+ label = Gtk.Label.new_with_mnemonic("_Entry 1")
+ table.attach_defaults(label, 0, 1, 0, 1);
+ local_entry1 = Gtk.Entry();
+ local_entry1.set_text(self.entry1.get_text())
+ table.attach_defaults(local_entry1, 1, 2, 0, 1)
+ label.set_mnemonic_widget(local_entry1)
+
+ label = Gtk.Label.new_with_mnemonic("E_ntry 2")
+ table.attach_defaults (label, 0, 1, 1, 2)
+
+ local_entry2 = Gtk.Entry()
+ local_entry2.set_text (self.entry2.get_text())
+ table.attach_defaults(local_entry2, 1, 2, 1, 2)
+ label.set_mnemonic_widget(local_entry2);
+
+ hbox.show_all();
+
+ response = dialog.run()
+ if response == Gtk.ResponseType.OK:
+ self.entry1.set_text(local_entry1.get_text());
+ self.entry2.set_text(local_entry2.get_text());
+
+ dialog.destroy()
+
+ def _message_dialog_clicked(self, button):
+ dialog = Gtk.MessageDialog(self.window,
+ Gtk.DialogFlags.MODAL |
+ Gtk.DialogFlags.DESTROY_WITH_PARENT,
+ Gtk.MessageType.INFO,
+ Gtk.ButtonsType.OK,
+ "This message box has been popped up the following\nnumber of times:")
+ dialog.format_secondary_text ('%d' % self.dialog_counter)
+ dialog.run()
+
+ self.dialog_counter += 1
+ dialog.destroy()
+
+def main(demoapp=None):
+ app = DialogsApp()
+ Gtk.main()
+
+if __name__ == '__main__':
+ main()
diff --git a/demos/gtk-demo/demos/drawingarea.py b/demos/gtk-demo/demos/drawingarea.py
index b5dc1db4..1d7471dd 100644
--- a/demos/gtk-demo/demos/drawingarea.py
+++ b/demos/gtk-demo/demos/drawingarea.py
@@ -34,14 +34,12 @@ and drag in the scribble area to draw squiggles. Resize the window
to clear the area.
"""
-# See FIXME's
-is_fully_bound = False
-
from gi.repository import Gtk, Gdk
+import cairo
class DrawingAreaApp:
def __init__(self):
- self.pixmap = None
+ self.sureface = None
window = Gtk.Window()
window.set_title(title)
@@ -63,7 +61,7 @@ class DrawingAreaApp:
da = Gtk.DrawingArea()
da.set_size_request(100, 100)
frame.add(da)
- da.connect('expose-event', self.checkerboard_expose_event)
+ da.connect('draw', self.checkerboard_draw_event)
# create scribble area
label = Gtk.Label()
@@ -77,7 +75,7 @@ class DrawingAreaApp:
da = Gtk.DrawingArea()
da.set_size_request(100, 100)
frame.add(da)
- da.connect('expose-event', self.scribble_expose_event)
+ da.connect('draw', self.scribble_draw_event)
da.connect('configure-event', self.scribble_configure_event)
# event signals
@@ -94,49 +92,34 @@ class DrawingAreaApp:
window.show_all()
- def checkerboard_expose_event(self, da, event):
- check_size = 10
- spacing = 2
+ def checkerboard_draw_event(self, da, cairo_ctx):
- # At the start of an expose handler, a clip region of event->area
- # is set on the window, and event->area has been cleared to the
+ # At the start of a draw handler, a clip region has been set on
+ # the Cairo context, and the contents have been cleared to the
# widget's background color. The docs for
# gdk_window_begin_paint_region() give more details on how this
# works.
-
- # It would be a bit more efficient to keep these
- # GC's around instead of recreating on each expose, but
- # this is the lazy/slow way.
-
- gc1 = Gdk.GC.new(da.get_window())
- color = Gdk.Color(30000, 0, 30000)
- gc1.set_rgb_fg_color(color)
-
- gc2 = Gdk.GC.new(da.get_window())
- color = Gdk.Color(65535, 65535, 65535)
- gc2.set_rgb_fg_color(color)
+ check_size = 10
+ spacing = 2
xcount = 0
i = spacing
+ width = da.get_allocated_width()
+ height = da.get_allocated_height()
- while i < da.allocation.width:
+ while i < width:
j = spacing
ycount = xcount % 2 # start with even/odd depending on row
- while j < da.allocation.height:
+ while j < height:
if ycount % 2:
- gc = gc1
+ cairo_ctx.set_source_rgb(0.45777, 0, 0.45777)
else:
- gc = gc2
-
- # If we're outside event->area, this will do nothing.
- # It might be mildly more efficient if we handled
- # the clipping ourselves, but again we're feeling lazy.
- Gdk.draw_rectangle(da.get_window(),
- gc,
- True,
- i, j,
- check_size,
- check_size)
+ cairo_ctx.set_source_rgb(1, 1, 1)
+ # If we're outside the clip this will do nothing.
+ cairo_ctx.rectangle(i, j,
+ check_size,
+ check_size)
+ cairo_ctx.fill()
j += check_size + spacing
ycount += 1
@@ -146,72 +129,43 @@ class DrawingAreaApp:
return True
- def scribble_expose_event(self, da, event):
-
- # We use the "foreground GC" for the widget since it already exists,
- # but honestly any GC would work. The only thing to worry about
- # is whether the GC has an inappropriate clip region set.
+ def scribble_draw_event(self, da, cairo_ctx):
- # FIXME: we should be using widget.style.forground_gc but
- # there is a bug in GObject Introspection when getting
- # certain struct offsets so we create a new GC here
- black_gc = Gdk.GC.new(da.get_window())
- color = Gdk.Color(0, 0, 0)
- black_gc.set_rgb_fg_color(color)
-
- Gdk.draw_drawable(da.get_window(),
- black_gc,
- self.pixmap,
- event.expose.area.x, event.expose.area.y,
- event.expose.area.x, event.expose.area.y,
- event.expose.area.width, event.expose.area.height)
+ cairo_ctx.set_source_surface(self.surface, 0, 0)
+ cairo_ctx.paint()
return False
def draw_brush(self, widget, x, y):
- update_rect = Gdk.Rectangle(x - 3,
- y - 3,
- 6,
- 6)
-
- # FIXME: we should be using widget.style.black_gc but
- # there is a bug in GObject Introspection when getting
- # certain struct offsets so we create a new GC here
- black_gc = Gdk.GC.new(widget.get_window())
- color = Gdk.Color(0, 0, 0)
- black_gc.set_rgb_fg_color(color)
-
- Gdk.draw_rectangle(self.pixmap,
- black_gc,
- True,
- update_rect.x, update_rect.y,
- update_rect.width, update_rect.height)
+ update_rect = Gdk.Rectangle()
+ update_rect.x = x - 3
+ update_rect.y = y - 3
+ update_rect.width = 6
+ update_rect.height = 6
+
+ # paint to the surface where we store our state
+ cairo_ctx = cairo.Context(self.surface)
+
+ Gdk.cairo_rectangle(cairo_ctx, update_rect)
+ cairo_ctx.fill()
widget.get_window().invalidate_rect(update_rect, False)
def scribble_configure_event(self, da, event):
- self.pixmap = Gdk.Pixmap.new(da.get_window(),
- da.allocation.width,
- da.allocation.height,
- -1)
-
- # FIXME: we should be using widget.style.white_gc but
- # there is a bug in GObject Introspection when getting
- # certain struct offsets so we create a new GC here
- white_gc = Gdk.GC.new(da.get_window())
- color = Gdk.Color(65535, 65535, 65535)
- white_gc.set_rgb_fg_color(color)
- Gdk.draw_rectangle(self.pixmap,
- white_gc,
- True,
- 0, 0,
- da.allocation.width,
- da.allocation.height)
+
+ allocation = da.get_allocation()
+ self.surface = da.get_window().create_similar_surface(cairo.CONTENT_COLOR,
+ allocation.width,
+ allocation.height)
+
+ cairo_ctx = cairo.Context(self.surface)
+ cairo_ctx.set_source_rgb(1, 1, 1)
+ cairo_ctx.paint()
return True
def scribble_motion_notify_event(self, da, event):
- if self.pixmap == None: # paranoia check, in case we haven't gotten a configure event
+ if self.surface == None: # paranoia check, in case we haven't gotten a configure event
return False
# This call is very important; it requests the next motion event.
@@ -224,20 +178,19 @@ class DrawingAreaApp:
# we avoid getting a huge number of events faster than we
# can cope.
- (window, x, y, state) = event.motion.window.get_pointer()
+ (window, x, y, state) = event.window.get_pointer()
- # FIXME: for some reason Gdk.ModifierType.BUTTON1_MASK doesn't exist
- #if state & Gdk.ModifierType.BUTTON1_MASK:
- # self.draw_brush(da, x, y)
+ if state & Gdk.ModifierType.BUTTON1_MASK:
+ self.draw_brush(da, x, y)
return True
def scribble_button_press_event(self, da, event):
- if self.pixmap == None: # paranoia check, in case we haven't gotten a configure event
+ if self.surface == None: # paranoia check, in case we haven't gotten a configure event
return False
- if event.button.button == 1:
- self.draw_brush(da, event.button.x, event.button.y)
+ if event.button == 1:
+ self.draw_brush(da, event.x, event.y)
return True
diff --git a/demos/gtk-demo/demos/expander.py b/demos/gtk-demo/demos/expander.py
new file mode 100644
index 00000000..96387b21
--- /dev/null
+++ b/demos/gtk-demo/demos/expander.py
@@ -0,0 +1,60 @@
+#!/usr/bin/env python
+# -*- Mode: Python; py-indent-offset: 4 -*-
+# vim: tabstop=4 shiftwidth=4 expandtab
+#
+# Copyright (C) 2010 Red Hat, Inc., John (J5) Palmieri <johnp@redhat.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
+# USA
+
+title = "Expander"
+description = """
+GtkExpander allows to provide additional content that is initially hidden.
+This is also known as "disclosure triangle".
+"""
+
+from gi.repository import Gtk, GdkPixbuf
+
+class ExpanderApp:
+ def __init__(self):
+ self.window = Gtk.Dialog("GtkExpander",
+ None, 0,
+ (Gtk.STOCK_CLOSE, Gtk.ResponseType.NONE))
+ self.window.set_resizable(False)
+ self.window.connect('response', lambda window, x:window.destroy())
+ self.window.connect('destroy', Gtk.main_quit)
+
+ content_area = self.window.get_content_area()
+ vbox = Gtk.VBox(spacing=5)
+ content_area.pack_start(vbox, True, True, 0)
+ vbox.set_border_width(5)
+
+ label = Gtk.Label('Expander demo. Click on the triangle for details.')
+ vbox.pack_start(label, True, True, 0)
+
+ expander = Gtk.Expander(label='Details')
+ vbox.pack_start(expander, False, False, 0)
+
+ label = Gtk.Label('Details can be shown or hidden')
+ expander.add(label)
+
+ self.window.show_all()
+
+def main(demoapp=None):
+ app = ExpanderApp()
+ Gtk.main()
+
+if __name__ == '__main__':
+ main()
diff --git a/demos/gtk-demo/demos/images.py b/demos/gtk-demo/demos/images.py
new file mode 100644
index 00000000..51c241e3
--- /dev/null
+++ b/demos/gtk-demo/demos/images.py
@@ -0,0 +1,312 @@
+#!/usr/bin/env python
+# -*- Mode: Python; py-indent-offset: 4 -*-
+# vim: tabstop=4 shiftwidth=4 expandtab
+#
+# Copyright (C) 2010 Red Hat, Inc., John (J5) Palmieri <johnp@redhat.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
+# USA
+
+title = "Images"
+description = """GtkImage is used to display an image; the image can be in a number of formats. Typically, you load an image into a GdkPixbuf, then display the pixbuf.
+This demo code shows some of the more obscure cases, in the simple case a call to gtk_image_new_from_file() is all you need.
+"""
+
+from gi.repository import Gtk, Gdk, GdkPixbuf, GLib, Gio, GObject
+import os
+import math
+from os import path
+
+class ImagesApp:
+ def __init__(self):
+ self.pixbuf_loader = None
+ self.image_stream = None
+
+ self.window = Gtk.Window(title="Images")
+ self.window.connect('destroy', self.cleanup_cb)
+ self.window.set_border_width(8)
+
+ vbox = Gtk.VBox(spacing=8)
+ vbox.set_border_width(8)
+ self.window.add(vbox)
+
+ label = Gtk.Label()
+ label.set_markup('<u>Image loaded from file</u>')
+ vbox.pack_start(label, False, False, 0)
+
+ frame = Gtk.Frame()
+ frame.set_shadow_type(Gtk.ShadowType.IN)
+
+ # The alignment keeps the frame from growing when users resize
+ # the window
+ align = Gtk.Alignment(xalign=0.5,
+ yalign=0.5,
+ xscale=0,
+ yscale=0)
+ align.add(frame)
+ vbox.pack_start(align, False, False, 0)
+
+ self.base_path = 'data'
+ if not path.isdir(self.base_path):
+ self.base_path = path.join('demos', self.base_path)
+
+ try:
+ img_path = path.join(self.base_path, 'gtk-logo-rgb.gif')
+ pixbuf = GdkPixbuf.Pixbuf.new_from_file(img_path)
+ except GObject.GError as e:
+ dialog = Gtk.MessageDialog(self.window,
+ Gtk.DialogFlags.DESTROY_WITH_PARENT,
+ Gtk.MessageType.ERROR,
+ Gtk.ButtonsType.CLOSE,
+ e.message)
+
+ dialog.show()
+ dialog.connect('response', lambda x,y: dialog.destroy())
+
+ image = Gtk.Image.new_from_pixbuf(pixbuf)
+ frame.add(image)
+
+ # Animation
+
+ label = Gtk.Label()
+ label.set_markup('<u>Animation loaded from file</u>')
+ vbox.pack_start(label, False, False, 0)
+
+ frame = Gtk.Frame()
+ frame.set_shadow_type(Gtk.ShadowType.IN)
+
+ # The alignment keeps the frame from growing when users resize
+ # the window
+ align = Gtk.Alignment(xalign=0.5,
+ yalign=0.5,
+ xscale=0,
+ yscale=0)
+ align.add(frame)
+ vbox.pack_start(align, False, False, 0)
+
+ img_path = path.join(self.base_path, 'floppybuddy.gif')
+ image = Gtk.Image.new_from_file(img_path)
+ frame.add(image)
+
+ # Symbolic icon
+
+ label = Gtk.Label()
+ label.set_markup('<u>Symbolic themed icon</u>')
+ vbox.pack_start(label, False, False, 0)
+
+ frame = Gtk.Frame()
+ frame.set_shadow_type(Gtk.ShadowType.IN)
+
+ # The alignment keeps the frame from growing when users resize
+ # the window
+ align = Gtk.Alignment(xalign=0.5,
+ yalign=0.5,
+ xscale=0,
+ yscale=0)
+ align.add(frame)
+ vbox.pack_start(align, False, False, 0)
+
+ gicon = Gio.ThemedIcon.new_with_default_fallbacks('battery-caution-charging-symbolic')
+ image = Gtk.Image.new_from_gicon(gicon, Gtk.IconSize.DIALOG)
+ frame.add(image)
+
+ # progressive
+
+ label = Gtk.Label()
+ label.set_markup('<u>Progressive image loading</u>')
+ vbox.pack_start(label, False, False, 0)
+
+ frame = Gtk.Frame()
+ frame.set_shadow_type(Gtk.ShadowType.IN)
+
+ # The alignment keeps the frame from growing when users resize
+ # the window
+ align = Gtk.Alignment(xalign=0.5,
+ yalign=0.5,
+ xscale=0,
+ yscale=0)
+ align.add(frame)
+ vbox.pack_start(align, False, False, 0)
+
+ image = Gtk.Image.new_from_pixbuf(None)
+ frame.add(image)
+
+ self.start_progressive_loading(image)
+
+ # Sensistivity control
+ button = Gtk.ToggleButton(label='_Insensitive')
+ button.connect('toggled', self.toggle_sensitivity_cb, vbox)
+ vbox.pack_start(button, False, False, 0)
+
+ self.window.show_all()
+
+ def toggle_sensitivity_cb(self, button, container):
+ widget_list = container.get_children()
+ for w in widget_list:
+ if w != button:
+ w.set_sensitive(not button.get_active())
+
+ return True
+
+ def progressive_timeout(self, image):
+ # This shows off fully-paranoid error handling, so looks scary.
+ # You could factor out the error handling code into a nice separate
+ # function to make things nicer.
+
+ if self.image_stream:
+ try:
+ buf = self.image_stream.read(256)
+ except IOError as e:
+ dialog = Gtk.MessageDialog(self.window,
+ Gtk.DialogFlags.DESTROY_WITH_PARENT,
+ Gtk.MessageType.ERROR,
+ Gtk.ButtonsType.CLOSE,
+ "Failure reading image file 'alphatest.png': %s" %str(e))
+
+ self.image_stream.close()
+ self.image_stream = None
+ self.load_timeout = 0
+
+ dialog.show()
+ dialog.connect('response', lambda x,y: dialog.destroy())
+
+ return False # uninstall the timeout
+
+ try:
+ self.pixbuf_loader.write(buf)
+
+ except GObject.GError as e:
+ dialog = Gtk.MessageDialog(self.window,
+ Gtk.DialogFlags.DESTROY_WITH_PARENT,
+ Gtk.MessageType.ERROR,
+ Gtk.ButtonsType.CLOSE,
+ e.message)
+
+ self.image_stream.close()
+ self.image_stream = None
+ self.load_timeout = 0
+
+ dialog.show()
+ dialog.connect('response', lambda x,y: dialog.destroy())
+
+ return False # uninstall the timeout
+
+ if len(buf) < 256: # eof
+ self.image_stream.close()
+ self.image_stream = None
+
+ # Errors can happen on close, e.g. if the image
+ # file was truncated we'll know on close that
+ # it was incomplete.
+ try:
+ self.pixbuf_loader.close()
+ except GObject.GError as e:
+ dialog = Gtk.MessageDialog(self.window,
+ Gtk.DialogFlags.DESTROY_WITH_PARENT,
+ Gtk.MessageType.ERROR,
+ Gtk.ButtonsType.CLOSE,
+ 'Failed to load image: %s' % e.message)
+
+ self.load_timeout = 0
+
+ dialog.show()
+ dialog.connect('response', lambda x,y: dialog.destroy())
+
+ return False # uninstall the timeout
+ else:
+ img_path = path.join(self.base_path, 'alphatest.png')
+ try:
+ self.image_stream = open(img_path, 'rb')
+ except IOError as e:
+ dialog = Gtk.MessageDialog(self.window,
+ Gtk.DialogFlags.DESTROY_WITH_PARENT,
+ Gtk.MessageType.ERROR,
+ Gtk.ButtonsType.CLOSE,
+ str(e))
+ self.load_timeout = 0
+ dialog.show()
+ dialog.connect('response', lambda x,y: dialog.destroy())
+
+ return False # uninstall the timeout
+
+ if self.pixbuf_loader:
+ try:
+ self.pixbuf_loader.close()
+ except GObject.GError:
+ pass
+ self.pixbuf_loader = None
+
+ self.pixbuf_loader = GdkPixbuf.PixbufLoader()
+
+ self.pixbuf_loader.connect('area-prepared',
+ self.progressive_prepared_callback,
+ image)
+
+ self.pixbuf_loader.connect('area-updated',
+ self.progressive_updated_callback,
+ image)
+ # leave timeout installed
+ return True
+
+ def progressive_prepared_callback(self, loader, image):
+ pixbuf = loader.get_pixbuf()
+ # Avoid displaying random memory contents, since the pixbuf
+ # isn't filled in yet.
+ pixbuf.fill(0xaaaaaaff)
+ image.set_from_pixbuf(pixbuf)
+
+ def progressive_updated_callback(self, loader, x, y, width, height, image):
+ # We know the pixbuf inside the GtkImage has changed, but the image
+ # itself doesn't know this; so queue a redraw. If we wanted to be
+ # really efficient, we could use a drawing area or something
+ # instead of a GtkImage, so we could control the exact position of
+ # the pixbuf on the display, then we could queue a draw for only
+ # the updated area of the image.
+ image.queue_draw()
+
+ def start_progressive_loading(self, image):
+ # This is obviously totally contrived (we slow down loading
+ # on purpose to show how incremental loading works).
+ # The real purpose of incremental loading is the case where
+ # you are reading data from a slow source such as the network.
+ # The timeout simply simulates a slow data source by inserting
+ # pauses in the reading process.
+
+ self.load_timeout = Gdk.threads_add_timeout(150,
+ GLib.PRIORITY_DEFAULT_IDLE,
+ self.progressive_timeout,
+ image)
+
+ def cleanup_cb(self, widget):
+ if self.load_timeout:
+ GObject.source_remove(self.load_timeout)
+
+ if self.pixbuf_loader:
+ try:
+ self.pixbuf_loader.close()
+ except GObject.GError:
+ pass
+
+ if self.image_stream:
+ self.image_stream.close()
+
+ Gtk.main_quit()
+
+def main(demoapp=None):
+ app = ImagesApp()
+ Gtk.main()
+
+if __name__ == '__main__':
+ main()
diff --git a/demos/gtk-demo/demos/infobars.py b/demos/gtk-demo/demos/infobars.py
new file mode 100644
index 00000000..58190386
--- /dev/null
+++ b/demos/gtk-demo/demos/infobars.py
@@ -0,0 +1,99 @@
+#!/usr/bin/env python
+# -*- Mode: Python; py-indent-offset: 4 -*-
+# vim: tabstop=4 shiftwidth=4 expandtab
+#
+# Copyright (C) 2010 Red Hat, Inc., John (J5) Palmieri <johnp@redhat.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
+# USA
+
+title = "Info Bars"
+description = """
+Info bar widgets are used to report important messages to the user.
+"""
+
+from gi.repository import Gtk
+
+class InfobarApp:
+ def __init__(self):
+ self.window = Gtk.Window()
+ self.window.set_title('Info Bars')
+ self.window.set_border_width(8)
+ self.window.connect('destroy', Gtk.main_quit)
+
+ vbox = Gtk.VBox(spacing=0)
+ self.window.add(vbox)
+
+ bar = Gtk.InfoBar()
+ vbox.pack_start(bar, False, False, 0)
+ bar.set_message_type(Gtk.MessageType.INFO)
+ label = Gtk.Label('This is an info bar with message type Gtk.MessageType.INFO')
+ bar.get_content_area().pack_start(label, False, False, 0)
+
+ bar = Gtk.InfoBar()
+ vbox.pack_start(bar, False, False, 0)
+ bar.set_message_type(Gtk.MessageType.WARNING)
+ label = Gtk.Label('This is an info bar with message type Gtk.MessageType.WARNING')
+ bar.get_content_area().pack_start(label, False, False, 0)
+
+ bar = Gtk.InfoBar()
+ bar.add_button(Gtk.STOCK_OK, Gtk.ResponseType.OK)
+ bar.connect('response', self.on_bar_response)
+ vbox.pack_start(bar, False, False, 0)
+ bar.set_message_type(Gtk.MessageType.QUESTION)
+ label = Gtk.Label('This is an info bar with message type Gtk.MessageType.QUESTION')
+ bar.get_content_area().pack_start(label, False, False, 0)
+
+ bar = Gtk.InfoBar()
+ vbox.pack_start(bar, False, False, 0)
+ bar.set_message_type(Gtk.MessageType.ERROR)
+ label = Gtk.Label('This is an info bar with message type Gtk.MessageType.ERROR')
+ bar.get_content_area().pack_start(label, False, False, 0)
+
+ bar = Gtk.InfoBar()
+ vbox.pack_start(bar, False, False, 0)
+ bar.set_message_type(Gtk.MessageType.OTHER)
+ label = Gtk.Label('This is an info bar with message type Gtk.MessageType.OTHER')
+ bar.get_content_area().pack_start(label, False, False, 0)
+
+ frame = Gtk.Frame(label="Info bars")
+ vbox.pack_start(frame, False, False, 8)
+
+ vbox2 = Gtk.VBox(spacing=8)
+ vbox2.set_border_width(8)
+ frame.add(vbox2)
+
+ # Standard message dialog
+ label = Gtk.Label('An example of different info bars')
+ vbox2.pack_start(label, False, False, 0)
+
+ self.window.show_all()
+
+ def on_bar_response(self, info_bar, response_id):
+ dialog = Gtk.MessageDialog(self.window,
+ Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT,
+ Gtk.MessageType.INFO,
+ Gtk.ButtonsType.OK,
+ 'You clicked on an info bar')
+ dialog.format_secondary_text('Your response has id %d' % response_id)
+ dialog.run()
+ dialog.destroy()
+
+def main(demoapp=None):
+ app = InfobarApp()
+ Gtk.main()
+
+if __name__ == '__main__':
+ main()
diff --git a/demos/gtk-demo/demos/links.py b/demos/gtk-demo/demos/links.py
new file mode 100644
index 00000000..898f3e6a
--- /dev/null
+++ b/demos/gtk-demo/demos/links.py
@@ -0,0 +1,74 @@
+#!/usr/bin/env python
+# -*- Mode: Python; py-indent-offset: 4 -*-
+# vim: tabstop=4 shiftwidth=4 expandtab
+#
+# Copyright (C) 2010 Red Hat, Inc., John (J5) Palmieri <johnp@redhat.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
+# USA
+
+title = "Links"
+description = """
+GtkLabel can show hyperlinks. The default action is to call gtk_show_uri() on
+their URI, but it is possible to override this with a custom handler.
+"""
+
+from gi.repository import Gtk
+
+class LinksApp:
+ def __init__(self):
+ self.window = Gtk.Window()
+ self.window.set_title('Links')
+ self.window.set_border_width(12)
+ self.window.connect('destroy', Gtk.main_quit)
+
+ label = Gtk.Label("""Some <a href="http://en.wikipedia.org/wiki/Text"
+title="plain text">text</a> may be marked up
+as hyperlinks, which can be clicked
+or activated via <a href="keynav">keynav</a>""")
+
+ label.set_use_markup(True)
+ label.connect("activate-link", self.activate_link)
+ self.window.add(label);
+ label.show()
+
+ self.window.show()
+
+ def activate_link(self, label, uri):
+ if uri == 'keynav':
+ parent = label.get_toplevel()
+ markup = """The term <i>keynav</i> is a shorthand for
+keyboard navigation and refers to the process of using
+a program (exclusively) via keyboard input."""
+ dialog = Gtk.MessageDialog(parent,
+ Gtk.DialogFlags.DESTROY_WITH_PARENT,
+ Gtk.MessageType.INFO,
+ Gtk.ButtonsType.OK,
+ text=markup,
+ use_markup=True)
+ dialog.present()
+ dialog.connect('response', self.response_cb)
+
+ return True
+
+ def response_cb(self, dialog, response_id):
+ dialog.destroy()
+
+def main(demoapp=None):
+ app = LinksApp()
+ Gtk.main()
+
+if __name__ == '__main__':
+ main()
diff --git a/demos/gtk-demo/demos/menus.py b/demos/gtk-demo/demos/menus.py
new file mode 100644
index 00000000..2e3a41b4
--- /dev/null
+++ b/demos/gtk-demo/demos/menus.py
@@ -0,0 +1,122 @@
+#!/usr/bin/env python
+# -*- Mode: Python; py-indent-offset: 4 -*-
+# vim: tabstop=4 shiftwidth=4 expandtab
+#
+# Copyright (C) 2010 Red Hat, Inc., John (J5) Palmieri <johnp@redhat.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
+# USA
+
+title = "Menus"
+description = """There are several widgets involved in displaying menus. The GtkMenuBar widget is a menu bar, which normally appears horizontally at the top of an application, but can also be layed out vertically. The GtkMenu widget is the actual menu that pops up. Both GtkMenuBar and GtkMenu are subclasses of GtkMenuShell; a GtkMenuShell contains menu items (GtkMenuItem). Each menu item contains text and/or images and can be selected by the user.
+There are several kinds of menu item, including plain GtkMenuItem, GtkCheckMenuItem which can be checked/unchecked, GtkRadioMenuItem which is a check menu item that's in a mutually exclusive group, GtkSeparatorMenuItem which is a separator bar, GtkTearoffMenuItem which allows a GtkMenu to be torn off, and GtkImageMenuItem which can place a GtkImage or other widget next to the menu text.
+A GtkMenuItem can have a submenu, which is simply a GtkMenu to pop up when the menu item is selected. Typically, all menu items in a menu bar have submenus.
+GtkUIManager provides a higher-level interface for creating menu bars and menus; while you can construct menus manually, most people don't do that. There's a separate demo for GtkUIManager.
+"""
+
+from gi.repository import Gtk
+
+class MenusApp:
+ def __init__(self):
+ self.window = Gtk.Window()
+ self.window.set_title('Menus')
+ self.window.connect('destroy', Gtk.main_quit)
+
+ accel_group = Gtk.AccelGroup()
+ self.window.add_accel_group(accel_group)
+ self.window.set_border_width(0)
+
+ box = Gtk.HBox()
+ self.window.add(box)
+
+ box1 = Gtk.VBox()
+ box.add(box1)
+
+ menubar = Gtk.MenuBar()
+ box1.pack_start(menubar, False, True, 0)
+
+ menu = self.create_menu(2, True)
+
+ menuitem = Gtk.MenuItem(label='test\nline2')
+ menuitem.set_submenu(self.create_menu(3, True))
+ menubar.append(menuitem)
+
+ menuitem = Gtk.MenuItem(label='foo')
+ menuitem.set_submenu(self.create_menu(4, True))
+ menuitem.set_right_justified(True)
+ menubar.append(menuitem)
+
+ box2 = Gtk.VBox(spacing=10)
+ box2.set_border_width(10)
+ box1.pack_start(box2, False, True, 0)
+
+ button = Gtk.Button('Flip')
+ button.connect('clicked', self.change_orientation, menubar)
+ box2.pack_start(button, True, True, 0)
+
+ button = Gtk.Button('Close')
+ button.connect('clicked', lambda x: self.window.destroy())
+ box2.pack_start(button, True, True, 0)
+ button.set_can_default(True)
+
+ self.window.show_all()
+
+ def create_menu(self, depth, tearoff):
+ if depth < 1:
+ return None
+
+ menu = Gtk.Menu()
+ group = None
+
+ if tearoff:
+ menuitem = Gtk.TearoffMenuItem()
+ menu.append(menuitem)
+
+ i = 0
+ j = 1
+ while i < 5:
+ label = 'item %2d - %d' % (depth, j)
+ # we should be adding this to a group but the group API
+ # isn't bindable - we need something more like the
+ # Gtk.RadioAction API
+ menuitem = Gtk.RadioMenuItem(label=label)
+ menu.append(menuitem)
+
+ if i == 3:
+ menuitem.set_sensitive(False)
+
+ menuitem.set_submenu(self.create_menu(depth - 1, True))
+
+ i += 1
+ j += 1
+
+ return menu
+
+ def change_orientation(self, button, menubar):
+ parent = menubar.get_parent()
+ orientation = parent.get_orientation()
+ parent.set_orientation(1 - orientation)
+
+ if orientation == Gtk.Orientation.VERTICAL:
+ menubar.props.pack_direction = Gtk.PackDirection.TTB
+ else:
+ menubar.props.pack_direction = Gtk.PackDirection.LTR
+
+def main(demoapp=None):
+ app = MenusApp()
+ Gtk.main()
+
+if __name__ == '__main__':
+ main()
diff --git a/demos/gtk-demo/demos/pickers.py b/demos/gtk-demo/demos/pickers.py
new file mode 100644
index 00000000..8ecedb6b
--- /dev/null
+++ b/demos/gtk-demo/demos/pickers.py
@@ -0,0 +1,74 @@
+#!/usr/bin/env python
+# -*- Mode: Python; py-indent-offset: 4 -*-
+# vim: tabstop=4 shiftwidth=4 expandtab
+#
+# Copyright (C) 2010 Red Hat, Inc., John (J5) Palmieri <johnp@redhat.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
+# USA
+
+title = "Pickers"
+description = """
+These widgets are mainly intended for use in preference dialogs. They allow to select colors, fonts, files and directories.
+"""
+
+from gi.repository import Gtk
+
+class PickersApp:
+ def __init__(self):
+ self.window = Gtk.Window(title='Pickers')
+ self.window.connect('destroy', Gtk.main_quit)
+ self.window.set_border_width(10)
+
+ table = Gtk.Table(4, 2, False)
+ table.set_col_spacing(0, 10)
+ table.set_row_spacings(3)
+ self.window.add(table)
+ table.set_border_width(10)
+
+ label = Gtk.Label('Color:')
+ label.set_alignment(0.0, 0.5)
+ picker = Gtk.ColorButton()
+ table.attach_defaults(label, 0, 1, 0, 1)
+ table.attach_defaults (picker, 1, 2, 0, 1)
+
+ label = Gtk.Label('Font:')
+ label.set_alignment(0.0, 0.5)
+ picker = Gtk.FontButton()
+ table.attach_defaults(label, 0, 1, 1, 2)
+ table.attach_defaults (picker, 1, 2, 1, 2)
+
+ label = Gtk.Label('File:')
+ label.set_alignment(0.0, 0.5)
+ picker = Gtk.FileChooserButton.new('Pick a File',
+ Gtk.FileChooserAction.OPEN)
+ table.attach_defaults(label, 0, 1, 2, 3)
+ table.attach_defaults (picker, 1, 2, 2, 3)
+
+ label = Gtk.Label('Folder:')
+ label.set_alignment(0.0, 0.5)
+ picker = Gtk.FileChooserButton.new('Pick a Folder',
+ Gtk.FileChooserAction.SELECT_FOLDER)
+ table.attach_defaults(label, 0, 1, 3, 4)
+ table.attach_defaults (picker, 1, 2, 3, 4)
+
+ self.window.show_all()
+
+def main(demoapp=None):
+ app = PickersApp()
+ Gtk.main()
+
+if __name__ == '__main__':
+ main()
diff --git a/demos/gtk-demo/demos/pixbuf.py b/demos/gtk-demo/demos/pixbuf.py
new file mode 100644
index 00000000..4a9aa581
--- /dev/null
+++ b/demos/gtk-demo/demos/pixbuf.py
@@ -0,0 +1,183 @@
+#!/usr/bin/env python
+# -*- Mode: Python; py-indent-offset: 4 -*-
+# vim: tabstop=4 shiftwidth=4 expandtab
+#
+# Copyright (C) 2010 Red Hat, Inc., John (J5) Palmieri <johnp@redhat.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
+# USA
+
+title = "Pixbufs"
+description = """A GdkPixbuf represents an image, normally in RGB or RGBA format. Pixbufs are normally used to load files from disk and perform image scaling.
+ It also shows off how to use GtkDrawingArea to do a simple animation.
+Look at the Image demo for additional pixbuf usage examples.
+"""
+
+from gi.repository import Gtk, Gdk, GdkPixbuf, GLib, GObject
+import os
+import math
+from os import path
+
+class PixbufApp:
+ FRAME_DELAY = 50
+ BACKGROUND_NAME = "background.jpg"
+ CYCLE_LEN = 60
+
+ def __init__(self):
+ self.background_width = 0
+ self.background_height = 0
+ self.background_pixbuf = None
+ self.frame = None
+ self.frame_num = 0
+ self.pixbufs = []
+ self.image_names = [
+ "apple-red.png",
+ "gnome-applets.png",
+ "gnome-calendar.png",
+ "gnome-foot.png",
+ "gnome-gmush.png",
+ "gnome-gimp.png",
+ "gnome-gsame.png",
+ "gnu-keys.png"
+ ]
+
+ self.window = Gtk.Window(title="Pixbufs")
+ self.window.set_resizable(False)
+ self.window.connect('destroy', self.cleanup_cb)
+
+ try:
+ self.load_pixbufs()
+ except GLib.Error as e:
+ dialog = Gtk.MessageDialog(None,
+ 0,
+ Gtk.MessageType.ERROR,
+ Gtk.ButtonsType.CLOSE,
+ e.message)
+
+ dialog.run()
+ dialog.destroy()
+ Gtk.main_quit()
+
+ self.window.set_size_request(self.background_width,
+ self.background_height)
+ self.frame = GdkPixbuf.Pixbuf.new(GdkPixbuf.Colorspace.RGB,
+ False,
+ 8,
+ self.background_width,
+ self.background_height)
+ self.da = Gtk.DrawingArea()
+ self.da.connect('draw', self.draw_cb)
+ self.window.add(self.da)
+ self.timeout_id = GObject.timeout_add(self.FRAME_DELAY, self.timeout_cb)
+ self.window.show_all()
+
+ def load_pixbufs(self):
+ base_path = 'data'
+ if not path.isdir(base_path):
+ base_path = path.join('demos', base_path)
+
+ img_path = path.join(base_path, self.BACKGROUND_NAME)
+ self.background_pixbuf = GdkPixbuf.Pixbuf.new_from_file(img_path)
+ self.background_height = self.background_pixbuf.get_height()
+ self.background_width = self.background_pixbuf.get_width()
+
+ for img_name in self.image_names:
+ img_path = path.join(base_path, img_name)
+ self.pixbufs.append(GdkPixbuf.Pixbuf.new_from_file(img_path))
+
+ def draw_cb(self, da, cairo_ctx):
+ Gdk.cairo_set_source_pixbuf(cairo_ctx, self.frame, 0, 0)
+ cairo_ctx.paint()
+
+ return True
+
+ def timeout_cb(self):
+ self.background_pixbuf.copy_area(0, 0,
+ self.background_width,
+ self.background_height,
+ self.frame,
+ 0, 0)
+
+ f = float(self.frame_num % self.CYCLE_LEN) / self.CYCLE_LEN
+
+ xmid = self.background_width / 2.0
+ ymid = self.background_height / 2.0
+ radius = min(xmid, ymid) / 2.0
+
+ r1 = Gdk.Rectangle()
+ r2 = Gdk.Rectangle()
+
+ i = 0
+ for pb in self.pixbufs:
+ i += 1
+ ang = 2.0 * math.pi * i / len(self.pixbufs) - f * 2.0 * math.pi
+
+ iw = pb.get_width()
+ ih = pb.get_height()
+
+ r = radius + (radius / 3.0) * math.sin(f * 2.0 * math.pi);
+
+ xpos = math.floor (xmid + r * math.cos(ang) - iw / 2.0 + 0.5)
+ ypos = math.floor (ymid + r * math.sin(ang) - ih / 2.0 + 0.5)
+
+ if i & 1:
+ k = math.sin(f * 2.0 * math.pi)
+ else:
+ k = math.cos(f * 2.0 * math.pi)
+
+ k = 2.0 * k * k
+ k = max(0.25, k)
+
+ r1.x = xpos
+ r1.y = ypos
+ r1.width = iw * k
+ r1.height = ih * k
+
+ r2.x = 0
+ r2.y = 0
+ r2.width = self.background_width
+ r2.height = self.background_height
+
+ success, dest = Gdk.rectangle_intersect(r1, r2)
+ if success:
+ alpha = 0
+ if i & 1:
+ alpha = max(127, math.fabs(255 * math.sin(f * 2.0 * math.pi)))
+ else:
+ alpha = max(127, math.fabs(255 * math.cos(f * 2.0 * math.pi)))
+
+ pb.composite(self.frame,
+ dest.x, dest.y,
+ dest.width, dest.height,
+ xpos, ypos,
+ k, k,
+ GdkPixbuf.InterpType.NEAREST,
+ alpha)
+
+ self.da.queue_draw()
+
+ self.frame_num += 1
+ return True
+
+ def cleanup_cb(self, widget):
+ GObject.source_remove(self.timeout_id)
+ Gtk.main_quit()
+
+def main(demoapp=None):
+ app = PixbufApp()
+ Gtk.main()
+
+if __name__ == '__main__':
+ main()
diff --git a/demos/gtk-demo/demos/printing.py b/demos/gtk-demo/demos/printing.py
new file mode 100644
index 00000000..d0bc6d21
--- /dev/null
+++ b/demos/gtk-demo/demos/printing.py
@@ -0,0 +1,173 @@
+#!/usr/bin/env python
+# -*- Mode: Python; py-indent-offset: 4 -*-
+# vim: tabstop=4 shiftwidth=4 expandtab
+#
+# Copyright (C) 2010 Red Hat, Inc., John (J5) Palmieri <johnp@redhat.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
+# USA
+
+title = "Printing"
+description = """
+GtkPrintOperation offers a simple API to support printing in a cross-platform way.
+"""
+
+from gi.repository import Gtk, GLib, Pango, PangoCairo
+import cairo
+import math
+import os
+from os import path
+
+class PrintingApp:
+ HEADER_HEIGHT = 10*72/25.4
+ HEADER_GAP = 3*72/25.4
+
+ def __init__(self):
+ self.operation = Gtk.PrintOperation()
+ print_data = {'filename': 'printing.py',
+ 'font_size': 12.0,
+ 'lines_per_page': 0,
+ 'lines': None,
+ 'num_lines': 0,
+ 'num_pages': 0
+ }
+
+ self.operation.connect('begin-print', self.begin_print, print_data)
+ self.operation.connect('draw-page', self.draw_page, print_data)
+ self.operation.connect('end-print', self.end_print, print_data)
+
+ self.operation.set_use_full_page(False)
+ self.operation.set_unit(Gtk.Unit.POINTS)
+ self.operation.set_embed_page_setup(True)
+
+ settings = Gtk.PrintSettings()
+
+ dir = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_DOCUMENTS)
+ if dir is None:
+ dir = GLib.get_home_dir()
+ if settings.get(Gtk.PRINT_SETTINGS_OUTPUT_FILE_FORMAT) == 'ps':
+ ext = '.ps'
+ elif settings.get(Gtk.PRINT_SETTINGS_OUTPUT_FILE_FORMAT) == 'svg':
+ ext = '.svg'
+ else:
+ ext = '.pdf'
+
+ uri = "file://%s/gtk-demo%s" % (dir, ext);
+ settings.set(Gtk.PRINT_SETTINGS_OUTPUT_URI, uri)
+ self.operation.set_print_settings(settings)
+
+ try:
+ self.operation.run(Gtk.PrintOperationAction.PRINT_DIALOG, None)
+ except GLib.Error as e:
+ dialog = Gtk.MessageDialog(None,
+ 0,
+ Gtk.MessageType.ERROR,
+ Gtk.ButtonsType.CLOSE,
+ e.message)
+
+ dialog.run()
+ dialog.destroy()
+
+ def begin_print(self, operation, print_ctx, print_data):
+ height = print_ctx.get_height() - self.HEADER_HEIGHT - self.HEADER_GAP
+ print_data['lines_per_page'] = \
+ math.floor(height / print_data['font_size'])
+
+ file_path = print_data['filename']
+ if not path.isfile(file_path):
+ file_path = path.join('demos', file_path)
+ if not path.isfile:
+ raise FileNotFoundError()
+
+ # in reality you should most likely not read the entire
+ # file into a buffer
+ source_file = open(file_path, 'r')
+ s = source_file.read()
+ print_data['lines'] = s.split('\n')
+
+ print_data['num_lines'] = len(print_data['lines'])
+ print_data['num_pages'] = \
+ (print_data['num_lines'] - 1) / print_data['lines_per_page'] + 1
+
+ operation.set_n_pages(print_data['num_pages'])
+
+ def draw_page(self, operation, print_ctx, page_num, print_data):
+ cr = print_ctx.get_cairo_context()
+ width = print_ctx.get_width()
+
+ cr.rectangle(0, 0, width, self.HEADER_HEIGHT)
+ cr.set_source_rgb(0.8, 0.8, 0.8)
+ cr.fill_preserve()
+
+ cr.set_source_rgb(0, 0, 0)
+ cr.set_line_width(1)
+ cr.stroke()
+
+ layout = print_ctx.create_pango_layout()
+ desc = Pango.FontDescription('sans 14')
+ layout.set_font_description(desc)
+
+ layout.set_text(print_data['filename'], -1)
+ (text_width, text_height) = layout.get_pixel_size()
+
+ if text_width > width:
+ layout.set_width(width)
+ layout.set_ellipsize(Pango.EllipsizeType.START);
+ (text_width, text_height) = layout.get_pixel_size(layout)
+
+ cr.move_to ((width - text_width) / 2,
+ (self.HEADER_HEIGHT - text_height) / 2)
+ PangoCairo.show_layout(cr, layout)
+
+ page_str = "%d/%d" % (page_num + 1, print_data['num_pages'])
+ layout.set_text(page_str, -1)
+
+ layout.set_width(-1)
+ (text_width, text_height) = layout.get_pixel_size()
+ cr.move_to(width - text_width - 4,
+ (self.HEADER_HEIGHT - text_height) / 2)
+ PangoCairo.show_layout(cr, layout);
+
+ layout = print_ctx.create_pango_layout()
+
+ desc = Pango.FontDescription('monospace')
+ desc.set_size(print_data['font_size'] * Pango.SCALE)
+ layout.set_font_description(desc)
+
+ cr.move_to (0, self.HEADER_HEIGHT + self.HEADER_GAP)
+ lines_pp = int(print_data['lines_per_page'])
+ num_lines = print_data['num_lines']
+ data_lines = print_data['lines']
+ font_size = print_data['font_size']
+ line = page_num * lines_pp
+
+ for i in range(lines_pp):
+ if line >= num_lines:
+ break;
+
+ layout.set_text(data_lines[line], -1)
+ PangoCairo.show_layout(cr, layout)
+ cr.rel_move_to (0, font_size)
+ line += 1
+
+ def end_print(self, operation, print_ctx, print_data):
+ pass
+
+def main(demoapp=None):
+ app = PrintingApp()
+ Gtk.main()
+
+if __name__ == '__main__':
+ main()
diff --git a/demos/gtk-demo/demos/rotatedtext.py b/demos/gtk-demo/demos/rotatedtext.py
new file mode 100644
index 00000000..af33319a
--- /dev/null
+++ b/demos/gtk-demo/demos/rotatedtext.py
@@ -0,0 +1,196 @@
+#!/usr/bin/env python
+# coding=utf-8
+# -*- Mode: Python; py-indent-offset: 4 -*-
+# vim: tabstop=4 shiftwidth=4 expandtab
+#
+# Copyright (C) 2010 Red Hat, Inc., John (J5) Palmieri <johnp@redhat.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
+# USA
+
+title = "Rotated Text"
+description = """This demo shows how to use PangoCairo to draw rotated and transformed text. The right pane shows a rotated GtkLabel widget.
+In both cases, a custom PangoCairo shape renderer is installed to draw a red heard using cairo drawing operations instead of the Unicode heart character.
+"""
+
+from gi.repository import Gtk, Pango, PangoCairo, Gdk
+import cairo
+import sys
+import math
+
+# Python 2 and 3 handle UTF8 differently
+if sys.version_info < (3, 0):
+ BYTES_TEXT = "I \xe2\x99\xa5 GTK+"
+ UTF8_TEXT = unicode(BYTES_TEXT, 'UTF-8')
+ BYTES_HEART = "\xe2\x99\xa5"
+ HEART = unicode(BYTES_HEART, 'UTF-8')
+else:
+ UTF8_TEXT = "I ♥ GTK+"
+ BYTES_TEXT = bytes(UTF8_TEXT, 'utf-8')
+ HEART = "♥"
+ BYTES_HEART = bytes(HEART, 'utf-8')
+
+class RotatedTextApp:
+ RADIUS = 150
+ N_WORDS = 5
+ FONT = "Serif 18"
+
+ def __init__(self):
+
+ white = Gdk.RGBA()
+ white.red = 1.0
+ white.green = 1.0
+ white.blue = 1.0
+ white.alpha = 1.0
+
+ self.window = Gtk.Window(title="Rotated Text")
+ self.window.set_default_size(4 * self.RADIUS, 2 * self.RADIUS)
+ self.window.connect('destroy', Gtk.main_quit)
+
+ box = Gtk.HBox()
+ box.set_homogeneous(True)
+ self.window.add(box)
+
+ # add a drawing area
+ da = Gtk.DrawingArea()
+ box.add(da)
+
+ # override the background color from the theme
+ da.override_background_color(0, white)
+
+ da.connect('draw', self.rotated_text_draw)
+
+ label = Gtk.Label(UTF8_TEXT)
+ box.add(label)
+ label.set_angle(45)
+
+ # Setup some fancy stuff on the label
+ layout = label.get_layout()
+
+ PangoCairo.context_set_shape_renderer(layout.get_context(),
+ self.fancy_shape_renderer,
+ None)
+ attrs = self.create_fancy_attr_list_for_layout(layout)
+ label.set_attributes(attrs)
+
+ self.window.show_all()
+
+ def fancy_shape_renderer(self, cairo_ctx, attr, do_path):
+ x, y = cairo_ctx.get_current_point()
+ cairo_ctx.translate(x, y)
+
+ cairo_ctx.scale(float(attr.inc_rect.width) / Pango.SCALE,
+ float(attr.inc_rect.height) / Pango.SCALE)
+
+ if int(attr.data) == 0x2665: # U+2665 BLACK HEART SUIT
+ cairo_ctx.move_to(0.5, 0.0)
+ cairo_ctx.line_to(0.9, -0.4)
+ cairo_ctx.curve_to(1.1, -0.8, 0.5, -0.9, 0.5, -0.5)
+ cairo_ctx.curve_to(0.5, -0.9, -0.1, -0.8, 0.1, -0.4)
+ cairo_ctx.close_path()
+
+ if not do_path:
+ cairo_ctx.set_source_rgb(1.0, 0.0, 0.0)
+ cairo_ctx.fill()
+
+ def create_fancy_attr_list_for_layout(self, layout):
+ pango_ctx = layout.get_context()
+ metrics = pango_ctx.get_metrics(layout.get_font_description(),
+ None)
+ ascent = metrics.get_ascent()
+
+ logical_rect = Pango.Rectangle()
+ logical_rect.x = 0
+ logical_rect.width = ascent
+ logical_rect.y = -ascent
+ logical_rect.height = ascent
+
+ ink_rect = logical_rect
+
+ # Set fancy shape attributes for all hearts
+ attrs = Pango.AttrList()
+ p = BYTES_TEXT.find(BYTES_HEART)
+
+ # FIXME: attr_shape_new_with_data isn't introspectable
+ '''
+ while (p != -1):
+ attr = Pango.attr_shape_new_with_data(ink_rect,
+ logical_rect,
+ ord(HEART),
+ None)
+ attr.start_index = p
+ attr.end_index = p + len(BYTES_HEART)
+ p = UTF8_TEXT.find(HEART, attr.end_index)
+
+ attrs.insert(attr)
+ '''
+ return attrs
+
+ def rotated_text_draw (self, da, cairo_ctx):
+ # Create a cairo context and set up a transformation matrix so that the user
+ # space coordinates for the centered square where we draw are [-RADIUS, RADIUS],
+ # [-RADIUS, RADIUS].
+ # We first center, then change the scale.
+ width = da.get_allocated_width()
+ height = da.get_allocated_height()
+ device_radius = min(width, height) / 2.0
+ cairo_ctx.translate(
+ device_radius + (width - 2 * device_radius) / 2,
+ device_radius + (height - 2 * device_radius) / 2)
+ cairo_ctx.scale(device_radius / self.RADIUS,
+ device_radius / self.RADIUS)
+
+ # Create a subtle gradient source and use it.
+ pattern = cairo.LinearGradient(-self.RADIUS, -self.RADIUS,
+ self.RADIUS, self.RADIUS)
+ pattern.add_color_stop_rgb(0.0, 0.5, 0.0, 0.0)
+ pattern.add_color_stop_rgb(1.0, 0.0, 0.0, 0.5)
+ cairo_ctx.set_source(pattern)
+
+ # Create a PangoContext and set up our shape renderer
+ context = da.create_pango_context()
+ PangoCairo.context_set_shape_renderer(context,
+ self.fancy_shape_renderer,
+ None)
+
+ # Create a PangoLayout, set the text, font, and attributes */
+ layout = Pango.Layout(context=context)
+ layout.set_text(UTF8_TEXT, -1)
+ desc = Pango.FontDescription(self.FONT)
+ layout.set_font_description(desc)
+
+ attrs = self.create_fancy_attr_list_for_layout(layout)
+ layout.set_attributes(attrs)
+
+ # Draw the layout N_WORDS times in a circle */
+ for i in range(self.N_WORDS):
+ # Inform Pango to re-layout the text with the new transformation matrix
+ PangoCairo.update_layout(cairo_ctx, layout)
+
+ width, height = layout.get_pixel_size()
+ cairo_ctx.move_to(-width / 2, -self.RADIUS * 0.9)
+ PangoCairo.show_layout(cairo_ctx, layout)
+
+ # Rotate for the next turn
+ cairo_ctx.rotate(math.pi*2 / self.N_WORDS)
+
+ return False
+
+def main(demoapp=None):
+ app = RotatedTextApp()
+ Gtk.main()
+
+if __name__ == '__main__':
+ main()
diff --git a/demos/gtk-demo/gtk-demo.py b/demos/gtk-demo/gtk-demo.py
index 41edc321..f0f27bbe 100755
--- a/demos/gtk-demo/gtk-demo.py
+++ b/demos/gtk-demo/gtk-demo.py
@@ -124,7 +124,7 @@ class GtkDemoApp(object):
try:
demo = Demo(module.title, module, f)
- except AttributeError, e:
+ except AttributeError as e:
raise AttributeError('(%s): %s' % (f, e.message))
demo_list.append(demo)
@@ -146,7 +146,7 @@ class GtkDemoApp(object):
if filename.endswith('.py'):
demo_file_list.append(fullname)
- demo_file_list.sort(lambda a, b: cmp(a.lower(), b.lower()))
+ demo_file_list = sorted(demo_file_list, key=str.lower)
self.load_demos_from_list(demo_file_list, demo_list)
@@ -175,10 +175,11 @@ class GtkDemoApp(object):
Gtk.Window.set_default_icon_list(list)
def selection_cb(self, selection, model):
- (success, m, treeiter) = selection.get_selected()
- if not success:
+ sel = selection.get_selected()
+ if sel == ():
return
+ treeiter = sel[1]
demo = model.get_value(treeiter, 1)
title = demo.title
@@ -212,9 +213,10 @@ class GtkDemoApp(object):
self.source_buffer.insert(end, code)
def row_activated_cb(self, view, path, col, store):
- (success, treeiter) = store.get_iter(path)
+ treeiter = store.get_iter(path)
demo = store.get_value(treeiter, 1)
- demo.module.main(self)
+ if not demo.isdir:
+ demo.module.main(self)
def create_tree(self):
tree_store = Gtk.TreeStore(str, Demo, Pango.Style)
@@ -244,10 +246,10 @@ class GtkDemoApp(object):
text = 0,
style = 2)
+ first_iter = tree_store.get_iter_first()
+ if first_iter is not None:
+ selection.select_iter(first_iter)
-
- (success, first_iter) = tree_store.get_iter_first()
- selection.select_iter(first_iter)
selection.connect('changed', self.selection_cb, tree_store)
tree_view.connect('row_activated', self.row_activated_cb, tree_store)
@@ -288,7 +290,7 @@ class GtkDemoApp(object):
scrolled_window.add(text_view)
if is_source:
- font_desc = Pango.Font.description_from_string('monospace')
+ font_desc = Pango.FontDescription('monospace')
text_view.modify_font(font_desc)
text_view.set_wrap_mode(Gtk.WrapMode.NONE)
else:
diff --git a/docs/.gitignore b/docs/.gitignore
deleted file mode 100644
index 6f502547..00000000
--- a/docs/.gitignore
+++ /dev/null
@@ -1,7 +0,0 @@
-# Configuration-generated files.
-fixxref.py
-reference/entities.docbook
-
-# Documentation build output.
-html
-reference/builddate.xml
diff --git a/docs/Makefile.am b/docs/Makefile.am
index 391eec8c..418df8ad 100644
--- a/docs/Makefile.am
+++ b/docs/Makefile.am
@@ -193,3 +193,5 @@ $(HTML_FILES): $(XML_FILES) $(XSL_FILES)
# pdfxmltex pygobject-ref.fo >output </dev/null
endif
+
+-include $(top_srcdir)/git.mk
diff --git a/dsextras.py b/dsextras.py
index c90b82e1..b2c9e900 100644
--- a/dsextras.py
+++ b/dsextras.py
@@ -1,69 +1,96 @@
+# -*- coding: utf-8 -*-
#
-# dsextras.py - Extra classes and utilities for distutils, adding
-# pkg-config support
+# dsextras.py - Extra classes and utilities for distutils,
+# adding pkg-config support
+import os
+import sys
+import fnmatch
+import re
+import string
+
+from distutils import dep_util
from distutils.command.build_ext import build_ext
from distutils.command.install_lib import install_lib
from distutils.command.install_data import install_data
from distutils.extension import Extension
-import distutils.dep_util
-import fnmatch
-import os
-import re
-import string
-import sys
+from distutils.spawn import find_executable
+
+try:
+ import codegen.createdefs
+ from codegen.override import Overrides
+ from codegen.defsparser import DefsParser
+ from codegen.codegen import register_types, SourceWriter, FileOutput
+except ImportError:
+ template_classes_enabled = False
+else:
+ template_classes_enabled = True
+
GLOBAL_INC = []
GLOBAL_MACROS = []
+codegen_error_message='''
+***************************************************************************
+Codegen could not be found on your system and is required by the
+dsextras.Template and dsextras.TemplateExtension classes.
+***************************************************************************
+'''
+
+
def get_m4_define(varname):
- """Return the value of a m4_define variable as set in configure.in."""
- pattern = re.compile("m4_define\(" + varname + "\,\s*(.+)\)")
+ '''Return the value of a m4_define variable as set in configure.in.'''
+ pattern = re.compile('m4_define\(' + varname + '\,\s*(.+)\)')
+
if os.path.exists('configure.ac'):
fname = 'configure.ac'
elif os.path.exists('configure.in'):
fname = 'configure.in'
else:
- raise SystemExit('could not find configure file')
+ raise SystemExit('ERROR: Could not find configure file')
for line in open(fname).readlines():
match_obj = pattern.match(line)
+
if match_obj:
return match_obj.group(1)
return None
def getoutput(cmd):
- """Return output (stdout or stderr) of executing cmd in a shell."""
+ '''Return output (stdout or stderr) of executing cmd in a shell.'''
return getstatusoutput(cmd)[1]
def getstatusoutput(cmd):
- """Return (status, output) of executing cmd in a shell."""
+ '''Return (status, output) of executing cmd in a shell.'''
if sys.platform == 'win32':
pipe = os.popen(cmd, 'r')
text = pipe.read()
sts = pipe.close() or 0
- if text[-1:] == '\n':
+
+ while text[-1:] in ['\n', '\r']:
text = text[:-1]
+
return sts, text
else:
from commands import getstatusoutput
return getstatusoutput(cmd)
+def have_gcc():
+ '''Checks for the existence of gcc'''
+ if find_executable('gcc'):
+ return True
+
def have_pkgconfig():
- """Checks for the existence of pkg-config"""
- if (sys.platform == 'win32' and
- os.system('pkg-config --version > NUL') == 0):
- return 1
- else:
- if getstatusoutput('pkg-config')[0] == 256:
- return 1
+ '''Checks for the existence of pkg-config'''
+ if find_executable('pkg-config'):
+ return True
def list_files(dir):
- """List all files in a dir, with filename match support:
+ '''List all files in a dir, with filename match support:
for example: glade/*.glade will return all files in the glade directory
- that matches *.glade. It also looks up the full path"""
+ that matches *.glade. It also looks up the full path'''
if dir.find(os.sep) != -1:
parts = dir.split(os.sep)
dir = string.join(parts[:-1], os.sep)
@@ -74,62 +101,82 @@ def list_files(dir):
dir = os.path.abspath(dir)
retval = []
+
for file in os.listdir(dir):
if fnmatch.fnmatch(file, pattern):
retval.append(os.path.join(dir, file))
+
return retval
def pkgc_version_check(name, req_version):
- """Check the existence and version number of a package:
- returns 0 if not installed or too old, 1 otherwise."""
+ '''Check the existence and version number of a package:
+ returns False if not installed or too old, True otherwise.'''
is_installed = not os.system('pkg-config --exists %s' % name)
+
if not is_installed:
- return 0
+ return False
- orig_version = getoutput('pkg-config --modversion %s' % name)
+ orig_version = pkgc_get_version(name)
version = map(int, orig_version.split('.'))
pkc_version = map(int, req_version.split('.'))
if version >= pkc_version:
- return 1
-
- return 0
+ return True
+
+ return False
+
+def pkgc_get_version(name):
+ '''return the version as return by pkg-config --modversion'''
+ return getoutput('pkg-config --modversion %s' % name)
def pkgc_get_libraries(name):
- """returns a list of libraries as returned by pkg-config --libs-only-l"""
+ '''returns a list of libraries as returned by pkg-config --libs-only-l'''
output = getoutput('pkg-config --libs-only-l %s' % name)
return output.replace('-l', '').split()
def pkgc_get_library_dirs(name):
- """returns a list of library dirs as returned by pkg-config --libs-only-L"""
+ '''returns a list of library dirs as returned by pkg-config --libs-only-L'''
output = getoutput('pkg-config --libs-only-L %s' % name)
return output.replace('-L', '').split()
def pkgc_get_include_dirs(name):
- """returns a list of include dirs as returned by pkg-config --cflags-only-I"""
+ '''returns a list of include dirs as returned by pkg-config --cflags-only-I'''
output = getoutput('pkg-config --cflags-only-I %s' % name)
return output.replace('-I', '').split()
+def pkgc_get_defs_dir(name):
+ '''returns the defs dir as returned by pkg-config --variable=defsdir'''
+ output = getoutput('pkg-config --variable=defsdir %s' % name)
+ return output
+
+
class BuildExt(build_ext):
def init_extra_compile_args(self):
self.extra_compile_args = []
- if sys.platform == 'win32' and \
- self.compiler.compiler_type == 'mingw32':
+
+ if sys.platform == 'win32' and self.compiler.compiler_type == 'mingw32':
+ if not have_gcc():
+ raise SystemExit('ERROR: Could not find gcc.')
+
# MSVC compatible struct packing is required.
# Note gcc2 uses -fnative-struct while gcc3
- # uses -mms-bitfields. Based on the version
- # the proper flag is used below.
- msnative_struct = { '2' : '-fnative-struct',
- '3' : '-mms-bitfields',
- '4' : '-mms-bitfields'}
+ # and gcc4 use -mms-bitfields. Based on the
+ # version the proper flag is used below.
+ msnative_struct = {'2': '-fnative-struct',
+ '3': '-mms-bitfields',
+ '4': '-mms-bitfields'}
gcc_version = getoutput('gcc -dumpversion')
+
print ('using MinGW GCC version %s with %s option' % \
(gcc_version, msnative_struct[gcc_version[0]]))
+
self.extra_compile_args.append(msnative_struct[gcc_version[0]])
def modify_compiler(self):
- if sys.platform == 'win32' and \
- self.compiler.compiler_type == 'mingw32':
+ if sys.platform == 'win32' and self.compiler.compiler_type == 'mingw32':
+ if not have_gcc():
+ raise SystemExit('ERROR: Could not find gcc.')
+
# Remove '-static' linker option to prevent MinGW ld
# from trying to link with MSVC import libraries.
if self.compiler.linker_so.count('-static'):
@@ -146,23 +193,27 @@ class BuildExt(build_ext):
def build_extension(self, ext):
# Add self.extra_compile_args to ext.extra_compile_args
ext.extra_compile_args += self.extra_compile_args
+
# Generate eventual templates before building
if hasattr(ext, 'generate'):
ext.generate()
+
# Filter out 'c' and 'm' libs when compilic w/ msvc
if sys.platform == 'win32' and self.compiler.compiler_type == 'msvc':
save_libs = ext.libraries
- ext.libraries = [lib for lib in ext.libraries
+ ext.libraries = [lib for lib in ext.libraries
if lib not in ['c', 'm']]
else:
save_libs = ext.libraries
+
# Invoke base build_extension()
build_ext.build_extension(self, ext)
- if save_libs != None and save_libs != ext.libraries:
+
+ if save_libs is not None and save_libs != ext.libraries:
ext.libraries = save_libs
-class InstallLib(install_lib):
+class InstallLib(install_lib):
local_outputs = []
local_inputs = []
@@ -175,17 +226,17 @@ class InstallLib(install_lib):
def get_inputs(self):
return install_lib.get_inputs(self) + self.local_inputs
-class InstallData(install_data):
+class InstallData(install_data):
local_outputs = []
local_inputs = []
template_options = {}
def prepare(self):
- if os.name == "nt":
+ if os.name == 'nt':
self.prefix = os.sep.join(self.install_dir.split(os.sep)[:-3])
else:
- # default: os.name == "posix"
+ # default: os.name == 'posix'
self.prefix = os.sep.join(self.install_dir.split(os.sep)[:-4])
self.exec_prefix = '${prefix}/bin'
@@ -210,16 +261,17 @@ class InstallData(install_data):
self.template_options['@%s@' % name] = value
def install_template(self, filename, install_dir):
- """Install template filename into target directory install_dir."""
+ '''Install template filename into target directory install_dir.'''
output_file = os.path.split(filename)[-1][:-3]
template = open(filename).read()
+
for key, value in self.template_options.items():
template = template.replace(key, value)
output = os.path.join(install_dir, output_file)
self.mkpath(install_dir)
- open(output, 'w').write(template)
+ open(output, 'wb').write(template)
self.local_inputs.append(filename)
self.local_outputs.append(output)
return output
@@ -230,31 +282,40 @@ class InstallData(install_data):
def get_inputs(self):
return install_data.get_inputs(self) + self.local_inputs
+
class PkgConfigExtension(Extension):
# Name of pygobject package extension depends on, can be None
pygobject_pkc = 'pygobject-2.0'
can_build_ok = None
+
def __init__(self, **kwargs):
name = kwargs['pkc_name']
+
if 'include_dirs' in kwargs:
kwargs['include_dirs'] += self.get_include_dirs(name) + GLOBAL_INC
else:
kwargs['include_dirs'] = self.get_include_dirs(name) + GLOBAL_INC
+
kwargs['define_macros'] = GLOBAL_MACROS
+
if 'libraries' in kwargs:
kwargs['libraries'] += self.get_libraries(name)
else:
kwargs['libraries'] = self.get_libraries(name)
+
if 'library_dirs' in kwargs:
kwargs['library_dirs'] += self.get_library_dirs(name)
else:
kwargs['library_dirs'] = self.get_library_dirs(name)
+
if 'pygobject_pkc' in kwargs:
self.pygobject_pkc = kwargs.pop('pygobject_pkc')
+
if self.pygobject_pkc:
kwargs['include_dirs'] += self.get_include_dirs(self.pygobject_pkc)
kwargs['libraries'] += self.get_libraries(self.pygobject_pkc)
kwargs['library_dirs'] += self.get_library_dirs(self.pygobject_pkc)
+
self.name = kwargs['name']
self.pkc_name = kwargs['pkc_name']
self.pkc_version = kwargs['pkc_version']
@@ -264,33 +325,39 @@ class PkgConfigExtension(Extension):
def get_include_dirs(self, names):
if type(names) != tuple:
names = (names,)
+
retval = []
+
for name in names:
- output = getoutput('pkg-config --cflags-only-I %s' % name)
- retval.extend(output.replace('-I', '').split())
+ retval.extend(pkgc_get_include_dirs(name))
+
return retval
def get_libraries(self, names):
if type(names) != tuple:
names = (names,)
+
retval = []
+
for name in names:
- output = getoutput('pkg-config --libs-only-l %s' % name)
- retval.extend(output.replace('-l', '').split())
+ retval.extend(pkgc_get_libraries(name))
+
return retval
def get_library_dirs(self, names):
if type(names) != tuple:
names = (names,)
+
retval = []
+
for name in names:
- output = getoutput('pkg-config --libs-only-L %s' % name)
- retval.extend(output.replace('-L', '').split())
+ retval.extend(pkgc_get_library_dirs(name))
+
return retval
def can_build(self):
- """If the pkg-config version found is good enough"""
- if self.can_build_ok != None:
+ '''If the pkg-config version found is good enough'''
+ if self.can_build_ok is not None:
return self.can_build_ok
if type(self.pkc_name) != tuple:
@@ -300,57 +367,42 @@ class PkgConfigExtension(Extension):
for package, version in reqs:
retval = os.system('pkg-config --exists %s' % package)
+
if retval:
- print ("* %s.pc could not be found, bindings for %s"
- " will not be built." % (package, self.name))
- self.can_build_ok = 0
- return 0
+ print ('* %s.pc could not be found, bindings for %s'
+ ' will not be built.' % (package, self.name))
+ self.can_build_ok = False
+ return False
+
+ orig_version = pkgc_get_version(package)
- orig_version = getoutput('pkg-config --modversion %s' %
- package)
if (map(int, orig_version.split('.')) >=
map(int, version.split('.'))):
- self.can_build_ok = 1
- return 1
+
+ self.can_build_ok = True
+ return True
else:
- print ("Warning: Too old version of %s" % package)
- print (" Need %s, but %s is installed" % \
- (version, orig_version))
- self.can_build_ok = 0
- return 0
+ print ('Warning: Too old version of %s' % package)
+ print (' Need %s, but %s is installed' % (version, orig_version))
+ self.can_build_ok = False
+ return False
def generate(self):
pass
-# The Template and TemplateExtension classes require codegen
-
-template_classes_enabled=True
-codegen_error_message="""
-***************************************************************************
-Codegen could not be found on your system and is required by the
-dsextras.Template and dsextras.TemplateExtension classes.
-***************************************************************************
-"""
-try:
- from codegen.override import Overrides
- from codegen.defsparser import DefsParser
- from codegen.codegen import register_types, SourceWriter, \
- FileOutput
- import codegen.createdefs
-except ImportError:
- (etype, e) = sys.exc_info()[:2]
- template_classes_enabled=False
class Template(object):
- def __new__(cls,*args, **kwds):
+ def __new__(cls, *args, **kwds):
+ # The Template and TemplateExtension classes require codegen
if not template_classes_enabled:
- raise NameError("'%s' is not defined\n" % cls.__name__
- + codegen_error_message)
+ raise NameError('\'%s\' is not defined\n%s' % (cls.__name__,
+ codegen_error_message))
+
return object.__new__(cls)
def __init__(self, override, output, defs, prefix,
register=[], load_types=None, py_ssize_t_clean=False):
-
+
self.override = override
self.output = output
self.prefix = prefix
@@ -358,15 +410,17 @@ class Template(object):
self.py_ssize_t_clean = py_ssize_t_clean
self.built_defs=[]
- if isinstance(defs,tuple):
+
+ if isinstance(defs, tuple):
self.defs=defs[0]
self.built_defs.append(defs)
else:
self.defs=defs
self.register=[]
+
for r in register:
- if isinstance(r,tuple):
+ if isinstance(r, tuple):
self.register.append(r[0])
self.built_defs.append(r)
else:
@@ -378,25 +432,25 @@ class Template(object):
files.append(self.override)
files.append(self.defs)
- return not distutils.dep_util.newer_group(files,self.output)
+ return not dep_util.newer_group(files, self.output)
def generate_defs(self):
- for (target,sources) in self.built_defs:
- if distutils.dep_util.newer_group(sources,target):
+ for (target, sources) in self.built_defs:
+ if dep_util.newer_group(sources, target):
# createdefs is mostly called from the CLI !
- args=['dummy',target]+sources
+ args=['dummy', target] + sources
codegen.createdefs.main(args)
-
def generate(self):
# Generate defs files if necessary
self.generate_defs()
+
# ... then check the file timestamps
if self.check_dates():
return
for item in self.register:
- dp = DefsParser(item,dict(GLOBAL_MACROS))
+ dp = DefsParser(item, dict(GLOBAL_MACROS))
dp.startParsing()
register_types(dp)
@@ -404,39 +458,44 @@ class Template(object):
globals = {}
execfile(self.load_types, globals)
- dp = DefsParser(self.defs,dict(GLOBAL_MACROS))
+ dp = DefsParser(self.defs, dict(GLOBAL_MACROS))
dp.startParsing()
register_types(dp)
fd = open(self.output, 'w')
- sw = SourceWriter(dp,Overrides(self.override),
- self.prefix,FileOutput(fd,self.output))
+ sw = SourceWriter(dp, Overrides(self.override), self.prefix,
+ FileOutput(fd, self.output))
sw.write(self.py_ssize_t_clean)
fd.close()
+
class TemplateExtension(PkgConfigExtension):
def __new__(cls,*args, **kwds):
if not template_classes_enabled:
- raise NameError("'%s' is not defined\n" % cls.__name__
- + codegen_error_message)
+ raise NameError('\'%s\' is not defined\n%s' % (cls.__name__,
+ codegen_error_message))
+
return PkgConfigExtension.__new__(cls,*args, **kwds)
-
+
def __init__(self, **kwargs):
name = kwargs['name']
defs = kwargs['defs']
- if isinstance(defs,tuple):
+
+ if isinstance(defs, tuple):
output = defs[0][:-5] + '.c'
else:
output = defs[:-5] + '.c'
+
override = kwargs['override']
load_types = kwargs.get('load_types')
- py_ssize_t_clean = kwargs.pop('py_ssize_t_clean',False)
+ py_ssize_t_clean = kwargs.pop('py_ssize_t_clean', False)
self.templates = []
self.templates.append(Template(override, output, defs, 'py' + name,
kwargs['register'], load_types,
py_ssize_t_clean))
del kwargs['register'], kwargs['override'], kwargs['defs']
+
if load_types:
del kwargs['load_types']
@@ -448,5 +507,3 @@ class TemplateExtension(PkgConfigExtension):
def generate(self):
map(lambda x: x.generate(), self.templates)
-
-
diff --git a/examples/Makefile.am b/examples/Makefile.am
index 7314b61a..b680b903 100644
--- a/examples/Makefile.am
+++ b/examples/Makefile.am
@@ -1,2 +1,4 @@
EXTRA_DIST = properties.py signal.py option.py cairo-demo.py
+
+-include $(top_srcdir)/git.mk
diff --git a/gi/.gitignore b/gi/.gitignore
deleted file mode 100644
index 24694626..00000000
--- a/gi/.gitignore
+++ /dev/null
@@ -1,40 +0,0 @@
-.libs/
-.deps/
-/COPYING
-Makefile
-Makefile.in
-/aclocal.m4
-/autom4te.cache/
-/config.guess
-/config.h
-/config.h.in
-/config.log
-/config.status
-/config.sub
-/configure
-/depcomp
-/install-sh
-/libtool
-/ltmain.sh
-/m4/
-/missing
-/py-compile
-/pygi-*.tar.gz
-/stamp-h1
-
-*.o
-*.lo
-*.la
-*.so
-*.pyc
-*.gir
-*.typelib
-
-.*.swp
-
-*~
-*.orig
-*.rej
-
-tests/tmp-introspect*
-
diff --git a/gi/Makefile.am b/gi/Makefile.am
index 2fbb4ac0..31f6c790 100644
--- a/gi/Makefile.am
+++ b/gi/Makefile.am
@@ -1,7 +1,7 @@
PLATFORM_VERSION = 2.0
pkgincludedir = $(includedir)/pygtk-$(PLATFORM_VERSION)
-pkgpyexecdir = $(pyexecdir)/gtk-2.0
+pkgpyexecdir = $(pyexecdir)
SUBDIRS = \
repository \
@@ -24,7 +24,8 @@ _gi_la_LDFLAGS = \
-avoid-version \
-export-symbols-regex "init_gi|PyInit__gi"
_gi_la_LIBADD = \
- $(GI_LIBS)
+ $(GI_LIBS) \
+ $(top_builddir)/glib/libpyglib-2.0-@PYTHON_BASENAME@.la
_gi_la_SOURCES = \
pygi-repository.c \
pygi-repository.h \
@@ -52,6 +53,8 @@ _gi_la_SOURCES = \
pygi-private.h \
pygi-property.c \
pygi-property.h \
+ pygi-signal-closure.c \
+ pygi-signal-closure.h \
pygobject-external.h \
gimodule.c
@@ -87,3 +90,5 @@ check-local: $(LTLIBRARIES:.la=.so)
clean-local:
rm -f $(LTLIBRARIES:.la=.so)
+
+-include $(top_srcdir)/git.mk
diff --git a/gi/__init__.py b/gi/__init__.py
index fb711c38..314d5797 100644
--- a/gi/__init__.py
+++ b/gi/__init__.py
@@ -20,8 +20,36 @@
from __future__ import absolute_import
-from ._gi import _API
+from ._gi import _API, Repository
# Force loading the GObject typelib so we have available the wrappers for
# base classes such as GInitiallyUnowned
from gi.repository import GObject
+
+_versions = {}
+
+def require_version(namespace, version):
+ repository = Repository.get_default()
+
+ if namespace in repository.get_loaded_namespaces():
+ loaded_version = repository.get_version(namespace)
+ if loaded_version != version:
+ raise ValueError('Namespace %s is already loaded with version %s' % \
+ (namespace, loaded_version))
+
+ if namespace in _versions and _versions[namespace] != version:
+ raise ValueError('Namespace %s already requires version %s' % \
+ (namespace, _versions[namespace]))
+
+ available_versions = repository.enumerate_versions(namespace)
+ if not available_versions:
+ raise ValueError('Namespace %s not available' % namespace)
+
+ if version not in available_versions:
+ raise ValueError('Namespace %s not available for version %s' % \
+ (namespace, version))
+
+ _versions[namespace] = version
+
+def get_required_version(namespace):
+ return _versions.get(namespace, None)
diff --git a/gi/gimodule.c b/gi/gimodule.c
index f7624aeb..2c4c335f 100644
--- a/gi/gimodule.c
+++ b/gi/gimodule.c
@@ -51,6 +51,70 @@ _wrap_pyg_enum_add (PyObject *self,
}
static PyObject *
+_wrap_pyg_enum_register_new_gtype_and_add (PyObject *self,
+ PyObject *args,
+ PyObject *kwargs)
+{
+ static char *kwlist[] = { "info", NULL };
+ PyGIBaseInfo *py_info;
+ GIEnumInfo *info;
+ gint n_values;
+ GEnumValue *g_enum_values;
+ GType g_type;
+ const gchar *type_name;
+
+ if (!PyArg_ParseTupleAndKeywords (args, kwargs,
+ "O:enum_add_make_new_gtype",
+ kwlist, (PyObject *)&py_info)) {
+ return NULL;
+ }
+
+ if (!GI_IS_ENUM_INFO (py_info->info) ||
+ g_base_info_get_type ((GIBaseInfo *) py_info->info) != GI_INFO_TYPE_ENUM) {
+ PyErr_SetString (PyExc_TypeError, "info must be an EnumInfo with info type GI_INFO_TYPE_ENUM");
+ return NULL;
+ }
+
+ info = (GIEnumInfo *)py_info->info;
+ n_values = g_enum_info_get_n_values (info);
+ g_enum_values = g_new0 (GEnumValue, n_values + 1);
+
+ for (int i=0; i < n_values; i++) {
+ GIValueInfo *value_info;
+ GEnumValue *enum_value;
+ const gchar *name;
+ const gchar *c_identifier;
+
+ value_info = g_enum_info_get_value (info, i);
+ name = g_base_info_get_name ((GIBaseInfo *) value_info);
+ c_identifier = g_base_info_get_attribute ((GIBaseInfo *) value_info,
+ "c:identifier");
+
+ enum_value = &g_enum_values[i];
+ enum_value->value_nick = g_strdup (name);
+ enum_value->value = g_value_info_get_value (value_info);
+
+ if (c_identifier == NULL) {
+ enum_value->value_name = enum_value->value_nick;
+ } else {
+ enum_value->value_name = g_strdup (c_identifier);
+ }
+
+ g_base_info_unref ((GIBaseInfo *) value_info);
+ }
+
+ g_enum_values[n_values].value = 0;
+ g_enum_values[n_values].value_nick = NULL;
+ g_enum_values[n_values].value_name = NULL;
+
+ type_name = g_base_info_get_name ((GIBaseInfo *) info);
+ type_name = g_strdup (type_name);
+ g_type = g_enum_register_static (type_name, g_enum_values);
+
+ return pyg_enum_add (NULL, g_type_name (g_type), NULL, g_type);
+}
+
+static PyObject *
_wrap_pyg_flags_add (PyObject *self,
PyObject *args,
PyObject *kwargs)
@@ -74,6 +138,71 @@ _wrap_pyg_flags_add (PyObject *self,
}
static PyObject *
+_wrap_pyg_flags_register_new_gtype_and_add (PyObject *self,
+ PyObject *args,
+ PyObject *kwargs)
+{
+ static char *kwlist[] = { "info", NULL };
+ PyGIBaseInfo *py_info;
+ GIEnumInfo *info;
+ gint n_values;
+ GFlagsValue *g_flags_values;
+ GType g_type;
+ const gchar *type_name;
+
+ if (!PyArg_ParseTupleAndKeywords (args, kwargs,
+ "O:flags_add_make_new_gtype",
+ kwlist, (PyObject *)&py_info)) {
+ return NULL;
+ }
+
+ if (!GI_IS_ENUM_INFO (py_info->info) ||
+ g_base_info_get_type ((GIBaseInfo *) py_info->info) != GI_INFO_TYPE_FLAGS) {
+ PyErr_SetString (PyExc_TypeError, "info must be an EnumInfo with info type GI_INFO_TYPE_FLAGS");
+ return NULL;
+ }
+
+ info = (GIEnumInfo *)py_info->info;
+ n_values = g_enum_info_get_n_values (info);
+ g_flags_values = g_new0 (GFlagsValue, n_values + 1);
+
+ for (int i=0; i < n_values; i++) {
+ GIValueInfo *value_info;
+ GFlagsValue *flags_value;
+ const gchar *name;
+ const gchar *c_identifier;
+
+ value_info = g_enum_info_get_value (info, i);
+ name = g_base_info_get_name ((GIBaseInfo *) value_info);
+ c_identifier = g_base_info_get_attribute ((GIBaseInfo *) value_info,
+ "c:identifier");
+
+ flags_value = &g_flags_values[i];
+ flags_value->value_nick = g_strdup (name);
+ flags_value->value = g_value_info_get_value (value_info);
+
+ if (c_identifier == NULL) {
+ flags_value->value_name = flags_value->value_nick;
+ } else {
+ flags_value->value_name = g_strdup (c_identifier);
+ }
+
+ g_base_info_unref ((GIBaseInfo *) value_info);
+ }
+
+ g_flags_values[n_values].value = 0;
+ g_flags_values[n_values].value_nick = NULL;
+ g_flags_values[n_values].value_name = NULL;
+
+ type_name = g_base_info_get_name ((GIBaseInfo *) info);
+ type_name = g_strdup (type_name);
+ g_type = g_flags_register_static (type_name, g_flags_values);
+
+ return pyg_flags_add (NULL, g_type_name (g_type), NULL, g_type);
+}
+
+
+static PyObject *
_wrap_pyg_set_object_has_new_constructor (PyObject *self,
PyObject *args,
PyObject *kwargs)
@@ -131,39 +260,25 @@ _wrap_pyg_register_interface_info (PyObject *self, PyObject *args)
Py_RETURN_NONE;
}
-static PyObject *
-_wrap_pyg_hook_up_vfunc_implementation (PyObject *self, PyObject *args)
+static void
+find_vfunc_info (GIBaseInfo *vfunc_info,
+ GType implementor_gtype,
+ gpointer *implementor_class_ret,
+ gpointer *implementor_vtable_ret,
+ GIFieldInfo **field_info_ret)
{
- PyGIBaseInfo *py_info;
- PyObject *py_type;
- PyObject *py_function;
- gpointer implementor_class = NULL;
GType ancestor_g_type = 0;
- GType implementor_gtype = 0;
- gpointer *method_ptr = NULL;
int length, i;
- GIBaseInfo *vfunc_info;
GIBaseInfo *ancestor_info;
GIStructInfo *struct_info;
+ gpointer implementor_class = NULL;
gboolean is_interface = FALSE;
- PyGICClosure *closure = NULL;
-
- if (!PyArg_ParseTuple (args, "O!O!O:hook_up_vfunc_implementation",
- &PyGIBaseInfo_Type, &py_info,
- &PyGTypeWrapper_Type, &py_type,
- &py_function))
- return NULL;
-
- implementor_gtype = pyg_type_from_object (py_type);
- g_assert (G_TYPE_IS_CLASSED (implementor_gtype));
- vfunc_info = py_info->info;
ancestor_info = g_base_info_get_container (vfunc_info);
is_interface = g_base_info_get_type (ancestor_info) == GI_INFO_TYPE_INTERFACE;
ancestor_g_type = g_registered_type_info_get_g_type (
(GIRegisteredTypeInfo *) ancestor_info);
-
implementor_class = g_type_class_ref (implementor_gtype);
if (is_interface) {
GTypeInstance *implementor_iface_class;
@@ -175,23 +290,23 @@ _wrap_pyg_hook_up_vfunc_implementation (PyObject *self, PyObject *args)
"Couldn't find GType of implementor of interface %s. "
"Forgot to set __gtype_name__?",
g_type_name (ancestor_g_type));
- return NULL;
+ return;
}
- g_type_class_unref (implementor_class);
- implementor_class = implementor_iface_class;
+ *implementor_vtable_ret = implementor_iface_class;
struct_info = g_interface_info_get_iface_struct ( (GIInterfaceInfo*) ancestor_info);
- } else
+ } else {
struct_info = g_object_info_get_class_struct ( (GIObjectInfo*) ancestor_info);
+ *implementor_vtable_ret = implementor_class;
+ }
+
+ *implementor_class_ret = implementor_class;
length = g_struct_info_get_n_fields (struct_info);
for (i = 0; i < length; i++) {
GIFieldInfo *field_info;
GITypeInfo *type_info;
- GIBaseInfo *interface_info;
- GICallbackInfo *callback_info;
- gint offset;
field_info = g_struct_info_get_field (struct_info, i);
@@ -202,18 +317,56 @@ _wrap_pyg_hook_up_vfunc_implementation (PyObject *self, PyObject *args)
}
type_info = g_field_info_get_type (field_info);
- if (g_type_info_get_tag (type_info) != GI_TYPE_TAG_INTERFACE) {
+ if (g_type_info_get_tag (type_info) == GI_TYPE_TAG_INTERFACE) {
g_base_info_unref (type_info);
- g_base_info_unref (field_info);
- continue;
+ *field_info_ret = field_info;
+ break;
}
+ g_base_info_unref (type_info);
+ g_base_info_unref (field_info);
+ }
+
+ g_base_info_unref (struct_info);
+}
+
+static PyObject *
+_wrap_pyg_hook_up_vfunc_implementation (PyObject *self, PyObject *args)
+{
+ PyGIBaseInfo *py_info;
+ PyObject *py_type;
+ PyObject *py_function;
+ GType implementor_gtype = 0;
+ gpointer implementor_class = NULL;
+ gpointer implementor_vtable = NULL;
+ GIFieldInfo *field_info = NULL;
+ gpointer *method_ptr = NULL;
+ PyGICClosure *closure = NULL;
+
+ if (!PyArg_ParseTuple (args, "O!O!O:hook_up_vfunc_implementation",
+ &PyGIBaseInfo_Type, &py_info,
+ &PyGTypeWrapper_Type, &py_type,
+ &py_function))
+ return NULL;
+
+ implementor_gtype = pyg_type_from_object (py_type);
+ g_assert (G_TYPE_IS_CLASSED (implementor_gtype));
+
+ find_vfunc_info (py_info->info, implementor_gtype, &implementor_class, &implementor_vtable, &field_info);
+ if (field_info != NULL) {
+ GITypeInfo *type_info;
+ GIBaseInfo *interface_info;
+ GICallbackInfo *callback_info;
+ gint offset;
+
+ type_info = g_field_info_get_type (field_info);
+
interface_info = g_type_info_get_interface (type_info);
g_assert (g_base_info_get_type (interface_info) == GI_INFO_TYPE_CALLBACK);
callback_info = (GICallbackInfo*) interface_info;
offset = g_field_info_get_offset (field_info);
- method_ptr = G_STRUCT_MEMBER_P (implementor_class, offset);
+ method_ptr = G_STRUCT_MEMBER_P (implementor_vtable, offset);
closure = _pygi_make_native_closure ( (GICallableInfo*) callback_info,
GI_SCOPE_TYPE_NOTIFIED, py_function, NULL);
@@ -223,17 +376,53 @@ _wrap_pyg_hook_up_vfunc_implementation (PyObject *self, PyObject *args)
g_base_info_unref (interface_info);
g_base_info_unref (type_info);
g_base_info_unref (field_info);
-
- break;
}
+ g_type_class_unref (implementor_class);
- g_base_info_unref (struct_info);
+ Py_RETURN_NONE;
+}
+
+#if 0
+/* Not used, left around for future reference */
+static PyObject *
+_wrap_pyg_has_vfunc_implementation (PyObject *self, PyObject *args)
+{
+ PyGIBaseInfo *py_info;
+ PyObject *py_type;
+ PyObject *py_ret;
+ gpointer implementor_class = NULL;
+ gpointer implementor_vtable = NULL;
+ GType implementor_gtype = 0;
+ GIFieldInfo *field_info = NULL;
+
+ if (!PyArg_ParseTuple (args, "O!O!:has_vfunc_implementation",
+ &PyGIBaseInfo_Type, &py_info,
+ &PyGTypeWrapper_Type, &py_type))
+ return NULL;
- if (!is_interface)
- g_type_class_unref (implementor_class);
+ implementor_gtype = pyg_type_from_object (py_type);
+ g_assert (G_TYPE_IS_CLASSED (implementor_gtype));
- Py_RETURN_NONE;
+ py_ret = Py_False;
+ find_vfunc_info (py_info->info, implementor_gtype, &implementor_class, &implementor_vtable, &field_info);
+ if (field_info != NULL) {
+ gpointer *method_ptr;
+ gint offset;
+
+ offset = g_field_info_get_offset (field_info);
+ method_ptr = G_STRUCT_MEMBER_P (implementor_vtable, offset);
+ if (*method_ptr != NULL) {
+ py_ret = Py_True;
+ }
+
+ g_base_info_unref (field_info);
+ }
+ g_type_class_unref (implementor_class);
+
+ Py_INCREF(py_ret);
+ return py_ret;
}
+#endif
static PyObject *
_wrap_pyg_variant_new_tuple (PyObject *self, PyObject *args)
@@ -258,7 +447,7 @@ _wrap_pyg_variant_new_tuple (PyObject *self, PyObject *args)
PyObject *value = PyTuple_GET_ITEM (py_values, i);
if (!PyObject_IsInstance (value, py_type)) {
- PyErr_Format (PyExc_TypeError, "argument %d is not a GLib.Variant", i);
+ PyErr_Format (PyExc_TypeError, "argument %" G_GSSIZE_FORMAT " is not a GLib.Variant", i);
return NULL;
}
@@ -293,7 +482,9 @@ _wrap_pyg_variant_type_from_string (PyObject *self, PyObject *args)
static PyMethodDef _gi_functions[] = {
{ "enum_add", (PyCFunction) _wrap_pyg_enum_add, METH_VARARGS | METH_KEYWORDS },
+ { "enum_register_new_gtype_and_add", (PyCFunction) _wrap_pyg_enum_register_new_gtype_and_add, METH_VARARGS | METH_KEYWORDS },
{ "flags_add", (PyCFunction) _wrap_pyg_flags_add, METH_VARARGS | METH_KEYWORDS },
+ { "flags_register_new_gtype_and_add", (PyCFunction) _wrap_pyg_flags_register_new_gtype_and_add, METH_VARARGS | METH_KEYWORDS },
{ "set_object_has_new_constructor", (PyCFunction) _wrap_pyg_set_object_has_new_constructor, METH_VARARGS | METH_KEYWORDS },
{ "register_interface_info", (PyCFunction) _wrap_pyg_register_interface_info, METH_VARARGS },
@@ -307,6 +498,7 @@ static struct PyGI_API CAPI = {
pygi_type_import_by_g_type_real,
pygi_get_property_value_real,
pygi_set_property_value_real,
+ pygi_signal_closure_new_real,
pygi_register_foreign_struct_real,
};
@@ -315,11 +507,11 @@ PYGLIB_MODULE_START(_gi, "_gi")
PyObject *api;
if (pygobject_init (-1, -1, -1) == NULL) {
- return;
+ return PYGLIB_MODULE_ERROR_RETURN;
}
if (_pygobject_import() < 0) {
- return;
+ return PYGLIB_MODULE_ERROR_RETURN;
}
_pygi_repository_register_types (module);
@@ -330,7 +522,7 @@ PYGLIB_MODULE_START(_gi, "_gi")
api = PYGLIB_CPointer_WrapPointer ( (void *) &CAPI, "gi._API");
if (api == NULL) {
- return;
+ return PYGLIB_MODULE_ERROR_RETURN;
}
PyModule_AddObject (module, "_API", api);
}
diff --git a/gi/importer.py b/gi/importer.py
index 1cb9b921..df082748 100644
--- a/gi/importer.py
+++ b/gi/importer.py
@@ -73,6 +73,7 @@ class DynamicImporter(object):
dynamic_module.__loader__ = self
sys.modules[fullname] = dynamic_module
+ dynamic_module._load()
return dynamic_module
diff --git a/gi/module.py b/gi/module.py
index d1aadc23..3e2b59e6 100644
--- a/gi/module.py
+++ b/gi/module.py
@@ -25,6 +25,9 @@ from __future__ import absolute_import
import os
import gobject
+import gi
+from .overrides import registry
+
from ._gi import \
Repository, \
FunctionInfo, \
@@ -38,16 +41,16 @@ from ._gi import \
Struct, \
Boxed, \
enum_add, \
- flags_add
+ enum_register_new_gtype_and_add, \
+ flags_add, \
+ flags_register_new_gtype_and_add
from .types import \
GObjectMeta, \
StructMeta, \
- Function, \
- Enum
+ Function
repository = Repository.get_default()
-
def get_parent_for_object(object_info):
parent_object_info = object_info.get_parent()
@@ -81,14 +84,14 @@ class IntrospectionModule(object):
def __init__(self, namespace, version=None):
repository.require(namespace, version)
self._namespace = namespace
- self.version = version
+ self._version = version
self.__name__ = 'gi.repository.' + namespace
- repository.require(self._namespace, self.version)
+ repository.require(self._namespace, self._version)
self.__path__ = repository.get_typelib_path(self._namespace)
- if self.version is None:
- self.version = repository.get_version(self._namespace)
+ if self._version is None:
+ self._version = repository.get_version(self._namespace)
def __getattr__(self, name):
info = repository.find_by_name(self._namespace, name)
@@ -101,20 +104,28 @@ class IntrospectionModule(object):
wrapper = g_type.pytype
if wrapper is None:
- if g_type.is_a(gobject.TYPE_ENUM):
- wrapper = enum_add(g_type)
- elif g_type.is_a(gobject.TYPE_NONE):
- # An enum with a GType of None is an enum without GType
- wrapper = Enum
+ if info.is_flags():
+ if g_type.is_a(gobject.TYPE_FLAGS):
+ wrapper = flags_add(g_type)
+ else:
+ assert g_type == gobject.TYPE_NONE
+ wrapper = flags_register_new_gtype_and_add(info)
else:
- wrapper = flags_add(g_type)
+ if g_type.is_a(gobject.TYPE_ENUM):
+ wrapper = enum_add(g_type)
+ else:
+ assert g_type == gobject.TYPE_NONE
+ wrapper = enum_register_new_gtype_and_add(info)
wrapper.__info__ = info
wrapper.__module__ = 'gi.repository.' + info.get_namespace()
for value_info in info.get_values():
- name = value_info.get_name().upper()
- setattr(wrapper, name, wrapper(value_info.get_value()))
+ value_name = value_info.get_name().upper()
+ setattr(wrapper, value_name, wrapper(value_info.get_value()))
+
+ if g_type != gobject.TYPE_NONE:
+ g_type.pytype = wrapper
elif isinstance(info, RegisteredTypeInfo):
g_type = info.get_g_type()
@@ -173,6 +184,18 @@ class IntrospectionModule(object):
path = repository.get_typelib_path(self._namespace)
return "<IntrospectionModule %r from %r>" % (self._namespace, path)
+ def __dir__ (self):
+ # Python's default dir() is just dir(self.__class__) + self.__dict__.keys()
+ result = set(dir(self.__class__))
+ result.update(self.__dict__.keys())
+
+ # update *set* because some repository attributes have already been
+ # wrapped by __getattr__() and included in self.__dict__
+ namespace_infos = repository.get_infos(self._namespace)
+ result.update(info.get_name() for info in namespace_infos)
+
+ return list(result)
+
class DynamicGObjectModule(IntrospectionModule):
"""Wrapper for the GObject module
@@ -207,31 +230,49 @@ class DynamicGObjectModule(IntrospectionModule):
class DynamicModule(object):
def __init__(self, namespace):
self._namespace = namespace
- self.introspection_module = None
- self._version = None
+ self._introspection_module = None
self._overrides_module = None
+ self.__path__ = None
- def require_version(self, version):
- if self.introspection_module is not None and \
- self.introspection_module.version != version:
- raise RuntimeError('Module has been already loaded ')
- self._version = version
-
- def _import(self):
- self.introspection_module = IntrospectionModule(self._namespace,
- self._version)
+ def _load(self):
+ version = gi.get_required_version(self._namespace)
+ self._introspection_module = IntrospectionModule(self._namespace,
+ version)
overrides_modules = __import__('gi.overrides', fromlist=[self._namespace])
self._overrides_module = getattr(overrides_modules, self._namespace, None)
self.__path__ = repository.get_typelib_path(self._namespace)
def __getattr__(self, name):
- if self.introspection_module is None:
- self._import()
-
if self._overrides_module is not None:
override_exports = getattr(self._overrides_module, '__all__', ())
if name in override_exports:
return getattr(self._overrides_module, name, None)
+ else:
+ # check the registry just in case the module hasn't loaded yet
+ # TODO: Only gtypes are registered in the registry right now
+ # but it would be nice to register all overrides and
+ # get rid of the module imports. We might actually see a
+ # speedup.
+ key = '%s.%s' % (self._namespace, name)
+ if key in registry:
+ return registry[key]
+
+ return getattr(self._introspection_module, name)
+
+ def __dir__ (self):
+ # Python's default dir() is just dir(self.__class__) + self.__dict__.keys()
+ result = set(dir(self.__class__))
+ result.update(self.__dict__.keys())
+
+ result.update(dir(self._introspection_module))
+ override_exports = getattr(self._overrides_module, '__all__', ())
+ result.update(override_exports)
+ return list(result)
- return getattr(self.introspection_module, name)
+ def __repr__(self):
+ path = repository.get_typelib_path(self._namespace)
+ return "<%s.%s %r from %r>" % (self.__class__.__module__,
+ self.__class__.__name__,
+ self._namespace,
+ path)
diff --git a/gi/overrides/GIMarshallingTests.py b/gi/overrides/GIMarshallingTests.py
index ee01495c..aac88831 100644
--- a/gi/overrides/GIMarshallingTests.py
+++ b/gi/overrides/GIMarshallingTests.py
@@ -18,10 +18,10 @@
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
# USA
-from ..types import override
+from ..overrides import override
from ..importer import modules
-GIMarshallingTests = modules['GIMarshallingTests'].introspection_module
+GIMarshallingTests = modules['GIMarshallingTests']._introspection_module
__all__ = []
diff --git a/gi/overrides/GLib.py b/gi/overrides/GLib.py
index 78d8c352..ac783be6 100644
--- a/gi/overrides/GLib.py
+++ b/gi/overrides/GLib.py
@@ -21,10 +21,21 @@
from ..importer import modules
from .._gi import variant_new_tuple, variant_type_from_string
-GLib = modules['GLib'].introspection_module
+GLib = modules['GLib']._introspection_module
__all__ = []
+def _create_variant(value):
+ '''Create a variant containing the variant "value".
+
+ This is usually done with the GLib.Variant.new_variant() leaf
+ constructor, but this is currently broken, see GNOME#639952.
+ '''
+ builder = GLib.VariantBuilder()
+ builder.init(variant_type_from_string('v'))
+ builder.add_value(value)
+ return builder.end()
+
class _VariantCreator(object):
_LEAF_CONSTRUCTORS = {
@@ -41,115 +52,242 @@ class _VariantCreator(object):
's': GLib.Variant.new_string,
'o': GLib.Variant.new_object_path,
'g': GLib.Variant.new_signature,
- 'v': GLib.Variant.new_variant,
+ #'v': GLib.Variant.new_variant,
+ 'v': _create_variant,
}
- def __init__(self, format_string, args):
- self._format_string = format_string
- self._args = args
+ def _create(self, format, args):
+ '''Create a GVariant object from given format and argument list.
- def create(self):
- if self._format_string_is_leaf():
- return self._new_variant_leaf()
+ This method recursively calls itself for complex structures (arrays,
+ dictionaries, boxed).
- format_char = self._pop_format_char()
- arg = self._pop_arg()
+ Return a tuple (variant, rest_format, rest_args) with the generated
+ GVariant, the remainder of the format string, and the remainder of the
+ arguments.
- if format_char == 'm':
- raise NotImplementedError()
- else:
- builder = GLib.VariantBuilder()
- if format_char == '(':
- builder.init(variant_type_from_string('r'))
- elif format_char == '{':
- builder.init(variant_type_from_string('{?*}'))
+ If args is None, then this won't actually consume any arguments, and
+ just parse the format string and generate empty GVariant structures.
+ This is required for creating empty dictionaries or arrays.
+ '''
+ # leaves (simple types)
+ constructor = self._LEAF_CONSTRUCTORS.get(format[0])
+ if constructor:
+ if args is not None:
+ if not args:
+ raise TypeError('not enough arguments for GVariant format string')
+ v = constructor(args[0])
+ return (constructor(args[0]), format[1:], args[1:])
else:
- raise NotImplementedError()
- format_char = self._pop_format_char()
- while format_char not in [')', '}']:
- builder.add_value(Variant(format_char, arg))
- format_char = self._pop_format_char()
- if self._args:
- arg = self._pop_arg()
- return builder.end()
-
- def _format_string_is_leaf(self):
- format_char = self._format_string[0]
- return not format_char in ['m', '(', '{']
-
- def _format_string_is_nnp(self):
- format_char = self._format_string[0]
- return format_char in ['a', 's', 'o', 'g', '^', '@', '*', '?', 'r',
- 'v', '&']
-
- def _new_variant_leaf(self):
- if self._format_string_is_nnp():
- return self._new_variant_nnp()
-
- format_char = self._pop_format_char()
- arg = self._pop_arg()
-
- return _VariantCreator._LEAF_CONSTRUCTORS[format_char](arg)
-
- def _new_variant_nnp(self):
- format_char = self._pop_format_char()
- arg = self._pop_arg()
-
- if format_char == '&':
- format_char = self._pop_format_char()
-
- if format_char == 'a':
- builder = GLib.VariantBuilder()
- builder.init(variant_type_from_string('a*'))
+ return (None, format[1:], None)
- element_format_string = self._pop_leaf_format_string()
+ if format[0] == '(':
+ return self._create_tuple(format, args)
- if isinstance(arg, dict):
- for element in arg.items():
- value = Variant(element_format_string, *element)
- builder.add_value(value)
- else:
- for element in arg:
- value = Variant(element_format_string, element)
- builder.add_value(value)
- return builder.end()
- elif format_char == '^':
- raise NotImplementedError()
- elif format_char == '@':
- raise NotImplementedError()
- elif format_char == '*':
- raise NotImplementedError()
- elif format_char == 'r':
- raise NotImplementedError()
- elif format_char == '?':
- raise NotImplementedError()
+ if format.startswith('a{'):
+ return self._create_dict(format, args)
+
+ if format[0] == 'a':
+ return self._create_array(format, args)
+
+ raise NotImplementedError('cannot handle GVariant type ' + format)
+
+ def _create_tuple(self, format, args):
+ '''Handle the case where the outermost type of format is a tuple.'''
+
+ format = format[1:] # eat the '('
+ builder = GLib.VariantBuilder()
+ builder.init(variant_type_from_string('r'))
+ if args is not None:
+ if not args or type(args[0]) != type(()):
+ raise (TypeError, 'expected tuple argument')
+
+ for i in range(len(args[0])):
+ if format.startswith(')'):
+ raise (TypeError, 'too many arguments for tuple signature')
+
+ (v, format, _) = self._create(format, args[0][i:])
+ builder.add_value(v)
+ args = args[1:]
+ return (builder.end(), format[1:], args)
+
+ def _create_dict(self, format, args):
+ '''Handle the case where the outermost type of format is a dict.'''
+
+ builder = GLib.VariantBuilder()
+ if args is None or not args[0]:
+ # empty value: we need to call _create() to parse the subtype,
+ # and specify the element type precisely
+ rest_format = self._create(format[2:], None)[1]
+ rest_format = self._create(rest_format, None)[1]
+ if not rest_format.startswith('}'):
+ raise ValueError('dictionary type string not closed with }')
+ rest_format = rest_format[1:] # eat the }
+ element_type = format[:len(format) - len(rest_format)]
+ builder.init(variant_type_from_string(element_type))
else:
- return _VariantCreator._LEAF_CONSTRUCTORS[format_char](arg)
+ builder.init(variant_type_from_string('a{?*}'))
+ for k, v in args[0].items():
+ (key_v, rest_format, _) = self._create(format[2:], [k])
+ (val_v, rest_format, _) = self._create(rest_format, [v])
- def _pop_format_char(self):
- format_char = self._format_string[0]
- self._format_string = self._format_string[1:]
- return format_char
+ if not rest_format.startswith('}'):
+ raise ValueError('dictionary type string not closed with }')
+ rest_format = rest_format[1:] # eat the }
- def _pop_leaf_format_string(self):
- # FIXME: This will break when the leaf is inside a tuple or dict entry
- format_string = self._format_string
- self._format_string = ''
- return format_string
+ entry = GLib.VariantBuilder()
+ entry.init(variant_type_from_string('{?*}'))
+ entry.add_value(key_v)
+ entry.add_value(val_v)
+ builder.add_value(entry.end())
- def _pop_arg(self):
- arg = self._args[0]
- self._args = self._args[1:]
- return arg
+ if args is not None:
+ args = args[1:]
+ return (builder.end(), rest_format, args)
+
+ def _create_array(self, format, args):
+ '''Handle the case where the outermost type of format is an array.'''
+
+ builder = GLib.VariantBuilder()
+ if args is None or not args[0]:
+ # empty value: we need to call _create() to parse the subtype,
+ # and specify the element type precisely
+ rest_format = self._create(format[1:], None)[1]
+ element_type = format[:len(format) - len(rest_format)]
+ builder.init(variant_type_from_string(element_type))
+ else:
+ builder.init(variant_type_from_string('a*'))
+ for i in range(len(args[0])):
+ (v, rest_format, _) = self._create(format[1:], args[0][i:])
+ builder.add_value(v)
+ if args is not None:
+ args = args[1:]
+ return (builder.end(), rest_format, args)
class Variant(GLib.Variant):
- def __new__(cls, format_string, *args):
- creator = _VariantCreator(format_string, args)
- return creator.create()
+ def __new__(cls, format_string, value):
+ '''Create a GVariant from a native Python object.
+
+ format_string is a standard GVariant type signature, value is a Python
+ object whose structure has to match the signature.
+
+ Examples:
+ GLib.Variant('i', 1)
+ GLib.Variant('(is)', (1, 'hello'))
+ GLib.Variant('(asa{sv})', ([], {'foo': GLib.Variant('b', True),
+ 'bar': GLib.Variant('i', 2)}))
+ '''
+ creator = _VariantCreator()
+ (v, rest_format, _) = creator._create(format_string, [value])
+ if rest_format:
+ raise TypeError('invalid remaining format string: "%s"' % rest_format)
+ return v
def __repr__(self):
return '<GLib.Variant(%s)>' % getattr(self, 'print')(True)
+ def unpack(self):
+ '''Decompose a GVariant into a native Python object.'''
+
+ LEAF_ACCESSORS = {
+ 'b': self.get_boolean,
+ 'y': self.get_byte,
+ 'n': self.get_int16,
+ 'q': self.get_uint16,
+ 'i': self.get_int32,
+ 'u': self.get_uint32,
+ 'x': self.get_int64,
+ 't': self.get_uint64,
+ 'h': self.get_handle,
+ 'd': self.get_double,
+ 's': self.get_string,
+ 'o': self.get_string, # object path
+ 'g': self.get_string, # signature
+ }
+
+ # simple values
+ la = LEAF_ACCESSORS.get(self.get_type_string())
+ if la:
+ return la()
+
+ # tuple
+ if self.get_type_string().startswith('('):
+ res = [self.get_child_value(i).unpack()
+ for i in range(self.n_children())]
+ return tuple(res)
+
+ # dictionary
+ if self.get_type_string().startswith('a{'):
+ res = {}
+ for i in range(self.n_children()):
+ v = self.get_child_value(i)
+ res[v.get_child_value(0).unpack()] = v.get_child_value(1).unpack()
+ return res
+
+ # array
+ if self.get_type_string().startswith('a'):
+ return [self.get_child_value(i).unpack()
+ for i in range(self.n_children())]
+
+ # variant (just unbox transparently)
+ if self.get_type_string().startswith('v'):
+ return self.get_variant().unpack()
+
+ raise NotImplementedError('unsupported GVariant type ' + self.get_type_string())
+
+ #
+ # Pythonic iterators
+ #
+ def __len__(self):
+ if self.get_type_string() in ['s', 'o', 'g']:
+ return len(self.get_string())
+ if self.get_type_string().startswith('a') or self.get_type_string().startswith('('):
+ return self.n_children()
+ raise TypeError('GVariant type %s does not have a length' % self.get_type_string())
+
+ def __getitem__(self, key):
+ # dict
+ if self.get_type_string().startswith('a{'):
+ try:
+ val = self.lookup_value(key, variant_type_from_string('*'))
+ if val is None:
+ raise KeyError(key)
+ return val.unpack()
+ except TypeError:
+ # lookup_value() only works for string keys, which is certainly
+ # the common case; we have to do painful iteration for other
+ # key types
+ for i in range(self.n_children()):
+ v = self.get_child_value(i)
+ if v.get_child_value(0).unpack() == key:
+ return v.get_child_value(1).unpack()
+ raise KeyError(key)
+
+ # array/tuple
+ if self.get_type_string().startswith('a') or self.get_type_string().startswith('('):
+ key = int(key)
+ if key < 0:
+ key = self.n_children() + key
+ if key < 0 or key >= self.n_children():
+ raise IndexError('list index out of range')
+ return self.get_child_value(key).unpack()
+
+ # string
+ if self.get_type_string() in ['s', 'o', 'g']:
+ return self.get_string().__getitem__(key)
+
+ raise TypeError('GVariant type %s is not a container' % self.get_type_string())
+
+ def keys(self):
+ if not self.get_type_string().startswith('a{'):
+ return TypeError, 'GVariant type %s is not a dictionary' % self.get_type_string()
+
+ res = []
+ for i in range(self.n_children()):
+ v = self.get_child_value(i)
+ res.append(v.get_child_value(0).unpack())
+ return res
+
@classmethod
def new_tuple(cls, *elements):
return variant_new_tuple(elements)
diff --git a/gi/overrides/Gdk.py b/gi/overrides/Gdk.py
index 23a9d8e8..e346f552 100644
--- a/gi/overrides/Gdk.py
+++ b/gi/overrides/Gdk.py
@@ -19,10 +19,12 @@
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
# USA
-from ..types import override
+from ..overrides import override
from ..importer import modules
-Gdk = modules['Gdk'].introspection_module
+import sys
+
+Gdk = modules['Gdk']._introspection_module
__all__ = []
@@ -43,7 +45,25 @@ class Color(Gdk.Color):
Color = override(Color)
__all__.append('Color')
-if Gdk.version == '2.0':
+if Gdk._version == '3.0':
+ class RGBA(Gdk.RGBA):
+ def __init__(self, red=1.0, green=1.0, blue=1.0, alpha=1.0):
+ Gdk.RGBA.__init__(self)
+ self.red = red
+ self.green = green
+ self.blue = blue
+ self.alpha = alpha
+
+ def __new__(cls, *args, **kwargs):
+ return Gdk.RGBA.__new__(cls)
+
+ def __repr__(self):
+ return '<Gdk.Color(red=%f, green=%f, blue=%f, alpha=%f)>' % (self.red, self.green, self.blue, self.alpha)
+
+ RGBA = override(RGBA)
+ __all__.append('RGBA')
+
+if Gdk._version == '2.0':
class Rectangle(Gdk.Rectangle):
def __init__(self, x, y, width, height):
@@ -61,13 +81,35 @@ if Gdk.version == '2.0':
Rectangle = override(Rectangle)
__all__.append('Rectangle')
+else:
+ from gi.repository import cairo as _cairo
+ Rectangle = _cairo.RectangleInt
+
+ __all__.append('Rectangle')
+
+if Gdk._version == '2.0':
+ class Drawable(Gdk.Drawable):
+ def cairo_create(self):
+ return Gdk.cairo_create(self)
+
+ Drawable = override(Drawable)
+ __all__.append('Drawable')
+else:
+ class Window(Gdk.Window):
+ def __new__(cls, parent, attributes, attributes_mask):
+ # Gdk.Window had to be made abstract,
+ # this override allows using the standard constructor
+ return Gdk.Window.new(parent, attributes, attributes_mask)
+ def __init__(self, parent, attributes, attributes_mask):
+ pass
+ def cairo_create(self):
+ return Gdk.cairo_create(self)
-class Drawable(Gdk.Drawable):
- def cairo_create(self):
- return Gdk.cairo_create(self)
+ Window = override(Window)
+ __all__.append('Window')
-Drawable = override(Drawable)
-__all__.append('Drawable')
+Gdk.EventType._2BUTTON_PRESS = getattr(Gdk.EventType, "2BUTTON_PRESS")
+Gdk.EventType._3BUTTON_PRESS = getattr(Gdk.EventType, "3BUTTON_PRESS")
class Event(Gdk.Event):
_UNION_MEMBERS = {
@@ -76,8 +118,8 @@ class Event(Gdk.Event):
Gdk.EventType.EXPOSE: 'expose',
Gdk.EventType.MOTION_NOTIFY: 'motion',
Gdk.EventType.BUTTON_PRESS: 'button',
- #Gdk.EventType.2BUTTON_PRESS: 'button',
- #Gdk.EventType.3BUTTON_PRESS: 'button',
+ Gdk.EventType._2BUTTON_PRESS: 'button',
+ Gdk.EventType._3BUTTON_PRESS: 'button',
Gdk.EventType.BUTTON_RELEASE: 'button',
Gdk.EventType.KEY_PRESS: 'key',
Gdk.EventType.KEY_RELEASE: 'key',
@@ -101,9 +143,11 @@ class Event(Gdk.Event):
Gdk.EventType.DROP_FINISHED: 'dnd',
Gdk.EventType.CLIENT_EVENT: 'client',
Gdk.EventType.VISIBILITY_NOTIFY: 'visibility',
- Gdk.EventType.NO_EXPOSE: 'no_expose'
}
+ if Gdk._version == '2.0':
+ _UNION_MEMBERS[Gdk.EventType.NO_EXPOSE] = 'no_expose'
+
def __new__(cls, *args, **kwargs):
return Gdk.Event.__new__(cls)
@@ -112,11 +156,127 @@ class Event(Gdk.Event):
if real_event:
return getattr(getattr(self, real_event), name)
else:
- return getattr(self, name)
+ raise AttributeError("'%s' object has no attribute '%s'" % (self.__class__.__name__, name))
Event = override(Event)
__all__.append('Event')
+# manually bind GdkEvent members to GdkEvent
+
+modname = globals()['__name__']
+module = sys.modules[modname]
+
+# right now we can't get the type_info from the
+# field info so manually list the class names
+event_member_classes = ['EventAny',
+ 'EventExpose',
+ 'EventVisibility',
+ 'EventMotion',
+ 'EventButton',
+ 'EventScroll',
+ 'EventKey',
+ 'EventCrossing',
+ 'EventFocus',
+ 'EventConfigure',
+ 'EventProperty',
+ 'EventSelection',
+ 'EventOwnerChange',
+ 'EventProximity',
+ 'EventDND',
+ 'EventWindowState',
+ 'EventSetting',
+ 'EventGrabBroken']
+
+if Gdk._version == '2.0':
+ event_member_classes.append('EventNoExpose')
+
+# whitelist all methods that have a success return we want to mask
+gsuccess_mask_funcs = ['get_state',
+ 'get_axis',
+ 'get_coords',
+ 'get_root_coords']
+
+def _gsuccess_mask(func):
+ def cull_success(*args):
+ result = func(*args)
+ success = result[0]
+ if success == False:
+ return None
+ else:
+ if len(result) == 2:
+ return result[1]
+ else:
+ return result[1:]
+ return cull_success
+
+for event_class in event_member_classes:
+ override_class = type(event_class, (getattr(Gdk, event_class),), {})
+ # add the event methods
+ for method_info in Gdk.Event.__info__.get_methods():
+ name = method_info.get_name()
+ event_method = getattr(Gdk.Event, name)
+ # python2 we need to use the __func__ attr to avoid internal
+ # instance checks
+ event_method = getattr(event_method, '__func__', event_method)
+
+ # use the _gsuccess_mask decorator if this method is whitelisted
+ if name in gsuccess_mask_funcs:
+ event_method = _gsuccess_mask(event_method)
+ setattr(override_class, name, event_method)
+
+ setattr(module, event_class, override_class)
+ __all__.append(event_class)
+
+# end GdkEvent overrides
+
+class DragContext(Gdk.DragContext):
+ def finish(self, success, del_, time):
+ Gtk = modules['Gtk']._introspection_module
+ Gtk.drag_finish(self, success, del_, time)
+
+DragContext = override(DragContext)
+__all__.append('DragContext')
+
+class Cursor(Gdk.Cursor):
+ def __new__(cls, *args, **kwds):
+ arg_len = len(args)
+ kwd_len = len(kwds)
+ total_len = arg_len + kwd_len
+
+ def _new(cursor_type):
+ return cls.new(cursor_type)
+
+ def _new_for_display(display, cursor_type):
+ return cls.new_for_display(display, cursor_type)
+
+ def _new_from_pixbuf(display, pixbuf, x, y):
+ return cls.new_from_pixbuf(display, pixbuf, x, y)
+
+ def _new_from_pixmap(source, mask, fg, bg, x, y):
+ return cls.new_from_pixmap(source, mask, fg, bg, x, y)
+
+ _constructor = None
+ if total_len == 1:
+ _constructor = _new
+ elif total_len == 2:
+ _constructor = _new_for_display
+ elif total_len == 4:
+ _constructor = _new_from_pixbuf
+ elif total_len == 6:
+ if Gdk._version != '2.0':
+ # pixmaps don't exist in Gdk 3.0
+ raise ValueError("Wrong number of parameters")
+ _constructor = _new_from_pixmap
+ else:
+ raise ValueError("Wrong number of parameters")
+
+ return _constructor(*args, **kwds)
+
+ def __init__(self, *args, **kwargs):
+ Gdk.Cursor.__init__(self)
+
+Cursor = override(Cursor)
+__all__.append('Cursor')
import sys
diff --git a/gi/overrides/Gio.py b/gi/overrides/Gio.py
new file mode 100644
index 00000000..20adf0ce
--- /dev/null
+++ b/gi/overrides/Gio.py
@@ -0,0 +1,215 @@
+# -*- Mode: Python; py-indent-offset: 4 -*-
+# vim: tabstop=4 shiftwidth=4 expandtab
+#
+# Copyright (C) 2010 Ignacio Casal Quinteiro <icq@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
+
+from ..overrides import override
+from ..importer import modules
+
+from gi.repository import GLib
+
+import sys
+
+Gio = modules['Gio']._introspection_module
+
+__all__ = []
+
+class FileEnumerator(Gio.FileEnumerator):
+ def __iter__(self):
+ return self
+
+ def __next__(self):
+ file_info = self.next_file(None)
+
+ if file_info is not None:
+ return file_info
+ else:
+ raise StopIteration
+
+ # python 2 compat for the iter protocol
+ next = __next__
+
+
+FileEnumerator = override(FileEnumerator)
+__all__.append('FileEnumerator')
+
+class Settings(Gio.Settings):
+ '''Provide dictionary-like access to GLib.Settings.'''
+
+ def __init__(self, schema, path=None, backend=None):
+ Gio.Settings.__init__(self, schema=schema, backend=backend, path=path)
+
+ def __contains__(self, key):
+ return key in self.list_keys()
+
+ def __len__(self):
+ return len(self.list_keys())
+
+ def __bool__(self):
+ # for "if mysettings" we don't want a dictionary-like test here, just
+ # if the object isn't None
+ return True
+
+ # alias for Python 2.x object protocol
+ __nonzero__ = __bool__
+
+ def __getitem__(self, key):
+ # get_value() aborts the program on an unknown key
+ if not key in self:
+ raise KeyError('unknown key: %r' % (key,))
+
+ return self.get_value(key).unpack()
+
+ def __setitem__(self, key, value):
+ # set_value() aborts the program on an unknown key
+ if not key in self:
+ raise KeyError('unknown key: %r' % (key,))
+
+ # determine type string of this key
+ range = self.get_range(key)
+ type_ = range.get_child_value(0).get_string()
+ v = range.get_child_value(1)
+ if type_ == 'type':
+ # v is boxed empty array, type of its elements is the allowed value type
+ type_str = v.get_child_value(0).get_type_string()
+ assert type_str.startswith('a')
+ type_str = type_str[1:]
+ else:
+ raise NotImplementedError('Cannot handle allowed type range class' + str(type_))
+
+ self.set_value(key, GLib.Variant(type_str, value))
+
+ def keys(self):
+ return self.list_keys()
+
+Settings = override(Settings)
+__all__.append('Settings')
+
+class _DBusProxyMethodCall:
+ '''Helper class to implement DBusProxy method calls.'''
+
+ def __init__(self, dbus_proxy, method_name):
+ self.dbus_proxy = dbus_proxy
+ self.method_name = method_name
+
+ def __async_result_handler(self, obj, result, user_data):
+ (result_callback, error_callback, real_user_data) = user_data
+ try:
+ ret = obj.call_finish(result)
+ except Exception:
+ etype, e = sys.exc_info()[:2]
+ # return exception as value
+ if error_callback:
+ error_callback(obj, e, real_user_data)
+ else:
+ result_callback(obj, e, real_user_data)
+ return
+
+ result_callback(obj, self._unpack_result(ret), real_user_data)
+
+ def __call__(self, *args, **kwargs):
+ # the first positional argument is the signature, unless we are calling
+ # a method without arguments; then signature is implied to be '()'.
+ if args:
+ signature = args[0]
+ args = args[1:]
+ if not isinstance(signature, str):
+ raise TypeError('first argument must be the method signature string: %r' % signature)
+ else:
+ signature = '()'
+
+ arg_variant = GLib.Variant(signature, tuple(args))
+
+ if 'result_handler' in kwargs:
+ # asynchronous call
+ user_data = (kwargs['result_handler'],
+ kwargs.get('error_handler'), kwargs.get('user_data'))
+ self.dbus_proxy.call(self.method_name, arg_variant,
+ kwargs.get('flags', 0), kwargs.get('timeout', -1), None,
+ self.__async_result_handler, user_data)
+ else:
+ # synchronous call
+ result = self.dbus_proxy.call_sync(self.method_name, arg_variant,
+ kwargs.get('flags', 0), kwargs.get('timeout', -1), None)
+ return self._unpack_result(result)
+
+ @classmethod
+ def _unpack_result(klass, result):
+ '''Convert a D-BUS return variant into an appropriate return value'''
+
+ result = result.unpack()
+
+ # to be compatible with standard Python behaviour, unbox
+ # single-element tuples and return None for empty result tuples
+ if len(result) == 1:
+ result = result[0]
+ elif len(result) == 0:
+ result = None
+
+ return result
+
+class DBusProxy(Gio.DBusProxy):
+ '''Provide comfortable and pythonic method calls.
+
+ This marshalls the method arguments into a GVariant, invokes the
+ call_sync() method on the DBusProxy object, and unmarshalls the result
+ GVariant back into a Python tuple.
+
+ The first argument always needs to be the D-Bus signature tuple of the
+ method call. Example:
+
+ proxy = Gio.DBusProxy.new_sync(...)
+ result = proxy.MyMethod('(is)', 42, 'hello')
+
+ The exception are methods which take no arguments, like
+ proxy.MyMethod('()'). For these you can omit the signature and just write
+ proxy.MyMethod().
+
+ Optional keyword arguments:
+
+ - timeout: timeout for the call in milliseconds (default to D-Bus timeout)
+
+ - flags: Combination of Gio.DBusCallFlags.*
+
+ - result_handler: Do an asynchronous method call and invoke
+ result_handler(proxy_object, result, user_data) when it finishes.
+
+ - error_handler: If the asynchronous call raises an exception,
+ error_handler(proxy_object, exception, user_data) is called when it
+ finishes. If error_handler is not given, result_handler is called with
+ the exception object as result instead.
+
+ - user_data: Optional user data to pass to result_handler for
+ asynchronous calls.
+
+ Example for asynchronous calls:
+
+ def mymethod_done(proxy, result, user_data):
+ if isinstance(result, Exception):
+ # handle error
+ else:
+ # do something with result
+
+ proxy.MyMethod('(is)', 42, 'hello',
+ result_handler=mymethod_done, user_data='data')
+ '''
+ def __getattr__(self, name):
+ return _DBusProxyMethodCall(self, name)
+
+DBusProxy = override(DBusProxy)
+__all__.append('DBusProxy')
diff --git a/gi/overrides/Gtk.py b/gi/overrides/Gtk.py
index 69a03429..30e883c6 100644
--- a/gi/overrides/Gtk.py
+++ b/gi/overrides/Gtk.py
@@ -21,9 +21,8 @@
import sys
import gobject
-from gi.repository import Gdk
from gi.repository import GObject
-from ..types import override
+from ..overrides import override
from ..importer import modules
if sys.version_info >= (3, 0):
@@ -33,10 +32,80 @@ else:
_basestring = basestring
_callable = callable
-Gtk = modules['Gtk'].introspection_module
+Gtk = modules['Gtk']._introspection_module
__all__ = []
+class Widget(Gtk.Widget):
+
+ def translate_coordinates(self, dest_widget, src_x, src_y):
+ success, dest_x, dest_y = super(Widget, self).translate_coordinates(
+ dest_widget, src_x, src_y)
+ if success:
+ return (dest_x, dest_y,)
+
+Widget = override(Widget)
+__all__.append('Widget')
+
+class Container(Gtk.Container, Widget):
+
+ def __len__(self):
+ return len(self.get_children())
+
+ def __contains__(self, child):
+ return child in self.get_children()
+
+ def __iter__(self):
+ return iter(self.get_children())
+
+ def __bool__(self):
+ return True
+
+ # alias for Python 2.x object protocol
+ __nonzero__ = __bool__
+
+ def get_focus_chain(self):
+ success, widgets = super(Container, self).get_focus_chain()
+ if success:
+ return widgets
+
+Container = override(Container)
+__all__.append('Container')
+
+class Editable(Gtk.Editable):
+
+ def insert_text(self, text, position):
+ pos = super(Editable, self).insert_text(text, -1, position)
+
+ return pos
+
+ def get_selection_bounds(self):
+ success, start_pos, end_pos = super(Editable, self).get_selection_bounds()
+ if success:
+ return (start_pos, end_pos,)
+ else:
+ return tuple()
+
+Editable = override(Editable)
+__all__.append("Editable")
+
+class Action(Gtk.Action):
+ def __init__(self, name, label, tooltip, stock_id, **kwds):
+ Gtk.Action.__init__(self, name=name, label=label, tooltip=tooltip, stock_id=stock_id, **kwds)
+
+Action = override(Action)
+__all__.append("Action")
+
+class RadioAction(Gtk.RadioAction):
+ def __init__(self, name, label, tooltip, stock_id, value, **kwds):
+ Gtk.RadioAction.__init__(self, name=name, label=label, tooltip=tooltip, stock_id=stock_id, value=value, **kwds)
+
+RadioAction = override(RadioAction)
+__all__.append("RadioAction")
+
class ActionGroup(Gtk.ActionGroup):
+ def __init__(self, name, **kwds):
+ super(ActionGroup, self).__init__(name = name, **kwds)
+
def add_actions(self, entries, user_data=None):
"""
The add_actions() method is a convenience method that creates a number
@@ -69,7 +138,7 @@ class ActionGroup(Gtk.ActionGroup):
raise TypeError('entries must be iterable')
def _process_action(name, stock_id=None, label=None, accelerator=None, tooltip=None, callback=None):
- action = Gtk.Action(name=name, label=label, tooltip=tooltip, stock_id=stock_id)
+ action = Action(name, label, tooltip, stock_id)
if callback is not None:
action.connect('activate', callback, user_data)
@@ -114,7 +183,7 @@ class ActionGroup(Gtk.ActionGroup):
raise TypeError('entries must be iterable')
def _process_action(name, stock_id=None, label=None, accelerator=None, tooltip=None, callback=None, is_active=False):
- action = Gtk.ToggleAction(name=name, label=label, tooltip=tooltip, stock_id=stock_id)
+ action = Gtk.ToggleAction(name, label, tooltip, stock_id)
action.set_active(is_active)
if callback is not None:
action.connect('activate', callback, user_data)
@@ -162,7 +231,7 @@ class ActionGroup(Gtk.ActionGroup):
first_action = None
def _process_action(group_source, name, stock_id=None, label=None, accelerator=None, tooltip=None, entry_value=0):
- action = Gtk.RadioAction(name=name, label=label, tooltip=tooltip, stock_id=stock_id, value=entry_value)
+ action = RadioAction(name, label, tooltip, stock_id, entry_value)
# FIXME: join_group is a patch to Gtk+ 3.0
# otherwise we can't effectively add radio actions to a
@@ -199,9 +268,22 @@ class UIManager(Gtk.UIManager):
return Gtk.UIManager.add_ui_from_string(self, buffer, length)
+ def insert_action_group(self, buffer, length=-1):
+ return Gtk.UIManager.insert_action_group(self, buffer, length)
+
UIManager = override(UIManager)
__all__.append('UIManager')
+class ComboBox(Gtk.ComboBox, Container):
+
+ def get_active_iter(self):
+ success, aiter = super(ComboBox, self).get_active_iter()
+ if success:
+ return aiter
+
+ComboBox = override(ComboBox)
+__all__.append('ComboBox')
+
class Builder(Gtk.Builder):
def connect_signals(self, obj_or_map):
@@ -230,8 +312,7 @@ class Builder(Gtk.Builder):
else:
gobj.connect(signal_name, handler)
- self.connect_signals_full(_full_callback,
- obj_or_map);
+ self.connect_signals_full(_full_callback, obj_or_map)
def add_from_string(self, buffer):
if not isinstance(buffer, _basestring):
@@ -253,15 +334,22 @@ Builder = override(Builder)
__all__.append('Builder')
-class Dialog(Gtk.Dialog):
+class Dialog(Gtk.Dialog, Container):
def __init__(self,
title=None,
parent=None,
flags=0,
buttons=None,
+ _buttons_property=None,
**kwds):
+ # buttons is overloaded by PyGtk so we have to do the same here
+ # this breaks some subclasses of Dialog so add a _buttons_property
+ # keyword to work around this
+ if _buttons_property is not None:
+ kwds['buttons'] = _buttons_property
+
Gtk.Dialog.__init__(self, **kwds)
if title:
self.set_title(title)
@@ -321,10 +409,19 @@ class MessageDialog(Gtk.MessageDialog, Dialog):
if message_format != None:
kwds['text'] = message_format
Gtk.MessageDialog.__init__(self,
- buttons=buttons,
+ _buttons_property=buttons,
+ message_type=type,
**kwds)
Dialog.__init__(self, parent=parent, flags=flags)
+ def format_secondary_text(self, message_format):
+ self.set_property('secondary-use-markup', False)
+ self.set_property('secondary-text', message_format)
+
+ def format_secondary_markup(self, message_format):
+ self.set_property('secondary-use-markup', True)
+ self.set_property('secondary-text', message_format)
+
MessageDialog = override(MessageDialog)
__all__.append('MessageDialog')
@@ -354,7 +451,10 @@ class FileChooserDialog(Gtk.FileChooserDialog, Dialog):
Gtk.FileChooserDialog.__init__(self,
action=action,
**kwds)
- Dialog.__init__(self, title=title, parent=parent, buttons=buttons)
+ Dialog.__init__(self,
+ title=title,
+ parent=parent,
+ buttons=buttons)
FileChooserDialog = override(FileChooserDialog)
__all__.append('FileChooserDialog')
@@ -384,6 +484,45 @@ class RecentChooserDialog(Gtk.RecentChooserDialog, Dialog):
RecentChooserDialog = override(RecentChooserDialog)
__all__.append('RecentChooserDialog')
+class IconView(Gtk.IconView):
+
+ def get_item_at_pos(self, x, y):
+ success, path, cell = super(IconView, self).get_item_at_pos(x, y)
+ if success:
+ return (path, cell,)
+
+ def get_visible_range(self):
+ success, start_path, end_path = super(IconView, self).get_visible_range()
+ if success:
+ return (start_path, end_path,)
+
+ def get_dest_item_at_pos(self, drag_x, drag_y):
+ success, path, pos = super(IconView, self).get_dest_item_at_pos(drag_x, drag_y)
+ if success:
+ return path, pos
+
+IconView = override(IconView)
+__all__.append('IconView')
+
+class IMContext(Gtk.IMContext):
+
+ def get_surrounding(self):
+ success, text, cursor_index = super(IMContext, self).get_surrounding()
+ if success:
+ return (text, cursor_index,)
+
+IMContext = override(IMContext)
+__all__.append('IMContext')
+
+class RecentInfo(Gtk.RecentInfo):
+
+ def get_application_info(self, app_name):
+ success, app_exec, count, time = super(RecentInfo, self).get_application_info(app_name)
+ if success:
+ return (app_exec, count, time,)
+
+RecentInfo = override(RecentInfo)
+__all__.append('RecentInfo')
class TextBuffer(Gtk.TextBuffer):
def _get_or_create_tag_table(self):
@@ -419,6 +558,12 @@ class TextBuffer(Gtk.TextBuffer):
self._get_or_create_tag_table().add(tag)
return tag
+ def create_mark(self, mark_name, where, left_gravity=False):
+ return Gtk.TextBuffer.create_mark(self, mark_name, where, left_gravity)
+
+ def set_text(self, text, length=-1):
+ Gtk.TextBuffer.set_text(self, text, length)
+
def insert(self, iter, text):
if not isinstance(text , _basestring):
raise TypeError('text must be a string, not %s' % type(text))
@@ -426,6 +571,32 @@ class TextBuffer(Gtk.TextBuffer):
length = len(text)
Gtk.TextBuffer.insert(self, iter, text, length)
+ def insert_with_tags(self, iter, text, *tags):
+ start_offset = iter.get_offset()
+ self.insert(iter, text)
+
+ if not tags:
+ return
+
+ start = self.get_iter_at_offset(start_offset)
+
+ for tag in tags:
+ self.apply_tag(tag, start, iter)
+
+ def insert_with_tags_by_name(self, iter, text, *tags):
+ if not tags:
+ return
+
+ tag_objs = []
+
+ for tag in tags:
+ tag_obj = self.get_tag_table().lookup(tag)
+ if not tag_obj:
+ raise ValueError('unknown text tag: %s' % tag)
+ tag_objs.append(tag_obj)
+
+ self.insert_with_tags(iter, text, *tag_objs)
+
def insert_at_cursor(self, text):
if not isinstance(text , _basestring):
raise TypeError('text must be a string, not %s' % type(text))
@@ -433,9 +604,40 @@ class TextBuffer(Gtk.TextBuffer):
length = len(text)
Gtk.TextBuffer.insert_at_cursor(self, text, length)
+ def get_selection_bounds(self):
+ success, start, end = super(TextBuffer, self).get_selection_bounds()
+ if success:
+ return (start, end)
+ else:
+ return ()
+
TextBuffer = override(TextBuffer)
__all__.append('TextBuffer')
+class TextIter(Gtk.TextIter):
+
+ def forward_search(self, string, flags, limit):
+ success, match_start, match_end = super(TextIter, self).forward_search(string,
+ flags, limit)
+ return (match_start, match_end,)
+
+ def backward_search(self, string, flags, limit):
+ success, match_start, match_end = super(TextIter, self).backward_search(string,
+ flags, limit)
+ return (match_start, match_end,)
+
+ def begins_tag(self, tag=None):
+ return super(TextIter, self).begins_tag(tag)
+
+ def ends_tag(self, tag=None):
+ return super(TextIter, self).ends_tag(tag)
+
+ def toggles_tag(self, tag=None):
+ return super(TextIter, self).toggles_tag(tag)
+
+TextIter = override(TextIter)
+__all__.append('TextIter')
+
class TreeModel(Gtk.TreeModel):
def __len__(self):
return self.iter_n_children(None)
@@ -443,6 +645,9 @@ class TreeModel(Gtk.TreeModel):
def __bool__(self):
return True
+ # alias for Python 2.x object protocol
+ __nonzero__ = __bool__
+
def __getitem__(self, key):
if isinstance(key, Gtk.TreeIter):
return TreeModelRow(self, key)
@@ -466,30 +671,14 @@ class TreeModel(Gtk.TreeModel):
return TreeModelRowIter(self, self.get_iter_first())
def get_iter(self, path):
- if isinstance(path, Gtk.TreePath):
- pass
- elif isinstance(path, (int, str,)):
- path = self._tree_path_from_string(str(path))
- elif isinstance(path, tuple):
- path_str = ":".join(str(val) for val in path)
- path = self._tree_path_from_string(path_str)
- else:
- raise TypeError("tree path must be one of Gtk.TreeIter, Gtk.TreePath, \
- int, str or tuple, not %s" % type(path).__name__)
+ if not isinstance(path, Gtk.TreePath):
+ path = TreePath(path)
success, aiter = super(TreeModel, self).get_iter(path)
if not success:
raise ValueError("invalid tree path '%s'" % path)
return aiter
- def _tree_path_from_string(self, path):
- if len(path) == 0:
- raise TypeError("could not parse subscript '%s' as a tree path" % path)
- try:
- return TreePath.new_from_string(path)
- except TypeError:
- raise TypeError("could not parse subscript '%s' as a tree path" % path)
-
def get_iter_first(self):
success, aiter = super(TreeModel, self).get_iter_first()
if success:
@@ -522,11 +711,138 @@ class TreeModel(Gtk.TreeModel):
if success:
return parent_iter
-TreeModel.__nonzero__ = TreeModel.__bool__
+ def set_row(self, treeiter, row):
+ # TODO: Accept a dictionary for row
+ # model.append(None,{COLUMN_ICON: icon, COLUMN_NAME: name})
+
+ n_columns = self.get_n_columns()
+ if len(row) != n_columns:
+ raise ValueError('row sequence has the incorrect number of elements')
+
+ for i in range(n_columns):
+ value = row[i]
+ if value is None:
+ continue # None means skip this row
+
+ self.set_value(treeiter, i, value)
+
+ def _convert_value(self, treeiter, column, value):
+ if value is None:
+ return
+
+ # we may need to convert to a basic type
+ type_ = self.get_column_type(column)
+ if type_ == GObject.TYPE_STRING:
+ if isinstance(value, str):
+ value = str(value)
+ elif sys.version_info < (3, 0):
+ if isinstance(value, unicode):
+ value = value.encode('UTF-8')
+ else:
+ raise ValueError('Expected string or unicode for column %i but got %s%s' % (column, value, type(value)))
+ else:
+ raise ValueError('Expected a string for column %i but got %s' % (column, type(value)))
+ elif type_ == GObject.TYPE_FLOAT or type_ == GObject.TYPE_DOUBLE:
+ if isinstance(value, float):
+ value = float(value)
+ else:
+ raise ValueError('Expected a float for column %i but got %s' % (column, type(value)))
+ elif type_ == GObject.TYPE_LONG or type_ == GObject.TYPE_INT:
+ if isinstance(value, int):
+ value = int(value)
+ elif sys.version_info < (3, 0):
+ if isinstance(value, long):
+ value = long(value)
+ else:
+ raise ValueError('Expected an long for column %i but got %s' % (column, type(value)))
+ else:
+ raise ValueError('Expected an integer for column %i but got %s' % (column, type(value)))
+ elif type_ == GObject.TYPE_BOOLEAN:
+ cmp_classes = [int]
+ if sys.version_info < (3, 0):
+ cmp_classes.append(long)
+
+ if isinstance(value, tuple(cmp_classes)):
+ value = bool(value)
+ else:
+ raise ValueError('Expected a bool for column %i but got %s' % (column, type(value)))
+ else:
+ # use GValues directly to marshal to the correct type
+ # standard object checks should take care of validation
+ # so we don't have to do it here
+ value_container = GObject.Value()
+ value_container.init(type_)
+ if type_ == GObject.TYPE_PYOBJECT:
+ value_container.set_boxed(value)
+ value = value_container
+ elif type_ == GObject.TYPE_CHAR:
+ value_container.set_char(value)
+ value = value_container
+ elif type_ == GObject.TYPE_UCHAR:
+ value_container.set_uchar(value)
+ value = value_container
+ elif type_ == GObject.TYPE_UNICHAR:
+ cmp_classes = [str]
+ if sys.version_info < (3, 0):
+ cmp_classes.append(unicode)
+
+ if isinstance(value, tuple(cmp_classes)):
+ value = ord(value[0])
+
+ value_container.set_uint(value)
+ value = value_container
+ elif type_ == GObject.TYPE_UINT:
+ value_container.set_uint(value)
+ value = value_container
+ elif type_ == GObject.TYPE_ULONG:
+ value_container.set_ulong(value)
+ value = value_container
+ elif type_ == GObject.TYPE_INT64:
+ value_container.set_int64(value)
+ value = value_container
+ elif type_ == GObject.TYPE_UINT64:
+ value_container.set_uint64(value)
+ value = value_container
+
+ return value
+
+ def get(self, treeiter, *columns):
+ n_columns = self.get_n_columns()
+
+ values = []
+ for col in columns:
+ if not isinstance(col, int):
+ raise TypeError("column numbers must be ints")
+
+ if col < 0 or col >= n_columns:
+ raise ValueError("column number is out of range")
+
+ values.append(self.get_value(treeiter, col))
+
+ return tuple(values)
+
TreeModel = override(TreeModel)
__all__.append('TreeModel')
-class ListStore(Gtk.ListStore, TreeModel):
+class TreeSortable(Gtk.TreeSortable, ):
+
+ def get_sort_column_id(self):
+ success, sort_column_id, order = super(TreeSortable, self).get_sort_column_id()
+ if success:
+ return (sort_column_id, order,)
+ else:
+ return (None, None,)
+
+ def set_sort_func(self, sort_column_id, sort_func, user_data=None):
+ super(TreeSortable, self).set_sort_func(sort_column_id, sort_func, user_data)
+
+ def set_default_sort_func(self, sort_func, user_data=None):
+ super(TreeSortable, self).set_default_sort_func(sort_func, user_data)
+
+TreeSortable = override(TreeSortable)
+__all__.append('TreeSortable')
+
+class ListStore(Gtk.ListStore, TreeModel, TreeSortable):
def __init__(self, *column_types):
Gtk.ListStore.__init__(self)
self.set_column_types(column_types)
@@ -534,20 +850,40 @@ class ListStore(Gtk.ListStore, TreeModel):
def append(self, row=None):
treeiter = Gtk.ListStore.append(self)
- # TODO: Accept a dictionary for row
- # model.append(None,{COLUMN_ICON: icon, COLUMN_NAME: name})
+ if row is not None:
+ self.set_row(treeiter, row)
+
+ return treeiter
+
+ def insert(self, position, row=None):
+ treeiter = Gtk.ListStore.insert(self, position)
if row is not None:
- n_columns = self.get_n_columns();
- if len(row) != n_columns:
- raise ValueError('row sequence has the incorrect number of elements')
+ self.set_row(treeiter, row)
- for i in range(n_columns):
- if row[i] is not None:
- self.set_value(treeiter, i, row[i])
+ return treeiter
+
+ def insert_before(self, sibling, row=None):
+ treeiter = Gtk.ListStore.insert_before(self, sibling)
+
+ if row is not None:
+ self.set_row(treeiter, row)
return treeiter
+ def insert_after(self, sibling, row=None):
+ treeiter = Gtk.ListStore.insert_after(self, sibling)
+
+ if row is not None:
+ self.set_row(treeiter, row)
+
+ return treeiter
+
+ def set_value(self, treeiter, column, value):
+ value = self._convert_value(treeiter, column, value)
+ Gtk.ListStore.set_value(self, treeiter, column, value)
+
+
ListStore = override(ListStore)
__all__.append('ListStore')
@@ -625,13 +961,16 @@ class TreeModelRowIter(object):
self.model = model
self.iter = aiter
- def next(self):
+ def __next__(self):
if not self.iter:
raise StopIteration
row = TreeModelRow(self.model, self.iter)
self.iter = self.model.iter_next(self.iter)
return row
+ # alias for Python 2.x object protocol
+ next = __next__
+
def __iter__(self):
return self
@@ -639,57 +978,130 @@ __all__.append('TreeModelRowIter')
class TreePath(Gtk.TreePath):
+ def __new__(cls, path=0):
+ if isinstance(path, int):
+ path = str(path)
+ elif isinstance(path, tuple):
+ path = ":".join(str(val) for val in path)
+
+ if len(path) == 0:
+ raise TypeError("could not parse subscript '%s' as a tree path" % path)
+ try:
+ return TreePath.new_from_string(path)
+ except TypeError:
+ raise TypeError("could not parse subscript '%s' as a tree path" % path)
+
def __str__(self):
return self.to_string()
def __lt__(self, other):
- return self.compare(other) < 0
+ return not other is None and self.compare(other) < 0
def __le__(self, other):
- return self.compare(other) <= 0
+ return not other is None and self.compare(other) <= 0
def __eq__(self, other):
- return self.compare(other) == 0
+ return not other is None and self.compare(other) == 0
def __ne__(self, other):
- return self.compare(other) != 0
+ return other is None or self.compare(other) != 0
def __gt__(self, other):
- return self.compare(other) > 0
+ return other is None or self.compare(other) > 0
def __ge__(self, other):
- return self.compare(other) >= 0
+ return other is None or self.compare(other) >= 0
TreePath = override(TreePath)
__all__.append('TreePath')
-class TreeStore(Gtk.TreeStore, TreeModel):
+class TreeStore(Gtk.TreeStore, TreeModel, TreeSortable):
def __init__(self, *column_types):
Gtk.TreeStore.__init__(self)
self.set_column_types(column_types)
def append(self, parent, row=None):
-
treeiter = Gtk.TreeStore.append(self, parent)
- # TODO: Accept a dictionary for row
- # model.append(None,{COLUMN_ICON: icon, COLUMN_NAME: name})
+ if row is not None:
+ self.set_row(treeiter, row)
+
+ return treeiter
+
+ def insert(self, parent, position, row=None):
+ treeiter = Gtk.TreeStore.insert(self, parent, position)
+
+ if row is not None:
+ self.set_row(treeiter, row)
+
+ return treeiter
+
+ def insert_before(self, parent, sibling, row=None):
+ treeiter = Gtk.TreeStore.insert_before(self, parent, sibling)
if row is not None:
- n_columns = self.get_n_columns();
- if len(row) != n_columns:
- raise ValueError('row sequence has the incorrect number of elements')
+ self.set_row(treeiter, row)
+
+ return treeiter
+
+ def insert_after(self, parent, sibling, row=None):
+ treeiter = Gtk.TreeStore.insert_after(self, parent, sibling)
- for i in range(n_columns):
- if row[i] is not None:
- self.set_value(treeiter, i, row[i])
+ if row is not None:
+ self.set_row(treeiter, row)
return treeiter
+ def set_value(self, treeiter, column, value):
+ value = self._convert_value(treeiter, column, value)
+ Gtk.TreeStore.set_value(self, treeiter, column, value)
+
+
TreeStore = override(TreeStore)
__all__.append('TreeStore')
+class TreeView(Gtk.TreeView, Container):
+
+ def get_path_at_pos(self, x, y):
+ success, path, column, cell_x, cell_y = super(TreeView, self).get_path_at_pos(x, y)
+ if success:
+ return (path, column, cell_x, cell_y,)
+
+ def get_dest_row_at_pos(self, drag_x, drag_y):
+ success, path, pos = super(TreeView, self).get_dest_row_at_pos(drag_x, drag_y)
+ if success:
+ return (path, pos,)
+
+ def _construct_target_list(self, targets):
+ # FIXME: this should most likely be part of Widget or a global helper
+ # function
+ target_entries = []
+ for t in targets:
+ entry = Gtk.TargetEntry.new(*t)
+ target_entries.append(entry)
+ return target_entries
+
+ def enable_model_drag_source(self, start_button_mask, targets, actions):
+ target_entries = self._construct_target_list(targets)
+ super(TreeView, self).enable_model_drag_source(start_button_mask,
+ target_entries,
+ actions)
+
+ def enable_model_drag_dest(self, targets, actions):
+ target_entries = self._construct_target_list(targets)
+ super(TreeView, self).enable_model_drag_dest(target_entries,
+ actions)
+
+ def scroll_to_cell(self, path, column=None, use_align=False, row_align=0.0, col_align=0.0):
+ if not isinstance(path, Gtk.TreePath):
+ path = TreePath(path)
+ super(TreeView, self).scroll_to_cell(path, column, use_align, row_align, col_align)
+
+
+TreeView = override(TreeView)
+__all__.append('TreeView')
+
class TreeViewColumn(Gtk.TreeViewColumn):
def __init__(self, title='',
cell_renderer=None,
@@ -701,11 +1113,24 @@ class TreeViewColumn(Gtk.TreeViewColumn):
for (name, value) in attributes.items():
self.add_attribute(cell_renderer, name, value)
+ def cell_get_position(self, cell_renderer):
+ success, start_pos, width = super(TreeViewColumn, self).cell_get_position(cell_renderer)
+ if success:
+ return (start_pos, width,)
+
+ def set_cell_data_func(self, cell_renderer, func, func_data=None):
+ super(TreeViewColumn, self).set_cell_data_func(cell_renderer, func, func_data)
+
TreeViewColumn = override(TreeViewColumn)
__all__.append('TreeViewColumn')
class TreeSelection(Gtk.TreeSelection):
+ def select_path(self, path):
+ if not isinstance(path, Gtk.TreePath):
+ path = TreePath(path)
+ super(TreeSelection, self).select_path(path)
+
def get_selected(self):
success, model, aiter = super(TreeSelection, self).get_selected()
if success:
@@ -713,11 +1138,17 @@ class TreeSelection(Gtk.TreeSelection):
else:
return (model, None)
+ # for compatibility with PyGtk
+ def get_selected_rows(self):
+ rows, model = super(TreeSelection, self).get_selected_rows()
+ return (model, rows)
+
+
TreeSelection = override(TreeSelection)
__all__.append('TreeSelection')
-class Button(Gtk.Button):
- def __init__(self, label=None, stock=None, use_underline=False):
+class Button(Gtk.Button, Container):
+ def __init__(self, label=None, stock=None, use_underline=False, **kwds):
if stock:
label = stock
use_stock = True
@@ -725,11 +1156,94 @@ class Button(Gtk.Button):
else:
use_stock = False
Gtk.Button.__init__(self, label=label, use_stock=use_stock,
- use_underline=use_underline)
+ use_underline=use_underline, **kwds)
Button = override(Button)
__all__.append('Button')
-import sys
+class LinkButton(Gtk.LinkButton):
+ def __init__(self, uri, label=None, **kwds):
+ Gtk.LinkButton.__init__(self, uri=uri, label=label, **kwds)
+
+LinkButton = override(LinkButton)
+__all__.append('LinkButton')
+
+class Label(Gtk.Label):
+ def __init__(self, label=None, **kwds):
+ Gtk.Label.__init__(self, label=label, **kwds)
+
+Label = override(Label)
+__all__.append('Label')
+
+class Adjustment(Gtk.Adjustment):
+ def __init__(self, *args, **kwds):
+ arg_names = ('value', 'lower', 'upper',
+ 'step_increment', 'page_increment', 'page_size')
+ new_args = dict(zip(arg_names, args))
+ new_args.update(kwds)
+ Gtk.Adjustment.__init__(self, **new_args)
+
+ # The value property is set between lower and (upper - page_size).
+ # Just in case lower, upper or page_size was still 0 when value
+ # was set, we set it again here.
+ if 'value' in new_args:
+ self.set_value(new_args['value'])
+
+Adjustment = override(Adjustment)
+__all__.append('Adjustment')
+
+class Table(Gtk.Table, Container):
+ def __init__(self, rows=1, columns=1, homogeneous=False, **kwds):
+ if 'n_rows' in kwds:
+ rows = kwds.pop('n_rows')
+
+ if 'n_columns' in kwds:
+ columns = kwds.pop('n_columns')
+
+ Gtk.Table.__init__(self, n_rows=rows, n_columns=columns, homogeneous=homogeneous, **kwds)
+
+ def attach(self, child, left_attach, right_attach, top_attach, bottom_attach, xoptions=Gtk.AttachOptions.EXPAND|Gtk.AttachOptions.FILL, yoptions=Gtk.AttachOptions.EXPAND|Gtk.AttachOptions.FILL, xpadding=0, ypadding=0):
+ Gtk.Table.attach(self, child, left_attach, right_attach, top_attach, bottom_attach, xoptions, yoptions, xpadding, ypadding)
+
+Table = override(Table)
+__all__.append('Table')
+
+class ScrolledWindow(Gtk.ScrolledWindow):
+ def __init__(self, hadjustment=None, vadjustment=None, **kwds):
+ Gtk.ScrolledWindow.__init__(self, hadjustment=hadjustment, vadjustment=vadjustment, **kwds)
+
+ScrolledWindow = override(ScrolledWindow)
+__all__.append('ScrolledWindow')
+
+class Paned(Gtk.Paned):
+ def pack1(self, child, resize=False, shrink=True):
+ super(Paned, self).pack1(child, resize, shrink)
+
+ def pack2(self, child, resize=True, shrink=True):
+ super(Paned, self).pack2(child, resize, shrink)
+
+Paned = override(Paned)
+__all__.append('Paned')
+
+if Gtk._version != '2.0':
+ class Menu(Gtk.Menu):
+ def popup(self, parent_menu_shell, parent_menu_item, func, data, button, activate_time):
+ self.popup_for_device(None, parent_menu_shell, parent_menu_item, func, data, button, activate_time)
+ Menu = override(Menu)
+ __all__.append('Menu')
+
+_Gtk_main_quit = Gtk.main_quit
+@override(Gtk.main_quit)
+def main_quit(*args):
+ _Gtk_main_quit()
+
+_Gtk_stock_lookup = Gtk.stock_lookup
+@override(Gtk.stock_lookup)
+def stock_lookup(*args):
+ success, item = _Gtk_stock_lookup(*args)
+ if not success:
+ return None
+
+ return item
initialized, argv = Gtk.init_check(sys.argv)
sys.argv = list(argv)
diff --git a/gi/overrides/Makefile.am b/gi/overrides/Makefile.am
index b36c7a5e..7a15b467 100644
--- a/gi/overrides/Makefile.am
+++ b/gi/overrides/Makefile.am
@@ -1,13 +1,17 @@
PLATFORM_VERSION = 2.0
-pkgpyexecdir = $(pyexecdir)/gtk-2.0/gi
+pkgpyexecdir = $(pyexecdir)/gi
pygioverridesdir = $(pkgpyexecdir)/overrides
pygioverrides_PYTHON = \
GLib.py \
Gtk.py \
Gdk.py \
+ Gio.py \
GIMarshallingTests.py \
+ Pango.py \
keysyms.py \
__init__.py
+
+-include $(top_srcdir)/git.mk
diff --git a/gi/overrides/Pango.py b/gi/overrides/Pango.py
new file mode 100644
index 00000000..3269806e
--- /dev/null
+++ b/gi/overrides/Pango.py
@@ -0,0 +1,51 @@
+# -*- Mode: Python; py-indent-offset: 4 -*-
+# vim: tabstop=4 shiftwidth=4 expandtab
+#
+# Copyright (C) 2010 Paolo Borelli <pborelli@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
+
+from ..overrides import override
+from ..importer import modules
+
+Pango = modules['Pango']._introspection_module
+
+__all__ = []
+
+class FontDescription(Pango.FontDescription):
+
+ def __new__(cls, string=None):
+ if string is not None:
+ return Pango.font_description_from_string (string)
+ else:
+ return Pango.FontDescription.__new__(cls)
+
+FontDescription = override(FontDescription)
+__all__.append('FontDescription')
+
+class Layout(Pango.Layout):
+
+ def __new__(cls, context):
+ return Pango.Layout.new(context)
+
+ def __init__(self, context, **kwds):
+ # simply discard 'context', since it was set by
+ # __new__ and it is not a PangoLayout property
+ super(Layout, self).__init__(**kwds)
+
+Layout = override(Layout)
+__all__.append('Layout')
+
diff --git a/gi/overrides/__init__.py b/gi/overrides/__init__.py
index e69de29b..a98974f6 100644
--- a/gi/overrides/__init__.py
+++ b/gi/overrides/__init__.py
@@ -0,0 +1,66 @@
+import sys
+import types
+
+import gobject
+
+registry = None
+class _Registry(dict):
+ def __setitem__(self, key, value):
+ '''We do checks here to make sure only submodules of the override
+ module are added. Key and value should be the same object and come
+ from the gi.override module.
+
+ We add the override to the dict as "override_module.name". For instance
+ if we were overriding Gtk.Button you would retrive it as such:
+ registry['Gtk.Button']
+ '''
+ if not key == value:
+ raise KeyError('You have tried to modify the registry. This should only be done by the override decorator')
+
+ info = getattr(value, '__info__')
+ if info == None:
+ raise KeyError('Can not override a type %s, which is not in a gobject introspection typelib' % value.__name__)
+
+ if not value.__module__.startswith('gi.overrides'):
+ raise KeyError('You have tried to modify the registry outside of the overrides module. This is not allowed')
+
+ g_type = info.get_g_type()
+ assert g_type != gobject.TYPE_NONE
+ if g_type != gobject.TYPE_INVALID:
+ g_type.pytype = value
+
+ # strip gi.overrides from module name
+ module = value.__module__[13:]
+ key = "%s.%s" % (module, value.__name__)
+ super(_Registry, self).__setitem__(key, value)
+
+ def register(self, override_class):
+ self[override_class] = override_class
+
+
+class overridefunc(object):
+ '''decorator for overriding a function'''
+ def __init__(self, func):
+ if not hasattr(func, '__info__'):
+ raise TypeError("func must be an gi function")
+ from ..importer import modules
+ self.module = modules[func.__module__]._introspection_module
+
+ def __call__(self, func):
+ def wrapper(*args, **kwargs):
+ return func(*args, **kwargs)
+ wrapper.__name__ = func.__name__
+ setattr(self.module, func.__name__, wrapper)
+ return wrapper
+
+registry = _Registry()
+
+def override(type_):
+ '''Decorator for registering an override'''
+ if type(type_) == types.FunctionType:
+ return overridefunc(type_)
+ else:
+ registry.register(type_)
+ return type_
+
+
diff --git a/gi/pygi-argument.c b/gi/pygi-argument.c
index e3dd8c38..6519e5ca 100644
--- a/gi/pygi-argument.c
+++ b/gi/pygi-argument.c
@@ -188,6 +188,7 @@ _pygi_g_type_interface_check_object (GIBaseInfo *info,
for (i = 0; i < g_enum_info_get_n_values (info); i++) {
GIValueInfo *value_info = g_enum_info_get_value (info, i);
glong enum_value = g_value_info_get_value (value_info);
+ g_base_info_unref (value_info);
if (value == enum_value) {
retval = 1;
break;
@@ -222,22 +223,18 @@ _pygi_g_type_interface_check_object (GIBaseInfo *info,
/* Handle special cases. */
type = g_registered_type_info_get_g_type ( (GIRegisteredTypeInfo *) info);
- if (g_type_is_a (type, G_TYPE_VALUE)) {
- GType object_type;
- object_type = pyg_type_from_object ( (PyObject *) object->ob_type);
- if (object_type == G_TYPE_INVALID) {
- PyErr_Format (PyExc_TypeError, "Must be of a known GType, not %s",
- object->ob_type->tp_name);
- retval = 0;
- }
- break;
- } else if (g_type_is_a (type, G_TYPE_CLOSURE)) {
- if (!PyCallable_Check (object)) {
+ if (g_type_is_a (type, G_TYPE_CLOSURE)) {
+ if (!(PyCallable_Check (object) ||
+ pyg_type_from_object_strict (object, FALSE) == G_TYPE_CLOSURE)) {
PyErr_Format (PyExc_TypeError, "Must be callable, not %s",
object->ob_type->tp_name);
retval = 0;
}
break;
+ } else if (g_type_is_a (type, G_TYPE_VALUE)) {
+ /* we can't check g_values because we don't have
+ * enough context so just pass them through */
+ break;
}
/* Fallback. */
@@ -245,8 +242,46 @@ _pygi_g_type_interface_check_object (GIBaseInfo *info,
case GI_INFO_TYPE_BOXED:
case GI_INFO_TYPE_INTERFACE:
case GI_INFO_TYPE_OBJECT:
+ retval = _pygi_g_registered_type_info_check_object ( (GIRegisteredTypeInfo *) info, TRUE, object);
+ break;
case GI_INFO_TYPE_UNION:
+
+
retval = _pygi_g_registered_type_info_check_object ( (GIRegisteredTypeInfo *) info, TRUE, object);
+
+ /* If not the same type then check to see if the object's type
+ * is the same as one of the union's members
+ */
+ if (retval == 0) {
+ gint i;
+ gint n_fields;
+
+ n_fields = g_union_info_get_n_fields ( (GIUnionInfo *) info);
+
+ for (i = 0; i < n_fields; i++) {
+ gint member_retval;
+ GIFieldInfo *field_info;
+ GITypeInfo *field_type_info;
+
+ field_info =
+ g_union_info_get_field ( (GIUnionInfo *) info, i);
+ field_type_info = g_field_info_get_type (field_info);
+
+ member_retval = _pygi_g_type_info_check_object(
+ field_type_info,
+ object,
+ TRUE);
+
+ g_base_info_unref ( ( GIBaseInfo *) field_type_info);
+ g_base_info_unref ( ( GIBaseInfo *) field_info);
+
+ if (member_retval == 1) {
+ retval = member_retval;
+ break;
+ }
+ }
+ }
+
break;
default:
g_assert_not_reached();
@@ -276,8 +311,18 @@ _pygi_g_type_info_check_object (GITypeInfo *type_info,
case GI_TYPE_TAG_BOOLEAN:
/* No check; every Python object has a truth value. */
break;
- case GI_TYPE_TAG_INT8:
case GI_TYPE_TAG_UINT8:
+ /* UINT8 types can be characters */
+ if (PYGLIB_PyBytes_Check(object)) {
+ if (PYGLIB_PyBytes_Size(object) != 1) {
+ PyErr_Format (PyExc_TypeError, "Must be a single character");
+ retval = 0;
+ break;
+ }
+
+ break;
+ }
+ case GI_TYPE_TAG_INT8:
case GI_TYPE_TAG_INT16:
case GI_TYPE_TAG_UINT16:
case GI_TYPE_TAG_INT32:
@@ -373,9 +418,36 @@ check_number_release:
}
break;
}
+ case GI_TYPE_TAG_UNICHAR:
+ {
+ Py_ssize_t size;
+ if (PyUnicode_Check (object)) {
+ size = PyUnicode_GET_SIZE (object);
+#if PY_VERSION_HEX < 0x03000000
+ } else if (PyString_Check (object)) {
+ PyObject *pyuni = PyUnicode_FromEncodedObject (object, "UTF-8", "strict");
+ size = PyUnicode_GET_SIZE (pyuni);
+ Py_DECREF(pyuni);
+#endif
+ } else {
+ PyErr_Format (PyExc_TypeError, "Must be string, not %s",
+ object->ob_type->tp_name);
+ retval = 0;
+ break;
+ }
+
+ if (size != 1) {
+ PyErr_Format (PyExc_TypeError, "Must be a one character string, not %ld characters",
+ size);
+ retval = 0;
+ break;
+ }
+
+ break;
+ }
case GI_TYPE_TAG_UTF8:
case GI_TYPE_TAG_FILENAME:
- if (!PYGLIB_PyUnicode_Check (object)) {
+ if (!PYGLIB_PyBaseString_Check (object) ) {
PyErr_Format (PyExc_TypeError, "Must be string, not %s",
object->ob_type->tp_name);
retval = 0;
@@ -412,6 +484,10 @@ check_number_release:
item_type_info = g_type_info_get_param_type (type_info, 0);
g_assert (item_type_info != NULL);
+ /* FIXME: This is insain. We really should only check the first
+ * object and perhaps have a debugging mode. Large arrays
+ * will cause apps to slow to a crawl.
+ */
for (i = 0; i < length; i++) {
PyObject *item;
@@ -645,8 +721,13 @@ _pygi_argument_from_object (PyObject *object,
arg.v_boolean = PyObject_IsTrue (object);
break;
}
- case GI_TYPE_TAG_INT8:
case GI_TYPE_TAG_UINT8:
+ if (PYGLIB_PyBytes_Check(object)) {
+ arg.v_long = (long)(PYGLIB_PyBytes_AsString(object)[0]);
+ break;
+ }
+
+ case GI_TYPE_TAG_INT8:
case GI_TYPE_TAG_INT16:
case GI_TYPE_TAG_UINT16:
case GI_TYPE_TAG_INT32:
@@ -680,8 +761,7 @@ _pygi_argument_from_object (PyObject *object,
value = PyInt_AS_LONG (number);
} else
#endif
- if (PyLong_Check (number))
- value = PyLong_AsUnsignedLongLong (number);
+ value = PyLong_AsUnsignedLongLong (number);
arg.v_uint64 = value;
@@ -702,10 +782,9 @@ _pygi_argument_from_object (PyObject *object,
#if PY_VERSION_HEX < 0x03000000
if (PyInt_Check (number)) {
value = PyInt_AS_LONG (number);
- } else
-#endif
- if (PyLong_Check (number))
- value = PyLong_AsLongLong (number);
+ } else
+#endif
+ value = PyLong_AsLongLong (number);
arg.v_int64 = value;
@@ -747,6 +826,42 @@ _pygi_argument_from_object (PyObject *object,
break;
}
+ case GI_TYPE_TAG_UNICHAR:
+ {
+ gchar *string;
+
+ if (object == Py_None) {
+ arg.v_uint32 = 0;
+ break;
+ }
+
+#if PY_VERSION_HEX < 0x03000000
+ if (PyUnicode_Check(object)) {
+ PyObject *pystr_obj = PyUnicode_AsUTF8String (object);
+
+ if (!pystr_obj)
+ break;
+
+ string = g_strdup(PyString_AsString (pystr_obj));
+ Py_DECREF(pystr_obj);
+ } else {
+ string = g_strdup(PyString_AsString (object));
+ }
+#else
+ {
+ PyObject *pybytes_obj = PyUnicode_AsUTF8String (object);
+ if (!pybytes_obj)
+ break;
+
+ string = g_strdup(PyBytes_AsString (pybytes_obj));
+ Py_DECREF (pybytes_obj);
+ }
+#endif
+
+ arg.v_uint32 = g_utf8_get_char (string);
+
+ break;
+ }
case GI_TYPE_TAG_UTF8:
{
gchar *string;
@@ -756,7 +871,17 @@ _pygi_argument_from_object (PyObject *object,
break;
}
#if PY_VERSION_HEX < 0x03000000
- string = g_strdup(PyString_AsString (object));
+ if (PyUnicode_Check(object)) {
+ PyObject *pystr_obj = PyUnicode_AsUTF8String (object);
+
+ if (!pystr_obj)
+ break;
+
+ string = g_strdup(PyString_AsString (pystr_obj));
+ Py_DECREF(pystr_obj);
+ } else {
+ string = g_strdup(PyString_AsString (object));
+ }
#else
{
PyObject *pybytes_obj = PyUnicode_AsUTF8String (object);
@@ -835,6 +960,15 @@ _pygi_argument_from_object (PyObject *object,
break;
}
+ if (g_type_info_get_tag (item_type_info) == GI_TYPE_TAG_UINT8 &&
+ PYGLIB_PyBytes_Check(object)) {
+
+ memcpy(array->data, PYGLIB_PyBytes_AsString(object), length);
+ array->len = length;
+ goto array_success;
+ }
+
+
item_transfer = transfer == GI_TRANSFER_CONTAINER ? GI_TRANSFER_NOTHING : transfer;
for (i = 0; i < length; i++) {
@@ -867,6 +1001,7 @@ array_item_error:
break;
}
+array_success:
arg.v_pointer = array;
g_base_info_unref ( (GIBaseInfo *) item_type_info);
@@ -904,7 +1039,7 @@ array_item_error:
GType object_type;
gint retval;
- object_type = pyg_type_from_object ( (PyObject *) object->ob_type);
+ object_type = pyg_type_from_object_strict ( (PyObject *) object->ob_type, FALSE);
if (object_type == G_TYPE_INVALID) {
PyErr_SetString (PyExc_RuntimeError, "unable to retrieve object's GType");
break;
@@ -913,25 +1048,39 @@ array_item_error:
g_warn_if_fail (transfer == GI_TRANSFER_NOTHING);
value = g_slice_new0 (GValue);
- g_value_init (value, object_type);
- retval = pyg_value_from_pyobject (value, object);
- if (retval < 0) {
- g_slice_free (GValue, value);
- PyErr_SetString (PyExc_RuntimeError, "PyObject conversion to GValue failed");
- break;
+ /* if already a gvalue, copy, else marshal into gvalue */
+ if (object_type == G_TYPE_VALUE) {
+ /* src GValue's lifecycle is handled by Python
+ * so we have to copy it into the destination's
+ * GValue which is freed during the cleanup of
+ * invoke.
+ */
+ GValue *src = (GValue *)((PyGObject *) object)->obj;
+ g_value_init (value, G_VALUE_TYPE (src));
+ g_value_copy(src, value);
+ } else {
+ g_value_init (value, object_type);
+ retval = pyg_value_from_pyobject (value, object);
+ if (retval < 0) {
+ g_slice_free (GValue, value);
+ PyErr_SetString (PyExc_RuntimeError, "PyObject conversion to GValue failed");
+ break;
+ }
}
arg.v_pointer = value;
} else if (g_type_is_a (type, G_TYPE_CLOSURE)) {
GClosure *closure;
- g_warn_if_fail (transfer == GI_TRANSFER_NOTHING);
-
- closure = pyg_closure_new (object, NULL, NULL);
- if (closure == NULL) {
- PyErr_SetString (PyExc_RuntimeError, "PyObject conversion to GClosure failed");
- break;
+ if (pyg_type_from_object_strict (object, FALSE) == G_TYPE_CLOSURE) {
+ closure = (GClosure *)pyg_boxed_get (object, void);
+ } else {
+ closure = pyg_closure_new (object, NULL, NULL);
+ if (closure == NULL) {
+ PyErr_SetString (PyExc_RuntimeError, "PyObject conversion to GClosure failed");
+ break;
+ }
}
arg.v_pointer = closure;
@@ -1246,6 +1395,27 @@ _pygi_argument_to_object (GIArgument *arg,
object = pyg_type_wrapper_new ( (GType) arg->v_long);
break;
}
+ case GI_TYPE_TAG_UNICHAR:
+ {
+ /* Preserve the bidirectional mapping between 0 and "" */
+ if (arg->v_uint32 == 0) {
+ object = PYGLIB_PyUnicode_FromString ("");
+ } else if (g_unichar_validate (arg->v_uint32)) {
+ gchar utf8[6];
+ gint bytes;
+
+ bytes = g_unichar_to_utf8 (arg->v_uint32, utf8);
+ object = PYGLIB_PyUnicode_FromStringAndSize ((char*)utf8, bytes);
+ } else {
+ /* TODO: Convert the error to an exception. */
+ PyErr_Format (PyExc_TypeError,
+ "Invalid unicode codepoint %" G_GUINT32_FORMAT,
+ arg->v_uint32);
+ object = Py_None;
+ Py_INCREF (object);
+ }
+ break;
+ }
case GI_TYPE_TAG_UTF8:
if (arg->v_string == NULL) {
object = Py_None;
@@ -1287,23 +1457,36 @@ _pygi_argument_to_object (GIArgument *arg,
GITransfer item_transfer;
gsize i, item_size;
- if (arg->v_pointer == NULL) {
- object = PyList_New (0);
- break;
- }
-
array = arg->v_pointer;
- object = PyList_New (array->len);
- if (object == NULL) {
- break;
- }
-
item_type_info = g_type_info_get_param_type (type_info, 0);
g_assert (item_type_info != NULL);
item_type_tag = g_type_info_get_tag (item_type_info);
item_transfer = transfer == GI_TRANSFER_CONTAINER ? GI_TRANSFER_NOTHING : transfer;
+
+ if (item_type_tag == GI_TYPE_TAG_UINT8) {
+ /* Return as a byte array */
+ if (arg->v_pointer == NULL) {
+ object = PYGLIB_PyBytes_FromString ("");
+ break;
+ }
+
+ object = PYGLIB_PyBytes_FromStringAndSize(array->data, array->len);
+ break;
+
+ } else {
+ if (arg->v_pointer == NULL) {
+ object = PyList_New (0);
+ break;
+ }
+
+ object = PyList_New (array->len);
+ if (object == NULL) {
+ break;
+ }
+
+ }
item_size = g_array_get_element_size (array);
for (i = 0; i < array->len; i++) {
@@ -1380,6 +1563,8 @@ _pygi_argument_to_object (GIArgument *arg,
type = g_registered_type_info_get_g_type ( (GIRegisteredTypeInfo *) info);
if (g_type_is_a (type, G_TYPE_VALUE)) {
object = pyg_value_as_pyobject (arg->v_pointer, FALSE);
+ } else if (g_struct_info_is_foreign (info)) {
+ object = pygi_struct_foreign_convert_from_g_argument (type_info, arg->v_pointer);
} else if (g_type_is_a (type, G_TYPE_BOXED)) {
PyObject *py_type;
@@ -1403,8 +1588,6 @@ _pygi_argument_to_object (GIArgument *arg,
}
Py_XDECREF (py_type);
- } else if ( (type == G_TYPE_NONE) && (g_struct_info_is_foreign (info))) {
- object = pygi_struct_foreign_convert_from_g_argument (type_info, arg->v_pointer);
} else if (type == G_TYPE_NONE) {
PyObject *py_type;
@@ -1589,6 +1772,97 @@ _pygi_argument_to_object (GIArgument *arg,
return object;
}
+
+GIArgument
+_pygi_argument_from_g_value(const GValue *value,
+ GITypeInfo *type_info)
+{
+ GIArgument arg = { 0, };
+
+ GITypeTag type_tag = g_type_info_get_tag (type_info);
+ switch (type_tag) {
+ case GI_TYPE_TAG_BOOLEAN:
+ arg.v_boolean = g_value_get_boolean (value);
+ break;
+ case GI_TYPE_TAG_INT8:
+ case GI_TYPE_TAG_INT16:
+ case GI_TYPE_TAG_INT32:
+ case GI_TYPE_TAG_INT64:
+ arg.v_int = g_value_get_int (value);
+ break;
+ case GI_TYPE_TAG_UINT8:
+ case GI_TYPE_TAG_UINT16:
+ case GI_TYPE_TAG_UINT32:
+ case GI_TYPE_TAG_UINT64:
+ arg.v_uint = g_value_get_uint (value);
+ break;
+ case GI_TYPE_TAG_UNICHAR:
+ arg.v_uint32 = g_value_get_char (value);
+ break;
+ case GI_TYPE_TAG_FLOAT:
+ arg.v_float = g_value_get_float (value);
+ break;
+ case GI_TYPE_TAG_DOUBLE:
+ arg.v_double = g_value_get_double (value);
+ break;
+ case GI_TYPE_TAG_GTYPE:
+ arg.v_long = g_value_get_gtype (value);
+ break;
+ case GI_TYPE_TAG_UTF8:
+ case GI_TYPE_TAG_FILENAME:
+ arg.v_string = g_value_dup_string (value);
+ break;
+ case GI_TYPE_TAG_GLIST:
+ case GI_TYPE_TAG_GSLIST:
+ arg.v_pointer = g_value_get_pointer (value);
+ break;
+ case GI_TYPE_TAG_ARRAY:
+ case GI_TYPE_TAG_GHASH:
+ arg.v_pointer = g_value_get_boxed (value);
+ break;
+ case GI_TYPE_TAG_INTERFACE:
+ {
+ GIBaseInfo *info;
+ GIInfoType info_type;
+
+ info = g_type_info_get_interface (type_info);
+ info_type = g_base_info_get_type (info);
+
+ g_base_info_unref (info);
+
+ switch (info_type) {
+ case GI_INFO_TYPE_FLAGS:
+ case GI_INFO_TYPE_ENUM:
+ arg.v_long = g_value_get_enum (value);
+ break;
+ case GI_INFO_TYPE_INTERFACE:
+ case GI_INFO_TYPE_OBJECT:
+ arg.v_pointer = g_value_get_object (value);
+ break;
+ case GI_INFO_TYPE_BOXED:
+ case GI_INFO_TYPE_STRUCT:
+ case GI_INFO_TYPE_UNION:
+ if (G_VALUE_HOLDS(value, G_TYPE_BOXED)) {
+ arg.v_pointer = g_value_get_boxed (value);
+ } else {
+ arg.v_pointer = g_value_get_pointer (value);
+ }
+ break;
+ default:
+ g_warning("Converting of type '%s' is not implemented", g_info_type_to_string(info_type));
+ g_assert_not_reached();
+ }
+ break;
+ }
+ case GI_TYPE_TAG_ERROR:
+ case GI_TYPE_TAG_VOID:
+ g_critical("Converting of type '%s' is not implemented", g_type_tag_to_string(type_tag));
+ g_assert_not_reached();
+ }
+
+ return arg;
+}
+
void
_pygi_argument_release (GIArgument *arg,
GITypeInfo *type_info,
@@ -1616,6 +1890,7 @@ _pygi_argument_release (GIArgument *arg,
case GI_TYPE_TAG_FLOAT:
case GI_TYPE_TAG_DOUBLE:
case GI_TYPE_TAG_GTYPE:
+ case GI_TYPE_TAG_UNICHAR:
break;
case GI_TYPE_TAG_FILENAME:
case GI_TYPE_TAG_UTF8:
@@ -1701,10 +1976,6 @@ _pygi_argument_release (GIArgument *arg,
|| (direction == GI_DIRECTION_OUT && transfer != GI_TRANSFER_NOTHING)) {
g_slice_free (GValue, value);
}
- } else if (g_type_is_a (type, G_TYPE_CLOSURE)) {
- if (direction == GI_DIRECTION_IN && transfer == GI_TRANSFER_NOTHING) {
- g_closure_unref (arg->v_pointer);
- }
} else if (g_struct_info_is_foreign ( (GIStructInfo*) info)) {
if (direction == GI_DIRECTION_OUT && transfer == GI_TRANSFER_EVERYTHING) {
pygi_struct_foreign_release (info, arg->v_pointer);
diff --git a/gi/pygi-argument.h b/gi/pygi-argument.h
index d932e8f1..7224c75a 100644
--- a/gi/pygi-argument.h
+++ b/gi/pygi-argument.h
@@ -55,6 +55,8 @@ PyObject* _pygi_argument_to_object (GIArgument *arg,
GITypeInfo *type_info,
GITransfer transfer);
+GIArgument _pygi_argument_from_g_value(const GValue *value,
+ GITypeInfo *type_info);
void _pygi_argument_release (GIArgument *arg,
GITypeInfo *type_info,
diff --git a/gi/pygi-closure.c b/gi/pygi-closure.c
index 1e2ce050..56ddc8be 100644
--- a/gi/pygi-closure.c
+++ b/gi/pygi-closure.c
@@ -27,6 +27,53 @@
*/
static GSList* async_free_list;
+static void
+_pygi_closure_assign_pyobj_to_out_argument (gpointer out_arg, PyObject *object,
+ GITypeInfo *type_info,
+ GITransfer transfer)
+{
+ GIArgument arg = _pygi_argument_from_object (object, type_info, transfer);
+ GITypeTag type_tag = g_type_info_get_tag (type_info);
+
+ switch (type_tag) {
+ case GI_TYPE_TAG_BOOLEAN:
+ *((gboolean *) out_arg) = arg.v_boolean;
+ break;
+ case GI_TYPE_TAG_INT8:
+ *((gint8 *) out_arg) = arg.v_int8;
+ break;
+ case GI_TYPE_TAG_UINT8:
+ *((guint8 *) out_arg) = arg.v_uint8;
+ break;
+ case GI_TYPE_TAG_INT16:
+ *((gint16 *) out_arg) = arg.v_int16;
+ break;
+ case GI_TYPE_TAG_UINT16:
+ *((guint16 *) out_arg) = arg.v_uint16;
+ break;
+ case GI_TYPE_TAG_INT32:
+ *((gint32 *) out_arg) = arg.v_int32;
+ break;
+ case GI_TYPE_TAG_UINT32:
+ *((guint32 *) out_arg) = arg.v_uint32;
+ break;
+ case GI_TYPE_TAG_INT64:
+ *((gint64 *) out_arg) = arg.v_int64;
+ break;
+ case GI_TYPE_TAG_UINT64:
+ *((glong *) out_arg) = arg.v_uint64;
+ break;
+ case GI_TYPE_TAG_FLOAT:
+ *((gfloat *) out_arg) = arg.v_float;
+ break;
+ case GI_TYPE_TAG_DOUBLE:
+ *((gdouble *) out_arg) = arg.v_double;
+ break;
+ default:
+ *((GIArgument *) out_arg) = arg;
+ break;
+ }
+}
static GIArgument *
_pygi_closure_convert_ffi_arguments (GICallableInfo *callable_info, void **args)
@@ -198,6 +245,7 @@ _pygi_closure_convert_arguments (GICallableInfo *callable_info, void **args,
if (_PyTuple_Resize (py_args, n_in_args) == -1)
goto error;
+ g_free (g_args);
return TRUE;
error:
@@ -225,15 +273,14 @@ _pygi_closure_set_out_arguments (GICallableInfo *callable_info,
return_type_info = g_callable_info_get_return_type (callable_info);
return_type_tag = g_type_info_get_tag (return_type_info);
if (return_type_tag != GI_TYPE_TAG_VOID) {
- GIArgument arg;
GITransfer transfer = g_callable_info_get_caller_owns (callable_info);
if (PyTuple_Check (py_retval)) {
PyObject *item = PyTuple_GET_ITEM (py_retval, 0);
- arg = _pygi_argument_from_object (item, return_type_info, transfer);
- * ( (GIArgument*) resp) = arg;
+ _pygi_closure_assign_pyobj_to_out_argument (resp, item,
+ return_type_info, transfer);
} else {
- arg = _pygi_argument_from_object (py_retval, return_type_info, transfer);
- * ( (GIArgument*) resp) = arg;
+ _pygi_closure_assign_pyobj_to_out_argument (resp, py_retval,
+ return_type_info, transfer);
}
i_py_retval++;
}
@@ -248,14 +295,22 @@ _pygi_closure_set_out_arguments (GICallableInfo *callable_info,
if (direction == GI_DIRECTION_OUT || direction == GI_DIRECTION_INOUT) {
GITransfer transfer = g_arg_info_get_ownership_transfer (arg_info);
- GIArgument arg;
+
+ if (g_type_info_get_tag (type_info) == GI_TYPE_TAG_ERROR) {
+ /* TODO: check if an exception has been set and convert it to a GError */
+ out_args[i_out_args].v_pointer = NULL;
+ i_out_args++;
+ continue;
+ }
+
if (PyTuple_Check (py_retval)) {
PyObject *item = PyTuple_GET_ITEM (py_retval, i_py_retval);
- arg = _pygi_argument_from_object (item, type_info, transfer);
- * ( (GIArgument*) out_args[i_out_args].v_pointer) = arg;
+ _pygi_closure_assign_pyobj_to_out_argument (
+ out_args[i_out_args].v_pointer, item, type_info, transfer);
} else if (i_py_retval == 0) {
- arg = _pygi_argument_from_object (py_retval, type_info, transfer);
- * ( (GIArgument*) out_args[i_out_args].v_pointer) = arg;
+ _pygi_closure_assign_pyobj_to_out_argument (
+ out_args[i_out_args].v_pointer, py_retval, type_info,
+ transfer);
} else
g_assert_not_reached();
@@ -309,6 +364,8 @@ _pygi_closure_handle (ffi_cif *cif,
_pygi_closure_set_out_arguments (closure->info, retval, out_args, result);
end:
+ if (out_args != NULL)
+ g_free (out_args);
g_base_info_unref ( (GIBaseInfo*) return_type);
PyGILState_Release (state);
diff --git a/gi/pygi-foreign-cairo.c b/gi/pygi-foreign-cairo.c
index 095f6cbe..0264b71f 100644
--- a/gi/pygi-foreign-cairo.c
+++ b/gi/pygi-foreign-cairo.c
@@ -22,6 +22,7 @@
*/
#include <cairo.h>
+#include <Python.h>
#if PY_VERSION_HEX < 0x03000000
#include <pycairo.h>
@@ -55,9 +56,9 @@ cairo_context_to_arg (PyObject *value,
}
PyObject *
-cairo_context_from_arg (GITypeInfo *type_info, GIArgument *arg)
+cairo_context_from_arg (GITypeInfo *type_info, gpointer data)
{
- cairo_t *context = (cairo_t*) arg;
+ cairo_t *context = (cairo_t*) data;
cairo_reference (context);
@@ -94,9 +95,9 @@ cairo_surface_to_arg (PyObject *value,
}
PyObject *
-cairo_surface_from_arg (GITypeInfo *type_info, GIArgument *arg)
+cairo_surface_from_arg (GITypeInfo *type_info, gpointer data)
{
- cairo_surface_t *surface = (cairo_surface_t*) arg;
+ cairo_surface_t *surface = (cairo_surface_t*) data;
cairo_surface_reference (surface);
@@ -111,13 +112,12 @@ cairo_surface_release (GIBaseInfo *base_info,
Py_RETURN_NONE;
}
-
static PyMethodDef _gi_cairo_functions[] = {};
PYGLIB_MODULE_START(_gi_cairo, "_gi_cairo")
{
Pycairo_IMPORT;
if (Pycairo_CAPI == NULL)
- return 0;
+ return PYGLIB_MODULE_ERROR_RETURN;
pygi_register_foreign_struct ("cairo",
"Context",
diff --git a/gi/pygi-foreign-gvariant.c b/gi/pygi-foreign-gvariant.c
index ac163950..3c8ae8d6 100644
--- a/gi/pygi-foreign-gvariant.c
+++ b/gi/pygi-foreign-gvariant.c
@@ -42,9 +42,9 @@ g_variant_to_arg (PyObject *value,
PyObject *
g_variant_from_arg (GITypeInfo *type_info,
- GIArgument *arg)
+ gpointer data)
{
- GVariant *variant = (GVariant *) arg;
+ GVariant *variant = (GVariant *) data;
GITypeInfo *interface_info = g_type_info_get_interface (type_info);
PyObject *type = _pygi_type_import_by_gi_info (interface_info);
diff --git a/gi/pygi-foreign-gvariant.h b/gi/pygi-foreign-gvariant.h
index 6de8c57a..b0c97811 100644
--- a/gi/pygi-foreign-gvariant.h
+++ b/gi/pygi-foreign-gvariant.h
@@ -32,7 +32,7 @@ PyObject *g_variant_to_arg(PyObject *value,
GIArgument *arg);
PyObject *g_variant_from_arg(GITypeInfo *type_info,
- GIArgument *arg);
+ gpointer data);
PyObject *g_variant_release_foreign (GIBaseInfo *base_info,
gpointer struct_);
diff --git a/gi/pygi-foreign.c b/gi/pygi-foreign.c
index 75d5bb91..5c9a88aa 100644
--- a/gi/pygi-foreign.c
+++ b/gi/pygi-foreign.c
@@ -165,7 +165,7 @@ pygi_register_foreign_struct_real (const char* namespace_,
PyGIArgOverrideFromGIArgumentFunc from_func,
PyGIArgOverrideReleaseFunc release_func)
{
- PyGIForeignStruct *new_struct = g_slice_new0 (PyGIForeignStruct);
+ PyGIForeignStruct *new_struct = g_slice_new (PyGIForeignStruct);
new_struct->namespace = namespace_;
new_struct->name = name;
new_struct->to_func = to_func;
diff --git a/gi/pygi-info.c b/gi/pygi-info.c
index feeccf7d..1bfd7d82 100644
--- a/gi/pygi-info.c
+++ b/gi/pygi-info.c
@@ -57,6 +57,34 @@ _base_info_repr (PyGIBaseInfo *self)
(void *) self);
}
+static PyObject *
+_base_info_richcompare (PyGIBaseInfo *self, PyObject *other, int op)
+{
+ PyObject *res;
+ GIBaseInfo *other_info;
+
+ if (!PyObject_TypeCheck(other, &PyGIBaseInfo_Type)) {
+ Py_INCREF(Py_NotImplemented);
+ return Py_NotImplemented;
+ }
+
+ other_info = ((PyGIBaseInfo *)other)->info;
+
+ switch (op) {
+ case Py_EQ:
+ res = g_base_info_equal (self->info, other_info) ? Py_True : Py_False;
+ break;
+ case Py_NE:
+ res = g_base_info_equal (self->info, other_info) ? Py_False : Py_True;
+ break;
+ default:
+ res = Py_NotImplemented;
+ break;
+ }
+ Py_INCREF(res);
+ return res;
+}
+
static PyMethodDef _PyGIBaseInfo_methods[];
PYGLIB_DEFINE_TYPE("gi.BaseInfo", PyGIBaseInfo_Type, PyGIBaseInfo);
@@ -113,14 +141,14 @@ _pygi_info_new (GIBaseInfo *info)
type = &PyGIFunctionInfo_Type;
break;
case GI_INFO_TYPE_CALLBACK:
- PyErr_SetString (PyExc_NotImplementedError, "GICallbackInfo bindings not implemented");
- return NULL;
+ type = &PyGICallbackInfo_Type;
+ break;
case GI_INFO_TYPE_STRUCT:
type = &PyGIStructInfo_Type;
break;
case GI_INFO_TYPE_BOXED:
- PyErr_SetString (PyExc_NotImplementedError, "GIBoxedInfo bindings not implemented");
- return NULL;
+ type = &PyGIBoxedInfo_Type;
+ break;
case GI_INFO_TYPE_ENUM:
case GI_INFO_TYPE_FLAGS:
type = &PyGIEnumInfo_Type;
@@ -135,8 +163,8 @@ _pygi_info_new (GIBaseInfo *info)
type = &PyGIConstantInfo_Type;
break;
case GI_INFO_TYPE_ERROR_DOMAIN:
- PyErr_SetString (PyExc_NotImplementedError, "GIErrorDomainInfo bindings not implemented");
- return NULL;
+ type = &PyGIErrorDomainInfo_Type;
+ break;
case GI_INFO_TYPE_UNION:
type = &PyGIUnionInfo_Type;
break;
@@ -144,23 +172,23 @@ _pygi_info_new (GIBaseInfo *info)
type = &PyGIValueInfo_Type;
break;
case GI_INFO_TYPE_SIGNAL:
- PyErr_SetString (PyExc_NotImplementedError, "GISignalInfo bindings not implemented");
- return NULL;
+ type = &PyGISignalInfo_Type;
+ break;
case GI_INFO_TYPE_VFUNC:
type = &PyGIVFuncInfo_Type;
break;
case GI_INFO_TYPE_PROPERTY:
- PyErr_SetString (PyExc_NotImplementedError, "GIPropertyInfo bindings not implemented");
- return NULL;
+ type = &PyGIPropertyInfo_Type;
+ break;
case GI_INFO_TYPE_FIELD:
type = &PyGIFieldInfo_Type;
break;
case GI_INFO_TYPE_ARG:
- PyErr_SetString (PyExc_NotImplementedError, "GIArgInfo bindings not implemented");
- return NULL;
+ type = &PyGIArgInfo_Type;
+ break;
case GI_INFO_TYPE_TYPE:
- PyErr_SetString (PyExc_NotImplementedError, "GITypeInfo bindings not implemented");
- return NULL;
+ type = &PyGITypeInfo_Type;
+ break;
case GI_INFO_TYPE_UNRESOLVED:
type = &PyGIUnresolvedInfo_Type;
break;
@@ -206,7 +234,94 @@ out:
/* CallableInfo */
PYGLIB_DEFINE_TYPE ("gi.CallableInfo", PyGICallableInfo_Type, PyGIBaseInfo);
+static PyObject *
+_wrap_g_callable_info_get_arguments (PyGIBaseInfo *self)
+{
+ gssize n_infos;
+ PyObject *infos;
+ gssize i;
+
+ n_infos = g_callable_info_get_n_args ( (GICallableInfo *) self->info);
+
+ infos = PyTuple_New (n_infos);
+ if (infos == NULL) {
+ return NULL;
+ }
+
+ for (i = 0; i < n_infos; i++) {
+ GIBaseInfo *info;
+ PyObject *py_info;
+
+ info = (GIBaseInfo *) g_callable_info_get_arg ( (GICallableInfo *) self->info, i);
+ g_assert (info != NULL);
+
+ py_info = _pygi_info_new (info);
+
+ g_base_info_unref (info);
+
+ if (py_info == NULL) {
+ Py_CLEAR (infos);
+ break;
+ }
+
+ PyTuple_SET_ITEM (infos, i, py_info);
+ }
+
+ return infos;
+}
+
static PyMethodDef _PyGICallableInfo_methods[] = {
+ { "invoke", (PyCFunction) _wrap_g_callable_info_invoke, METH_VARARGS | METH_KEYWORDS },
+ { "get_arguments", (PyCFunction) _wrap_g_callable_info_get_arguments, METH_NOARGS },
+ { NULL, NULL, 0 }
+};
+
+/* CallbackInfo */
+PYGLIB_DEFINE_TYPE ("gi.CallbackInfo", PyGICallbackInfo_Type, PyGIBaseInfo);
+
+static PyMethodDef _PyGICallbackInfo_methods[] = {
+ { NULL, NULL, 0 }
+};
+
+/* BoxedInfo */
+PYGLIB_DEFINE_TYPE ("gi.BoxedInfo", PyGIBoxedInfo_Type, PyGIBaseInfo);
+
+static PyMethodDef _PyGIBoxedInfo_methods[] = {
+ { NULL, NULL, 0 }
+};
+
+/* ErrorDomainInfo */
+PYGLIB_DEFINE_TYPE ("gi.ErrorDomainInfo", PyGIErrorDomainInfo_Type, PyGIBaseInfo);
+
+static PyMethodDef _PyGIErrorDomainInfo_methods[] = {
+ { NULL, NULL, 0 }
+};
+
+/* SignalInfo */
+PYGLIB_DEFINE_TYPE ("gi.SignalInfo", PyGISignalInfo_Type, PyGIBaseInfo);
+
+static PyMethodDef _PyGISignalInfo_methods[] = {
+ { NULL, NULL, 0 }
+};
+
+/* PropertyInfo */
+PYGLIB_DEFINE_TYPE ("gi.PropertyInfo", PyGIPropertyInfo_Type, PyGIBaseInfo);
+
+static PyMethodDef _PyGIPropertyInfo_methods[] = {
+ { NULL, NULL, 0 }
+};
+
+/* ArgInfo */
+PYGLIB_DEFINE_TYPE ("gi.ArgInfo", PyGIArgInfo_Type, PyGIBaseInfo);
+
+static PyMethodDef _PyGIArgInfo_methods[] = {
+ { NULL, NULL, 0 }
+};
+
+/* TypeInfo */
+PYGLIB_DEFINE_TYPE ("gi.TypeInfo", PyGITypeInfo_Type, PyGIBaseInfo);
+
+static PyMethodDef _PyGITypeInfo_methods[] = {
{ NULL, NULL, 0 }
};
@@ -272,6 +387,9 @@ _pygi_g_type_tag_size (GITypeTag type_tag)
case GI_TYPE_TAG_GTYPE:
size = sizeof (GType);
break;
+ case GI_TYPE_TAG_UNICHAR:
+ size = sizeof (gunichar);
+ break;
case GI_TYPE_TAG_VOID:
case GI_TYPE_TAG_UTF8:
case GI_TYPE_TAG_FILENAME:
@@ -311,6 +429,7 @@ _pygi_g_type_info_size (GITypeInfo *type_info)
case GI_TYPE_TAG_FLOAT:
case GI_TYPE_TAG_DOUBLE:
case GI_TYPE_TAG_GTYPE:
+ case GI_TYPE_TAG_UNICHAR:
if (g_type_info_is_pointer (type_info)) {
size = sizeof (gpointer);
} else {
@@ -395,7 +514,6 @@ _pygi_g_type_info_size (GITypeInfo *type_info)
static PyMethodDef _PyGIFunctionInfo_methods[] = {
{ "is_constructor", (PyCFunction) _wrap_g_function_info_is_constructor, METH_NOARGS },
{ "is_method", (PyCFunction) _wrap_g_function_info_is_method, METH_NOARGS },
- { "invoke", (PyCFunction) _wrap_g_function_info_invoke, METH_VARARGS },
{ NULL, NULL, 0 }
};
@@ -690,6 +808,7 @@ pygi_g_struct_info_is_simple (GIStructInfo *struct_info)
case GI_TYPE_TAG_UINT64:
case GI_TYPE_TAG_FLOAT:
case GI_TYPE_TAG_DOUBLE:
+ case GI_TYPE_TAG_UNICHAR:
if (g_type_info_is_pointer (field_type_info)) {
is_simple = FALSE;
}
@@ -804,8 +923,23 @@ _wrap_g_enum_info_get_values (PyGIBaseInfo *self)
return infos;
}
+static PyObject *
+_wrap_g_enum_info_is_flags (PyGIBaseInfo *self)
+{
+ GIInfoType info_type = g_base_info_get_type ((GIBaseInfo *) self->info);
+
+ if (info_type == GI_INFO_TYPE_ENUM) {
+ Py_RETURN_FALSE;
+ } else if (info_type == GI_INFO_TYPE_FLAGS) {
+ Py_RETURN_TRUE;
+ } else {
+ g_assert_not_reached();
+ }
+}
+
static PyMethodDef _PyGIEnumInfo_methods[] = {
{ "get_values", (PyCFunction) _wrap_g_enum_info_get_values, METH_NOARGS },
+ { "is_flags", (PyCFunction) _wrap_g_enum_info_is_flags, METH_NOARGS },
{ NULL, NULL, 0 }
};
@@ -1276,7 +1410,23 @@ static PyMethodDef _PyGIUnresolvedInfo_methods[] = {
/* GIVFuncInfo */
PYGLIB_DEFINE_TYPE ("gi.VFuncInfo", PyGIVFuncInfo_Type, PyGIBaseInfo);
+static PyObject *
+_wrap_g_vfunc_info_get_invoker (PyGIBaseInfo *self)
+{
+ PyObject *result = Py_None;
+ GIBaseInfo *info;
+
+ info = (GIBaseInfo *) g_vfunc_info_get_invoker ( (GIVFuncInfo *) self->info );
+ if (info)
+ result = _pygi_info_new(info);
+ else
+ Py_INCREF(Py_None);
+
+ return result;
+}
+
static PyMethodDef _PyGIVFuncInfo_methods[] = {
+ { "get_invoker", (PyCFunction) _wrap_g_vfunc_info_get_invoker, METH_NOARGS },
{ NULL, NULL, 0 }
};
@@ -1413,6 +1563,7 @@ _pygi_info_register_types (PyObject *m)
PyGIBaseInfo_Type.tp_traverse = (traverseproc) _base_info_traverse;
PyGIBaseInfo_Type.tp_weaklistoffset = offsetof(PyGIBaseInfo, inst_weakreflist);
PyGIBaseInfo_Type.tp_methods = _PyGIBaseInfo_methods;
+ PyGIBaseInfo_Type.tp_richcompare = (richcmpfunc)_base_info_richcompare;
if (PyType_Ready(&PyGIBaseInfo_Type))
return;
@@ -1424,6 +1575,8 @@ _pygi_info_register_types (PyObject *m)
PyGIBaseInfo_Type);
_PyGI_REGISTER_TYPE (m, PyGICallableInfo_Type, CallableInfo,
PyGIBaseInfo_Type);
+ _PyGI_REGISTER_TYPE (m, PyGICallbackInfo_Type, CallbackInfo,
+ PyGIBaseInfo_Type);
_PyGI_REGISTER_TYPE (m, PyGIFunctionInfo_Type, FunctionInfo,
PyGICallableInfo_Type);
_PyGI_REGISTER_TYPE (m, PyGIRegisteredTypeInfo_Type, RegisteredTypeInfo,
@@ -1443,9 +1596,21 @@ _pygi_info_register_types (PyObject *m)
_PyGI_REGISTER_TYPE (m, PyGIFieldInfo_Type, FieldInfo,
PyGIBaseInfo_Type);
_PyGI_REGISTER_TYPE (m, PyGIVFuncInfo_Type, VFuncInfo,
- PyGIBaseInfo_Type);
+ PyGICallableInfo_Type);
_PyGI_REGISTER_TYPE (m, PyGIUnionInfo_Type, UnionInfo,
PyGIRegisteredTypeInfo_Type);
+ _PyGI_REGISTER_TYPE (m, PyGIBoxedInfo_Type, BoxedInfo,
+ PyGIBaseInfo_Type);
+ _PyGI_REGISTER_TYPE (m, PyGIErrorDomainInfo_Type, ErrorDomainInfo,
+ PyGIBaseInfo_Type);
+ _PyGI_REGISTER_TYPE (m, PyGISignalInfo_Type, SignalInfo,
+ PyGIBaseInfo_Type);
+ _PyGI_REGISTER_TYPE (m, PyGIPropertyInfo_Type, PropertyInfo,
+ PyGIBaseInfo_Type);
+ _PyGI_REGISTER_TYPE (m, PyGIArgInfo_Type, ArgInfo,
+ PyGIBaseInfo_Type);
+ _PyGI_REGISTER_TYPE (m, PyGITypeInfo_Type, TypeInfo,
+ PyGIBaseInfo_Type);
#undef _PyGI_REGISTER_TYPE
diff --git a/gi/pygi-info.h b/gi/pygi-info.h
index 0d2bade3..afd65dc6 100644
--- a/gi/pygi-info.h
+++ b/gi/pygi-info.h
@@ -35,6 +35,7 @@ gboolean pygi_g_struct_info_is_simple (GIStructInfo *struct_info);
extern PyTypeObject PyGIBaseInfo_Type;
extern PyTypeObject PyGICallableInfo_Type;
+extern PyTypeObject PyGICallbackInfo_Type;
extern PyTypeObject PyGIFunctionInfo_Type;
extern PyTypeObject PyGIRegisteredTypeInfo_Type;
extern PyTypeObject PyGIStructInfo_Type;
@@ -47,6 +48,12 @@ extern PyTypeObject PyGIFieldInfo_Type;
extern PyTypeObject PyGIUnresolvedInfo_Type;
extern PyTypeObject PyGIVFuncInfo_Type;
extern PyTypeObject PyGIUnionInfo_Type;
+extern PyTypeObject PyGIBoxedInfo_Type;
+extern PyTypeObject PyGIErrorDomainInfo_Type;
+extern PyTypeObject PyGISignalInfo_Type;
+extern PyTypeObject PyGIPropertyInfo_Type;
+extern PyTypeObject PyGIArgInfo_Type;
+extern PyTypeObject PyGITypeInfo_Type;
#define PyGIBaseInfo_GET_GI_INFO(object) g_base_info_ref(((PyGIBaseInfo *)object)->info)
diff --git a/gi/pygi-invoke.c b/gi/pygi-invoke.c
index 64b3f31a..78984e5c 100644
--- a/gi/pygi-invoke.c
+++ b/gi/pygi-invoke.c
@@ -21,6 +21,7 @@
* USA
*/
+#include <pyglib.h>
#include "pygi-invoke.h"
struct invocation_state
@@ -58,19 +59,46 @@ struct invocation_state
GIArgument *backup_args;
GIArgument return_arg;
- PyObject *return_value;
+ PyObject *return_value;
+
+ GType implementor_gtype;
+
+ /* hack to avoid treating C arrays as GArrays during free
+ * due to overly complicated array handling
+ * this will be removed when the new invoke branch is merged
+ */
+ gboolean c_arrays_are_wrapped;
};
-static void
+static gboolean
_initialize_invocation_state (struct invocation_state *state,
GIFunctionInfo *info,
- PyObject *py_args)
+ PyObject *py_args,
+ PyObject *kwargs)
{
- GIFunctionInfoFlags flags;
+ if (g_base_info_get_type (info) == GI_INFO_TYPE_FUNCTION) {
+ GIFunctionInfoFlags flags = g_function_info_get_flags (info);
- flags = g_function_info_get_flags (info);
- state->is_method = (flags & GI_FUNCTION_IS_METHOD) != 0;
- state->is_constructor = (flags & GI_FUNCTION_IS_CONSTRUCTOR) != 0;
+ state->is_method = (flags & GI_FUNCTION_IS_METHOD) != 0;
+ state->is_constructor = (flags & GI_FUNCTION_IS_CONSTRUCTOR) != 0;
+ state->implementor_gtype = 0;
+ } else {
+ PyObject *obj;
+
+ state->is_method = TRUE;
+ state->is_constructor = FALSE;
+
+ obj = PyDict_GetItemString (kwargs, "gtype");
+ if (obj == NULL) {
+ PyErr_SetString (PyExc_TypeError,
+ "need the GType of the implementor class");
+ return FALSE;
+ }
+
+ state->implementor_gtype = pyg_type_from_object (obj);
+ if (state->implementor_gtype == 0)
+ return FALSE;
+ }
/* Count arguments. */
state->n_args = g_callable_info_get_n_args ( (GICallableInfo *) info);
@@ -98,6 +126,13 @@ _initialize_invocation_state (struct invocation_state *state,
state->out_args = NULL;
state->out_values = NULL;
state->backup_args = NULL;
+
+ /* HACK: this gets marked FALSE whenever a C array in the args is
+ * not wrapped by a GArray
+ */
+ state->c_arrays_are_wrapped = TRUE;
+
+ return TRUE;
}
static gboolean
@@ -343,9 +378,20 @@ _prepare_invocation_state (struct invocation_state *state,
/* if caller allocates only use one level of indirection */
state->out_args[out_args_pos].v_pointer = NULL;
state->args[i] = &state->out_args[out_args_pos];
- if (g_type_is_a (g_registered_type_info_get_g_type (info), G_TYPE_BOXED))
+ if (g_type_is_a (g_registered_type_info_get_g_type (info), G_TYPE_BOXED)) {
state->args[i]->v_pointer = _pygi_boxed_alloc (info, NULL);
- else {
+ } else if (g_struct_info_is_foreign((GIStructInfo *) info) ) {
+ PyObject *foreign_struct =
+ pygi_struct_foreign_convert_from_g_argument(state->arg_type_infos[i], NULL);
+
+ pygi_struct_foreign_convert_to_g_argument(
+ foreign_struct,
+ state->arg_type_infos[i],
+ GI_TRANSFER_EVERYTHING,
+ state->args[i]);
+
+ Py_DECREF(foreign_struct);
+ } else {
gssize size = g_struct_info_get_size ( (GIStructInfo *) info);
state->args[i]->v_pointer = g_malloc0 (size);
}
@@ -521,6 +567,20 @@ _prepare_invocation_state (struct invocation_state *state,
(g_type_info_get_array_type (state->arg_type_infos[i]) == GI_ARRAY_TYPE_C)) {
state->args[i]->v_pointer = array->data;
+ /* HACK: We have unwrapped a C array so
+ * set the state to reflect this.
+ * If there is an error between now
+ * and when we rewrap the array
+ * we will leak C arrays due to
+ * being in an inconsitant state.
+ * e.g. for interfaces with more
+ * than one C array argument, an
+ * error may occure when not all
+ * C arrays have been rewrapped.
+ * This will be removed once the invoke
+ * rewrite branch is merged.
+ */
+ state->c_arrays_are_wrapped = FALSE;
if (direction != GI_DIRECTION_INOUT || transfer != GI_TRANSFER_NOTHING) {
/* The array hasn't been referenced anywhere, so free it to avoid losing memory. */
g_array_free (array, FALSE);
@@ -541,20 +601,36 @@ _prepare_invocation_state (struct invocation_state *state,
static gboolean
_invoke_function (struct invocation_state *state,
- GIFunctionInfo *function_info, PyObject *py_args)
+ GICallableInfo *callable_info, PyObject *py_args)
{
GError *error;
gint retval;
error = NULL;
- retval = g_function_info_invoke ( (GIFunctionInfo *) function_info,
- state->in_args, state->n_in_args, state->out_args, state->n_out_args, &state->return_arg, &error);
+ pyg_begin_allow_threads;
+ if (g_base_info_get_type (callable_info) == GI_INFO_TYPE_FUNCTION) {
+ retval = g_function_info_invoke ( (GIFunctionInfo *) callable_info,
+ state->in_args,
+ state->n_in_args,
+ state->out_args,
+ state->n_out_args,
+ &state->return_arg,
+ &error);
+ } else {
+ retval = g_vfunc_info_invoke ( (GIVFuncInfo *) callable_info,
+ state->implementor_gtype,
+ state->in_args,
+ state->n_in_args,
+ state->out_args,
+ state->n_out_args,
+ &state->return_arg,
+ &error);
+ }
+ pyg_end_allow_threads;
+
if (!retval) {
- g_assert (error != NULL);
- /* TODO: raise the right error, out of the error domain. */
- PyErr_SetString (PyExc_RuntimeError, error->message);
- g_error_free (error);
+ pyglib_error_check(&error);
/* TODO: release input arguments. */
@@ -566,11 +642,7 @@ _invoke_function (struct invocation_state *state,
error = state->args[state->error_arg_pos]->v_pointer;
- if (*error != NULL) {
- /* TODO: raise the right error, out of the error domain, if applicable. */
- PyErr_SetString (PyExc_Exception, (*error)->message);
- g_error_free (*error);
-
+ if (pyglib_error_check(error)) {
/* TODO: release input arguments. */
return FALSE;
@@ -653,6 +725,10 @@ _process_invocation_state (struct invocation_state *state,
/* The new wrapper increased the reference count, so decrease it. */
g_object_unref (state->return_arg.v_pointer);
}
+ if (state->is_constructor && G_IS_INITIALLY_UNOWNED (state->return_arg.v_pointer)) {
+ /* GInitiallyUnowned constructors always end up with one extra reference, so decrease it. */
+ g_object_unref (state->return_arg.v_pointer);
+ }
break;
default:
/* Other types don't have neither methods nor constructors. */
@@ -753,13 +829,16 @@ _process_invocation_state (struct invocation_state *state,
if (type_tag == GI_TYPE_TAG_INTERFACE) {
GIBaseInfo *info;
GIInfoType info_type;
+ GType type;
info = g_type_info_get_interface (state->arg_type_infos[i]);
g_assert (info != NULL);
info_type = g_base_info_get_type (info);
+ type = g_registered_type_info_get_g_type ( (GIRegisteredTypeInfo *) info);
if ( (info_type == GI_INFO_TYPE_STRUCT) &&
- !g_struct_info_is_foreign((GIStructInfo *) info) ) {
+ !g_struct_info_is_foreign((GIStructInfo *) info) &&
+ !g_type_is_a (type, G_TYPE_BOXED)) {
if (g_arg_info_is_caller_allocates (state->arg_infos[i])) {
transfer = GI_TRANSFER_EVERYTHING;
} else if (transfer == GI_TRANSFER_EVERYTHING) {
@@ -797,6 +876,14 @@ _process_invocation_state (struct invocation_state *state,
}
+ /* HACK: We rewrapped any C arrays above in a GArray so they are ok to
+ * free as GArrays. We will always leak C arrays if there is
+ * an error before we reach this state as there is no easy way
+ * to know which arrays were wrapped if there are more than one.
+ * This will be removed with better array handling once merge
+ * the invoke rewrite branch.
+ */
+ state->c_arrays_are_wrapped = TRUE;
g_assert (state->n_return_values <= 1 || return_values_pos == state->n_return_values);
}
@@ -827,9 +914,7 @@ _free_invocation_state (struct invocation_state *state)
continue;
}
- if (state->args != NULL
- && state->args[i] != NULL
- && state->arg_infos[i] != NULL
+ if (state->arg_infos[i] != NULL
&& state->arg_type_infos[i] != NULL) {
GIDirection direction;
GITypeTag type_tag;
@@ -838,20 +923,38 @@ _free_invocation_state (struct invocation_state *state)
direction = g_arg_info_get_direction (state->arg_infos[i]);
transfer = g_arg_info_get_ownership_transfer (state->arg_infos[i]);
- type_tag = g_type_info_get_tag (state->arg_type_infos[i]);
-
/* Release the argument. */
if (direction == GI_DIRECTION_INOUT) {
- _pygi_argument_release (&state->backup_args[backup_args_pos], state->arg_type_infos[i],
- transfer, GI_DIRECTION_IN);
+ if (state->args != NULL) {
+ _pygi_argument_release (&state->backup_args[backup_args_pos],
+ state->arg_type_infos[i],
+ transfer, GI_DIRECTION_IN);
+ }
backup_args_pos += 1;
}
- _pygi_argument_release (state->args[i], state->arg_type_infos[i], transfer, direction);
+ if (state->args != NULL && state->args[i] != NULL) {
+ type_tag = g_type_info_get_tag (state->arg_type_infos[i]);
+
+ if (type_tag == GI_TYPE_TAG_ARRAY &&
+ (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT) &&
+ (g_type_info_get_array_type (state->arg_type_infos[i]) == GI_ARRAY_TYPE_C) &&
+ !state->c_arrays_are_wrapped) {
+ /* HACK: Noop - we are in an inconsitant state due to
+ * complex array handler so leak any C arrays
+ * as we don't know if we can free them safely.
+ * This will be removed when we merge the
+ * invoke rewrite branch.
+ */
+ } else {
+ _pygi_argument_release (state->args[i], state->arg_type_infos[i],
+ transfer, direction);
+ }
- if (type_tag == GI_TYPE_TAG_ARRAY
+ if (type_tag == GI_TYPE_TAG_ARRAY
&& (direction != GI_DIRECTION_IN && transfer == GI_TRANSFER_NOTHING)) {
- /* We created a #GArray and it has not been released above, so free it. */
- state->args[i]->v_pointer = g_array_free (state->args[i]->v_pointer, FALSE);
+ /* We created an *out* #GArray and it has not been released above, so free it. */
+ state->args[i]->v_pointer = g_array_free (state->args[i]->v_pointer, FALSE);
+ }
}
}
@@ -894,11 +997,15 @@ _free_invocation_state (struct invocation_state *state)
PyObject *
-_wrap_g_function_info_invoke (PyGIBaseInfo *self, PyObject *py_args)
+_wrap_g_callable_info_invoke (PyGIBaseInfo *self, PyObject *py_args,
+ PyObject *kwargs)
{
struct invocation_state state = { 0, };
- _initialize_invocation_state (&state, self->info, py_args);
+ if (!_initialize_invocation_state (&state, self->info, py_args, kwargs)) {
+ _free_invocation_state (&state);
+ return NULL;
+ }
if (!_prepare_invocation_state (&state, self->info, py_args)) {
_free_invocation_state (&state);
diff --git a/gi/pygi-invoke.h b/gi/pygi-invoke.h
index 0d07b21a..dc1ce18a 100644
--- a/gi/pygi-invoke.h
+++ b/gi/pygi-invoke.h
@@ -30,7 +30,8 @@
G_BEGIN_DECLS
-PyObject *_wrap_g_function_info_invoke (PyGIBaseInfo *self, PyObject *py_args);
+PyObject *_wrap_g_callable_info_invoke (PyGIBaseInfo *self, PyObject *py_args,
+ PyObject *kwargs);
G_END_DECLS
diff --git a/gi/pygi-private.h b/gi/pygi-private.h
index 3a14bc31..efe62c85 100644
--- a/gi/pygi-private.h
+++ b/gi/pygi-private.h
@@ -29,6 +29,7 @@
#include "pygi-callbacks.h"
#include "pygi-invoke.h"
#include "pygi-property.h"
+#include "pygi-signal-closure.h"
G_BEGIN_DECLS
#if PY_VERSION_HEX >= 0x03000000
diff --git a/gi/pygi-property.c b/gi/pygi-property.c
index 7b6a508b..2f8970da 100644
--- a/gi/pygi-property.c
+++ b/gi/pygi-property.c
@@ -139,7 +139,7 @@ pygi_get_property_value_real (PyGObject *instance,
arg.v_double = g_value_get_double (&value);
break;
case GI_TYPE_TAG_GTYPE:
- arg.v_size = g_value_get_uint (&value);
+ arg.v_size = g_value_get_gtype (&value);
break;
case GI_TYPE_TAG_UTF8:
case GI_TYPE_TAG_FILENAME:
@@ -155,6 +155,8 @@ pygi_get_property_value_real (PyGObject *instance,
type = g_registered_type_info_get_g_type (info);
info_type = g_base_info_get_type (info);
+ g_base_info_unref (info);
+
switch (info_type) {
case GI_INFO_TYPE_ENUM:
arg.v_int32 = g_value_get_enum (&value);
@@ -261,6 +263,8 @@ pygi_set_property_value_real (PyGObject *instance,
type = g_registered_type_info_get_g_type (info);
info_type = g_base_info_get_type (info);
+ g_base_info_unref (info);
+
switch (info_type) {
case GI_INFO_TYPE_ENUM:
g_value_set_enum (&value, arg.v_int32);
@@ -310,7 +314,7 @@ pygi_set_property_value_real (PyGObject *instance,
g_value_set_double (&value, arg.v_double);
break;
case GI_TYPE_TAG_GTYPE:
- g_value_set_uint (&value, arg.v_size);
+ g_value_set_gtype (&value, arg.v_size);
break;
case GI_TYPE_TAG_UTF8:
case GI_TYPE_TAG_FILENAME:
diff --git a/gi/pygi-repository.c b/gi/pygi-repository.c
index 9b22eaed..c48d2ce5 100644
--- a/gi/pygi-repository.c
+++ b/gi/pygi-repository.c
@@ -234,6 +234,28 @@ _wrap_g_irepository_get_version (PyGIRepository *self,
return PYGLIB_PyUnicode_FromString (version);
}
+static PyObject *
+_wrap_g_irepository_get_loaded_namespaces (PyGIRepository *self)
+{
+ char **namespaces;
+ PyObject *py_namespaces;
+ gssize i;
+
+ namespaces = g_irepository_get_loaded_namespaces (self->repository);
+
+ py_namespaces = PyList_New (0);
+ for (i = 0; namespaces[i] != NULL; i++) {
+ PyObject *py_namespace = PYGLIB_PyUnicode_FromString (namespaces[i]);
+ PyList_Append (py_namespaces, py_namespace);
+ Py_DECREF(py_namespace);
+ g_free (namespaces[i]);
+ }
+
+ g_free (namespaces);
+
+ return py_namespaces;
+}
+
static PyMethodDef _PyGIRepository_methods[] = {
{ "enumerate_versions", (PyCFunction) _wrap_g_irepository_enumerate_versions, METH_VARARGS | METH_KEYWORDS },
{ "get_default", (PyCFunction) _wrap_g_irepository_get_default, METH_STATIC | METH_NOARGS },
@@ -242,6 +264,7 @@ static PyMethodDef _PyGIRepository_methods[] = {
{ "find_by_name", (PyCFunction) _wrap_g_irepository_find_by_name, METH_VARARGS | METH_KEYWORDS },
{ "get_typelib_path", (PyCFunction) _wrap_g_irepository_get_typelib_path, METH_VARARGS | METH_KEYWORDS },
{ "get_version", (PyCFunction) _wrap_g_irepository_get_version, METH_VARARGS | METH_KEYWORDS },
+ { "get_loaded_namespaces", (PyCFunction) _wrap_g_irepository_get_loaded_namespaces, METH_NOARGS },
{ NULL, NULL, 0 }
};
diff --git a/gi/pygi-signal-closure.c b/gi/pygi-signal-closure.c
new file mode 100644
index 00000000..14825298
--- /dev/null
+++ b/gi/pygi-signal-closure.c
@@ -0,0 +1,245 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (c) 2011 Laszlo Pandy <lpandy@src.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 Street, Fifth Floor, Boston, MA 02110-1301
+ * USA
+ */
+
+#include "pygi-private.h"
+
+/* Copied from glib */
+static void
+canonicalize_key (gchar *key)
+{
+ gchar *p;
+
+ for (p = key; *p != 0; p++)
+ {
+ gchar c = *p;
+
+ if (c != '-' &&
+ (c < '0' || c > '9') &&
+ (c < 'A' || c > 'Z') &&
+ (c < 'a' || c > 'z'))
+ *p = '-';
+ }
+}
+
+static GISignalInfo *
+_pygi_lookup_signal_from_g_type (GType g_type,
+ const gchar *signal_name)
+{
+ GIRepository *repository;
+ GIBaseInfo *info;
+ gssize n_infos;
+ gssize i;
+ GType parent;
+
+ repository = g_irepository_get_default();
+ info = g_irepository_find_by_gtype (repository, g_type);
+ if (info != NULL) {
+ n_infos = g_object_info_get_n_signals ( (GIObjectInfo *) info);
+ for (i = 0; i < n_infos; i++) {
+ GISignalInfo *signal_info;
+
+ signal_info = g_object_info_get_signal ( (GIObjectInfo *) info, i);
+ g_assert (info != NULL);
+
+ if (strcmp (signal_name, g_base_info_get_name (signal_info)) == 0) {
+ g_base_info_unref (info);
+ return signal_info;
+ }
+
+ g_base_info_unref (signal_info);
+ }
+
+ g_base_info_unref (info);
+ }
+
+ parent = g_type_parent (g_type);
+ if (parent > 0)
+ return _pygi_lookup_signal_from_g_type (parent, signal_name);
+
+ return NULL;
+}
+
+static void
+pygi_signal_closure_invalidate(gpointer data,
+ GClosure *closure)
+{
+ PyGClosure *pc = (PyGClosure *)closure;
+ PyGILState_STATE state;
+
+ state = PyGILState_Ensure();
+ Py_XDECREF(pc->callback);
+ Py_XDECREF(pc->extra_args);
+ Py_XDECREF(pc->swap_data);
+ PyGILState_Release(state);
+
+ pc->callback = NULL;
+ pc->extra_args = NULL;
+ pc->swap_data = NULL;
+
+ g_base_info_unref (((PyGISignalClosure *) pc)->signal_info);
+ ((PyGISignalClosure *) pc)->signal_info = NULL;
+}
+
+static void
+pygi_signal_closure_marshal(GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data)
+{
+ PyGILState_STATE state;
+ PyGClosure *pc = (PyGClosure *)closure;
+ PyObject *params, *ret = NULL;
+ guint i;
+ GISignalInfo *signal_info;
+ gint n_sig_info_args;
+ gint sig_info_highest_arg;
+
+ state = PyGILState_Ensure();
+
+ signal_info = ((PyGISignalClosure *)closure)->signal_info;
+ n_sig_info_args = g_callable_info_get_n_args(signal_info);
+ /* the first argument to a signal callback is instance,
+ but instance is not counted in the introspection data */
+ sig_info_highest_arg = n_sig_info_args + 1;
+ g_assert_cmpint(sig_info_highest_arg, ==, n_param_values);
+
+ /* construct Python tuple for the parameter values */
+ params = PyTuple_New(n_param_values);
+ for (i = 0; i < n_param_values; i++) {
+ /* swap in a different initial data for connect_object() */
+ if (i == 0 && G_CCLOSURE_SWAP_DATA(closure)) {
+ g_return_if_fail(pc->swap_data != NULL);
+ Py_INCREF(pc->swap_data);
+ PyTuple_SetItem(params, 0, pc->swap_data);
+
+ } else if (i == 0) {
+ PyObject *item = pyg_value_as_pyobject(&param_values[i], FALSE);
+
+ if (!item) {
+ goto out;
+ }
+ PyTuple_SetItem(params, i, item);
+
+ } else if (i < sig_info_highest_arg) {
+ GIArgInfo arg_info;
+ GITypeInfo type_info;
+ GITransfer transfer;
+ GIArgument arg = { 0, };
+ PyObject *item = NULL;
+
+ g_callable_info_load_arg(signal_info, i - 1, &arg_info);
+ g_arg_info_load_type(&arg_info, &type_info);
+ transfer = g_arg_info_get_ownership_transfer(&arg_info);
+
+ arg = _pygi_argument_from_g_value(&param_values[i], &type_info);
+ item = _pygi_argument_to_object(&arg, &type_info, transfer);
+
+ if (item == NULL) {
+ goto out;
+ }
+ PyTuple_SetItem(params, i, item);
+ }
+ }
+ /* params passed to function may have extra arguments */
+ if (pc->extra_args) {
+ PyObject *tuple = params;
+ params = PySequence_Concat(tuple, pc->extra_args);
+ Py_DECREF(tuple);
+ }
+ ret = PyObject_CallObject(pc->callback, params);
+ if (ret == NULL) {
+ if (pc->exception_handler)
+ pc->exception_handler(return_value, n_param_values, param_values);
+ else
+ PyErr_Print();
+ goto out;
+ }
+
+ if (return_value && pyg_value_from_pyobject(return_value, ret) != 0) {
+ PyErr_SetString(PyExc_TypeError,
+ "can't convert return value to desired type");
+
+ if (pc->exception_handler)
+ pc->exception_handler(return_value, n_param_values, param_values);
+ else
+ PyErr_Print();
+ }
+ Py_DECREF(ret);
+
+ out:
+ Py_DECREF(params);
+ PyGILState_Release(state);
+}
+
+GClosure *
+pygi_signal_closure_new_real (PyGObject *instance,
+ const gchar *sig_name,
+ PyObject *callback,
+ PyObject *extra_args,
+ PyObject *swap_data)
+{
+ GClosure *closure = NULL;
+ PyGISignalClosure *pygi_closure = NULL;
+ GType g_type;
+ GISignalInfo *signal_info = NULL;
+ char *signal_name = g_strdup (sig_name);
+
+ g_return_val_if_fail(callback != NULL, NULL);
+
+ canonicalize_key(signal_name);
+
+ g_type = pyg_type_from_object ((PyObject *)instance);
+ signal_info = _pygi_lookup_signal_from_g_type (g_type, signal_name);
+
+ if (signal_info == NULL)
+ goto out;
+
+ closure = g_closure_new_simple(sizeof(PyGISignalClosure), NULL);
+ g_closure_add_invalidate_notifier(closure, NULL, pygi_signal_closure_invalidate);
+ g_closure_set_marshal(closure, pygi_signal_closure_marshal);
+
+ pygi_closure = (PyGISignalClosure *)closure;
+
+ pygi_closure->signal_info = signal_info;
+ Py_INCREF(callback);
+ pygi_closure->pyg_closure.callback = callback;
+
+ if (extra_args != NULL && extra_args != Py_None) {
+ Py_INCREF(extra_args);
+ if (!PyTuple_Check(extra_args)) {
+ PyObject *tmp = PyTuple_New(1);
+ PyTuple_SetItem(tmp, 0, extra_args);
+ extra_args = tmp;
+ }
+ pygi_closure->pyg_closure.extra_args = extra_args;
+ }
+ if (swap_data) {
+ Py_INCREF(swap_data);
+ pygi_closure->pyg_closure.swap_data = swap_data;
+ closure->derivative_flag = TRUE;
+ }
+
+out:
+ g_free (signal_name);
+
+ return closure;
+}
diff --git a/gi/pygi-signal-closure.h b/gi/pygi-signal-closure.h
new file mode 100644
index 00000000..4687f3f7
--- /dev/null
+++ b/gi/pygi-signal-closure.h
@@ -0,0 +1,46 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (c) 2011 Laszlo Pandy <lpandy@src.gnome.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef __PYGI_SIGNAL_CLOSURE_H__
+#define __PYGI_SIGNAL_CLOSURE_H__
+
+#include "pygi.h"
+
+G_BEGIN_DECLS
+
+/* Private */
+typedef struct _PyGISignalClosure
+{
+ PyGClosure pyg_closure;
+ GISignalInfo *signal_info;
+} PyGISignalClosure;
+
+GClosure * pygi_signal_closure_new_real (PyGObject *instance,
+ const gchar *sig_name,
+ PyObject *callback,
+ PyObject *extra_args,
+ PyObject *swap_data);
+
+G_END_DECLS
+
+#endif /* __PYGI_SIGNAL_CLOSURE_H__ */
diff --git a/gi/pygi.h b/gi/pygi.h
index 03f71a47..1c3bf5e3 100644
--- a/gi/pygi.h
+++ b/gi/pygi.h
@@ -60,7 +60,7 @@ typedef PyObject * (*PyGIArgOverrideToGIArgumentFunc) (PyObject *value,
GITransfer transfer,
GIArgument *arg);
typedef PyObject * (*PyGIArgOverrideFromGIArgumentFunc) (GITypeInfo *type_info,
- GIArgument *arg);
+ gpointer data);
typedef PyObject * (*PyGIArgOverrideReleaseFunc) (GITypeInfo *type_info,
gpointer struct_);
@@ -71,6 +71,11 @@ struct PyGI_API {
gint (*set_property_value) (PyGObject *instance,
const gchar *attr_name,
PyObject *value);
+ GClosure * (*signal_closure_new) (PyGObject *instance,
+ const gchar *sig_name,
+ PyObject *callback,
+ PyObject *extra_args,
+ PyObject *swap_data);
void (*register_foreign_struct) (const char* namespace_,
const char* name,
PyGIArgOverrideToGIArgumentFunc to_func,
@@ -83,14 +88,20 @@ static struct PyGI_API *PyGI_API = NULL;
static int
_pygi_import (void)
{
+ PyObject *modules_dict;
+
if (PyGI_API != NULL) {
return 1;
}
+
+ modules_dict = PyImport_GetModuleDict(); /* borrowed reference -- don't unref */
+ if (PyMapping_HasKeyString(modules_dict, "gi")) {
#if PY_VERSION_HEX >= 0x03000000
- PyGI_API = (struct PyGI_API*) PyCapsule_Import("gi._API", FALSE);
+ PyGI_API = (struct PyGI_API*) PyCapsule_Import("gi._API", FALSE);
#else
- PyGI_API = (struct PyGI_API*) PyCObject_Import("gi", "_API");
+ PyGI_API = (struct PyGI_API*) PyCObject_Import("gi", "_API");
#endif
+ }
if (PyGI_API == NULL) {
return -1;
}
@@ -128,6 +139,19 @@ pygi_set_property_value (PyGObject *instance,
return PyGI_API->set_property_value(instance, attr_name, value);
}
+static inline GClosure *
+pygi_signal_closure_new (PyGObject *instance,
+ const gchar *sig_name,
+ PyObject *callback,
+ PyObject *extra_args,
+ PyObject *swap_data)
+{
+ if (_pygi_import() < 0) {
+ return NULL;
+ }
+ return PyGI_API->signal_closure_new(instance, sig_name, callback, extra_args, swap_data);
+}
+
static inline PyObject *
pygi_register_foreign_struct (const char* namespace_,
const char* name,
@@ -169,6 +193,16 @@ pygi_set_property_value (PyGObject *instance,
return -1;
}
+static inline GClosure *
+pygi_signal_closure_new (PyGObject *instance,
+ const gchar *sig_name,
+ PyObject *callback,
+ PyObject *extra_args,
+ PyObject *swap_data)
+{
+ return NULL;
+}
+
#endif /* ENABLE_INTROSPECTION */
#endif /* __PYGI_H__ */
diff --git a/gi/repository/Makefile.am b/gi/repository/Makefile.am
index c9138ce9..ece9c4ff 100644
--- a/gi/repository/Makefile.am
+++ b/gi/repository/Makefile.am
@@ -1,8 +1,10 @@
PLATFORM_VERSION = 2.0
-pkgpyexecdir = $(pyexecdir)/gtk-2.0/gi
+pkgpyexecdir = $(pyexecdir)/gi
pygirepositorydir = $(pkgpyexecdir)/repository
pygirepository_PYTHON = \
__init__.py
+
+-include $(top_srcdir)/git.mk
diff --git a/gi/tests/Makefile.am b/gi/tests/Makefile.am
index f8a65dc5..883bc083 100644
--- a/gi/tests/Makefile.am
+++ b/gi/tests/Makefile.am
@@ -20,3 +20,5 @@ check.valgrind:
%.valgrind:
EXEC_NAME="valgrind" TEST_NAMES=$* $(MAKE) check
+
+-include $(top_srcdir)/git.mk
diff --git a/gi/types.py b/gi/types.py
index 0a8768cc..9b250b16 100644
--- a/gi/types.py
+++ b/gi/types.py
@@ -29,10 +29,14 @@ from ._gi import \
InterfaceInfo, \
ObjectInfo, \
StructInfo, \
+ VFuncInfo, \
set_object_has_new_constructor, \
register_interface_info, \
hook_up_vfunc_implementation
+if sys.version_info > (3, 0):
+ def callable(obj):
+ return hasattr(obj, '__call__')
def Function(info):
@@ -45,6 +49,16 @@ def Function(info):
return function
+def NativeVFunc(info, cls):
+
+ def native_vfunc(*args):
+ return info.invoke(*args, **dict(gtype=cls.__gtype__))
+ native_vfunc.__info__ = info
+ native_vfunc.__name__ = info.get_name()
+ native_vfunc.__module__ = info.get_namespace()
+
+ return native_vfunc
+
def Constructor(info):
def constructor(cls, *args):
@@ -93,40 +107,106 @@ class MetaClassHelper(object):
setattr(cls, name, value)
def _setup_vfuncs(cls):
- for base in cls.__bases__:
- if not hasattr(base, '__info__') or \
- not hasattr(base.__info__, 'get_vfuncs'):
+ for vfunc_name, py_vfunc in cls.__dict__.items():
+ if not vfunc_name.startswith("do_") or not callable(py_vfunc):
continue
- for vfunc_info in base.__info__.get_vfuncs():
- vfunc = getattr(cls, 'do_' + vfunc_info.get_name(), None)
- if vfunc is None and isinstance(base.__info__, InterfaceInfo) and \
- not hasattr(cls, vfunc_info.get_name()):
- raise TypeError('Class implementing %s.%s should implement '
- 'the method do_%s()' % (base.__info__.get_namespace(),
- base.__info__.get_name(),
- vfunc_info.get_name()))
- elif vfunc is not None:
- function = vfunc
- if sys.version_info < (3, 0):
- function = vfunc.im_func
-
- if not is_function_in_classes(function, cls.__bases__):
- hook_up_vfunc_implementation(vfunc_info, cls.__gtype__,
- vfunc)
-
-def is_function_in_classes(function, classes):
- for klass in classes:
- if function in klass.__dict__.values():
- return True
- elif is_function_in_classes(function, klass.__bases__):
- return True
- return False
+
+ # If a method name starts with "do_" assume it is a vfunc, and search
+ # in the base classes for a method with the same name to override.
+ # Recursion is not necessary here because getattr() searches all
+ # super class attributes as well.
+ vfunc_info = None
+ for base in cls.__bases__:
+ method = getattr(base, vfunc_name, None)
+ if method is not None and hasattr(method, '__info__') and \
+ isinstance(method.__info__, VFuncInfo):
+ vfunc_info = method.__info__
+ break
+
+ # If we did not find a matching method name in the bases, we might
+ # be overriding an interface virtual method. Since interfaces do not
+ # provide implementations, there will be no method attribute installed
+ # on the object. Instead we have to search through
+ # InterfaceInfo.get_vfuncs(). Note that the infos returned by
+ # get_vfuncs() use the C vfunc name (ie. there is no "do_" prefix).
+ if vfunc_info is None:
+ vfunc_info = find_vfunc_info_in_interface(cls.__bases__, vfunc_name[len("do_"):])
+
+ if vfunc_info is not None:
+ assert vfunc_name == ('do_' + vfunc_info.get_name())
+ # Check to see if there are vfuncs with the same name in the bases.
+ # We have no way of specifying which one we are supposed to override.
+ ambiguous_base = find_vfunc_conflict_in_bases(vfunc_info, cls.__bases__)
+ if ambiguous_base is not None:
+ base_info = vfunc_info.get_container()
+ raise TypeError('Method %s() on class %s.%s is ambiguous '
+ 'with methods in base classes %s.%s and %s.%s' %
+ (vfunc_name,
+ cls.__info__.get_namespace(),
+ cls.__info__.get_name(),
+ base_info.get_namespace(),
+ base_info.get_name(),
+ ambiguous_base.__info__.get_namespace(),
+ ambiguous_base.__info__.get_name()))
+
+ hook_up_vfunc_implementation(vfunc_info, cls.__gtype__,
+ py_vfunc)
+
+ def _setup_native_vfuncs(cls):
+ # Only InterfaceInfo and ObjectInfo have the get_vfuncs() method.
+ # We skip InterfaceInfo because interfaces have no implementations for vfuncs.
+ # Also check if __info__ in __dict__, not hasattr('__info__', ...)
+ # because we do not want to accidentally retrieve __info__ from a base class.
+ class_info = cls.__dict__.get('__info__')
+ if class_info is None or not isinstance(class_info, ObjectInfo):
+ return
+
+ for vfunc_info in class_info.get_vfuncs():
+ name = 'do_%s' % vfunc_info.get_name()
+ value = NativeVFunc(vfunc_info, cls)
+ setattr(cls, name, value)
+
+def find_vfunc_info_in_interface(bases, vfunc_name):
+ for base in bases:
+ # All wrapped interfaces inherit from GInterface.
+ # This can be seen in IntrospectionModule.__getattr__() in module.py.
+ # We do not need to search regular classes here, only wrapped interfaces.
+ # We also skip GInterface, because it is not wrapped and has no __info__ attr.
+ if base is gobject.GInterface or\
+ not issubclass(base, gobject.GInterface) or\
+ not isinstance(base.__info__, InterfaceInfo):
+ continue
+
+ for vfunc in base.__info__.get_vfuncs():
+ if vfunc.get_name() == vfunc_name:
+ return vfunc
+
+ vfunc = find_vfunc_info_in_interface(base.__bases__, vfunc_name)
+ if vfunc is not None:
+ return vfunc
+
+ return None
+
+def find_vfunc_conflict_in_bases(vfunc, bases):
+ for klass in bases:
+ if not hasattr(klass, '__info__') or \
+ not hasattr(klass.__info__, 'get_vfuncs'):
+ continue
+ vfuncs = klass.__info__.get_vfuncs()
+ vfunc_name = vfunc.get_name()
+ for v in vfuncs:
+ if v.get_name() == vfunc_name and v != vfunc:
+ return klass
+
+ aklass = find_vfunc_conflict_in_bases(vfunc, klass.__bases__)
+ if aklass is not None:
+ return aklass
+ return None
class GObjectMeta(gobject.GObjectMeta, MetaClassHelper):
def __init__(cls, name, bases, dict_):
super(GObjectMeta, cls).__init__(name, bases, dict_)
-
is_gi_defined = False
if cls.__module__ == 'gi.repository.' + cls.__info__.get_namespace():
is_gi_defined = True
@@ -140,6 +220,7 @@ class GObjectMeta(gobject.GObjectMeta, MetaClassHelper):
elif is_gi_defined:
cls._setup_methods()
cls._setup_constants()
+ cls._setup_native_vfuncs()
if isinstance(cls.__info__, ObjectInfo):
cls._setup_fields()
@@ -148,6 +229,47 @@ class GObjectMeta(gobject.GObjectMeta, MetaClassHelper):
elif isinstance(cls.__info__, InterfaceInfo):
register_interface_info(cls.__info__.get_g_type())
+ def mro(cls):
+ return mro(cls)
+
+
+def mro(C):
+ """Compute the class precedence list (mro) according to C3
+
+ Based on http://www.python.org/download/releases/2.3/mro/
+ Modified to consider that interfaces don't create the diamond problem
+ """
+ # TODO: If this turns out being too slow, consider using generators
+ bases = []
+ bases_of_subclasses = [[C]]
+
+ if C.__bases__:
+ bases_of_subclasses += list(map(mro, C.__bases__)) + [list(C.__bases__)]
+
+ while bases_of_subclasses:
+ for subclass_bases in bases_of_subclasses:
+ candidate = subclass_bases[0]
+ not_head = [s for s in bases_of_subclasses if candidate in s[1:]]
+ if not_head and gobject.GInterface not in candidate.__bases__:
+ candidate = None # conflict, reject candidate
+ else:
+ break
+
+ if candidate is None:
+ raise TypeError('Cannot create a consistent method resolution '
+ 'order (MRO)')
+
+ bases.append(candidate)
+
+ for subclass_bases in bases_of_subclasses[:]: # remove candidate
+ if subclass_bases and subclass_bases[0] == candidate:
+ del subclass_bases[0]
+ if not subclass_bases:
+ bases_of_subclasses.remove(subclass_bases)
+
+ return bases
+
+
class StructMeta(type, MetaClassHelper):
def __init__(cls, name, bases, dict_):
@@ -162,24 +284,9 @@ class StructMeta(type, MetaClassHelper):
cls._setup_methods()
cls._setup_constructors()
-
-def override(type_):
- g_type = type_.__info__.get_g_type()
- assert g_type != gobject.TYPE_NONE
- if g_type != gobject.TYPE_INVALID:
- g_type.pytype = type_
- return type_
-
-class Enum(int):
- __info__ = None
- def __init__(self, value):
- int.__init__(value)
-
- def __repr__(self):
- value_name = str(self)
- for value_info in self.__info__.get_values():
- if self == value_info.get_value():
- value_name = value_info.get_name().upper()
- return "<enum %s of type %s.%s>" % (value_name,
- self.__info__.get_namespace(),
- self.__info__.get_name())
+ for method_info in cls.__info__.get_methods():
+ if method_info.is_constructor() and \
+ method_info.get_name() == 'new' and \
+ not method_info.get_arguments():
+ cls.__new__ = staticmethod(Constructor(method_info))
+ break
diff --git a/gio/gfile.override b/gio/gfile.override
index b50130cc..13019638 100644
--- a/gio/gfile.override
+++ b/gio/gfile.override
@@ -840,7 +840,7 @@ _wrap_g_file_set_attribute(PyGObject *self, PyObject *args, PyObject *kwargs)
static char *kwlist[] = { "attribute", "type", "value_p",
"flags", "cancellable", NULL };
GFileQueryInfoFlags flags = G_FILE_QUERY_INFO_NONE;
- int ret;
+ int ret = 0;
GCancellable *cancellable = NULL;
GError *error = NULL;
char *attribute;
@@ -883,7 +883,7 @@ _wrap_g_file_set_attribute(PyGObject *self, PyObject *args, PyObject *kwargs)
&error);
}
break;
-
+
case G_FILE_ATTRIBUTE_TYPE_BYTE_STRING:
{
char* s;
@@ -902,7 +902,6 @@ _wrap_g_file_set_attribute(PyGObject *self, PyObject *args, PyObject *kwargs)
&error);
}
break;
-
case G_FILE_ATTRIBUTE_TYPE_STRINGV:
{
diff --git a/git.mk b/git.mk
new file mode 100644
index 00000000..abd6c0a2
--- /dev/null
+++ b/git.mk
@@ -0,0 +1,200 @@
+# git.mk
+#
+# Copyright 2009, Red Hat, Inc.
+# Written by Behdad Esfahbod
+#
+# Copying and distribution of this file, with or without modification,
+# are permitted in any medium without royalty provided the copyright
+# notice and this notice are preserved.
+#
+# The canonical source for this file is pango/git.mk, or whereever the
+# header of pango/git.mk suggests in the future.
+#
+# To use in your project, import this file in your git repo's toplevel,
+# then do "make -f git.mk". This modifies all Makefile.am files in
+# your project to include git.mk.
+#
+# This enables automatic .gitignore generation. If you need to ignore
+# more files, add them to the GITIGNOREFILES variable in your Makefile.am.
+# But think twice before doing that. If a file has to be in .gitignore,
+# chances are very high that it's a generated file and should be in one
+# of MOSTLYCLEANFILES, CLEANFILES, DISTCLEANFILES, or MAINTAINERCLEANFILES.
+#
+# The only case that you need to manually add a file to GITIGNOREFILES is
+# when remove files in one of mostlyclean-local, clean-local, distclean-local,
+# or maintainer-clean-local.
+#
+# Note that for files like editor backup, etc, there are better places to
+# ignore them. See "man gitignore".
+#
+# If "make maintainer-clean" removes the files but they are not recognized
+# by this script (that is, if "git status" shows untracked files still), send
+# me the output of "git status" as well as your Makefile.am and Makefile for
+# the directories involved.
+#
+# For a list of toplevel files that should be in MAINTAINERCLEANFILES, see
+# pango/Makefile.am.
+#
+# Don't EXTRA_DIST this file. It is supposed to only live in git clones,
+# not tarballs. It serves no useful purpose in tarballs and clutters the
+# build dir.
+#
+# This file knows how to handle autoconf, automake, libtool, gtk-doc,
+# gnome-doc-utils, mallard, intltool, gsettings.
+#
+#
+# KNOWN ISSUES:
+#
+# - Recursive configure doesn't work as $(top_srcdir)/git.mk inside the
+# submodule doesn't find us. If you have configure.{in,ac} files in
+# subdirs, add a proxy git.mk file in those dirs that simply does:
+# "include $(top_srcdir)/../git.mk". Add more ..'s to your taste.
+# And add those files to git. See vte/gnome-pty-helper/git.mk for
+# example.
+#
+# ChangeLog
+#
+# - 2010-12-06 Add support for Mallard docs
+# - 2010-12-06 Start this change log
+
+git-all: git-mk-install
+
+git-mk-install:
+ @echo Installing git makefile
+ @any_failed=; find $(top_srcdir) -name Makefile.am | while read x; do \
+ if grep 'include .*/git.mk' $$x >/dev/null; then \
+ echo $$x already includes git.mk; \
+ else \
+ failed=; \
+ echo "Updating $$x"; \
+ { cat $$x; \
+ echo ''; \
+ echo '-include $$(top_srcdir)/git.mk'; \
+ } > $$x.tmp || failed=1; \
+ if test x$$failed = x; then \
+ mv $$x.tmp $$x || failed=1; \
+ fi; \
+ if test x$$failed = x; then : else \
+ echo Failed updating $$x; >&2 \
+ any_failed=1; \
+ fi; \
+ fi; done; test -z "$$any_failed"
+
+.PHONY: git-all git-mk-install
+
+
+### .gitignore generation
+
+$(srcdir)/.gitignore: Makefile.am $(top_srcdir)/git.mk
+ $(AM_V_GEN) \
+ { \
+ if test "x$(DOC_MODULE)" = x -o "x$(DOC_MAIN_SGML_FILE)" = x; then :; else \
+ for x in \
+ $(DOC_MODULE)-decl-list.txt \
+ $(DOC_MODULE)-decl.txt \
+ tmpl/$(DOC_MODULE)-unused.sgml \
+ "tmpl/*.bak" \
+ xml html \
+ ; do echo /$$x; done; \
+ fi; \
+ if test "x$(DOC_MODULE)$(DOC_ID)" = x -o "x$(DOC_LINGUAS)" = x; then :; else \
+ for x in \
+ $(_DOC_C_DOCS) \
+ $(_DOC_LC_DOCS) \
+ $(_DOC_OMF_ALL) \
+ $(_DOC_DSK_ALL) \
+ $(_DOC_HTML_ALL) \
+ $(_DOC_MOFILES) \
+ $(_DOC_POFILES) \
+ $(DOC_H_FILE) \
+ "*/.xml2po.mo" \
+ "*/*.omf.out" \
+ ; do echo /$$x; done; \
+ fi; \
+ if test "x$(gsettings_SCHEMAS)" = x; then :; else \
+ for x in \
+ $(gsettings_SCHEMAS:.xml=.valid) \
+ $(gsettings__enum_file) \
+ ; do echo /$$x; done; \
+ fi; \
+ if test -f $(srcdir)/po/Makefile.in.in; then \
+ for x in \
+ po/Makefile.in.in \
+ po/Makefile.in \
+ po/Makefile \
+ po/POTFILES \
+ po/stamp-it \
+ po/.intltool-merge-cache \
+ "po/*.gmo" \
+ "po/*.mo" \
+ po/$(GETTEXT_PACKAGE).pot \
+ intltool-extract.in \
+ intltool-merge.in \
+ intltool-update.in \
+ ; do echo /$$x; done; \
+ fi; \
+ if test -f $(srcdir)/configure; then \
+ for x in \
+ autom4te.cache \
+ configure \
+ config.h \
+ stamp-h1 \
+ libtool \
+ config.lt \
+ ; do echo /$$x; done; \
+ fi; \
+ for x in \
+ .gitignore \
+ $(GITIGNOREFILES) \
+ $(CLEANFILES) \
+ $(PROGRAMS) \
+ $(check_PROGRAMS) \
+ $(EXTRA_PROGRAMS) \
+ $(LTLIBRARIES) \
+ so_locations \
+ .libs _libs \
+ $(MOSTLYCLEANFILES) \
+ "*.$(OBJEXT)" \
+ "*.lo" \
+ $(DISTCLEANFILES) \
+ $(am__CONFIG_DISTCLEAN_FILES) \
+ $(CONFIG_CLEAN_FILES) \
+ TAGS ID GTAGS GRTAGS GSYMS GPATH tags \
+ "*.tab.c" \
+ $(MAINTAINERCLEANFILES) \
+ $(BUILT_SOURCES) \
+ $(DEPDIR) \
+ Makefile \
+ Makefile.in \
+ "*.orig" \
+ "*.rej" \
+ "*.bak" \
+ "*~" \
+ ".*.sw[nop]" \
+ ".dirstamp" \
+ ; do echo /$$x; done; \
+ } | \
+ sed "s@^/`echo "$(srcdir)" | sed 's/\(.\)/[\1]/g'`/@/@" | \
+ sed 's@/[.]/@/@g' | \
+ LC_ALL=C sort | uniq > $@.tmp && \
+ mv $@.tmp $@;
+
+all: $(srcdir)/.gitignore gitignore-recurse-maybe
+gitignore-recurse-maybe:
+ @if test "x$(SUBDIRS)" = "x$(DIST_SUBDIRS)"; then :; else \
+ $(MAKE) $(AM_MAKEFLAGS) gitignore-recurse; \
+ fi;
+gitignore-recurse:
+ @for subdir in $(DIST_SUBDIRS); do \
+ case " $(SUBDIRS) " in \
+ *" $$subdir "*) :;; \
+ *) test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) .gitignore gitignore-recurse || echo "Skipping $$subdir");; \
+ esac; \
+ done
+gitignore: $(srcdir)/.gitignore gitignore-recurse
+
+maintainer-clean: gitignore-clean
+gitignore-clean:
+ -rm -f $(srcdir)/.gitignore
+
+.PHONY: gitignore-clean gitignore gitignore-recurse gitignore-recurse-maybe
diff --git a/glib/Makefile.am b/glib/Makefile.am
index f1644a12..e210318b 100644
--- a/glib/Makefile.am
+++ b/glib/Makefile.am
@@ -6,7 +6,7 @@ pkginclude_HEADERS = pyglib.h
lib_LTLIBRARIES = libpyglib-2.0-@PYTHON_BASENAME@.la
-pkgpyexecdir = $(pyexecdir)/gtk-2.0
+pkgpyexecdir = $(pyexecdir)
pyglibdir = $(pkgpyexecdir)/glib
pyglib_PYTHON = \
@@ -57,3 +57,5 @@ clean-local:
rm -f $(pyglib_LTLIBRARIES:.la=.so)
.la.so:
$(LN_S) .libs/$@ $@ || true
+
+-include $(top_srcdir)/git.mk
diff --git a/glib/glibmodule.c b/glib/glibmodule.c
index 29a47137..963e5ea9 100644
--- a/glib/glibmodule.c
+++ b/glib/glibmodule.c
@@ -787,7 +787,10 @@ static struct _PyGLib_Functions pyglib_api = {
FALSE, /* threads_enabled */
NULL, /* gerror_exception */
NULL, /* block_threads */
- NULL /* unblock_threads */
+ NULL, /* unblock_threads */
+ pyg_main_context_new,
+ pyg_option_context_new,
+ pyg_option_group_new,
};
static void
diff --git a/glib/pyglib-private.h b/glib/pyglib-private.h
index 8b033e1e..183184f3 100644
--- a/glib/pyglib-private.h
+++ b/glib/pyglib-private.h
@@ -34,6 +34,9 @@ struct _PyGLib_Functions {
PyObject *gerror_exception;
PyGLibThreadBlockFunc block_threads;
PyGLibThreadBlockFunc unblock_threads;
+ PyObject* (*main_context_new)(GMainContext *context);
+ PyObject* (*option_context_new)(GOptionContext *context);
+ PyObject* (*option_group_new)(GOptionGroup *group);
};
gboolean _pyglib_handler_marshal(gpointer user_data);
diff --git a/glib/pyglib-python-compat.h b/glib/pyglib-python-compat.h
index 5d7516e9..8c1dd51a 100644
--- a/glib/pyglib-python-compat.h
+++ b/glib/pyglib-python-compat.h
@@ -112,7 +112,12 @@ static int _pyglib_init_##modname(PyObject *module)
/* Compilation on Python 2.x */
#if PY_VERSION_HEX < 0x03000000
+#define PYGLIB_MODULE_ERROR_RETURN
+
#define RO READONLY
+
+#define PYGLIB_PyBaseString_Check(ob) (PyString_Check(ob) || PyUnicode_Check(ob))
+
#define PYGLIB_PyUnicode_Check PyString_Check
#define PYGLIB_PyUnicode_AsString PyString_AsString
#define PYGLIB_PyUnicode_AsStringAndSize PyString_AsStringAndSize
@@ -173,6 +178,7 @@ PyTypeObject symbol = { \
#else
+#define PYGLIB_MODULE_ERROR_RETURN 0
#define PYGLIB_MODULE_START(symbol, modname) \
static struct PyModuleDef _##symbol##module = { \
@@ -206,6 +212,8 @@ PyTypeObject symbol = { \
return; \
PyDict_SetItemString(d, name, (PyObject *)&type);
+#define PYGLIB_PyBaseString_Check PyUnicode_Check
+
#define PYGLIB_PyUnicode_Check PyUnicode_Check
#define PYGLIB_PyUnicode_AsString _PyUnicode_AsString
#define PYGLIB_PyUnicode_AsStringAndSize(obj, buf, size) \
diff --git a/glib/pyglib.c b/glib/pyglib.c
index c85a628e..1e024c2f 100644
--- a/glib/pyglib.c
+++ b/glib/pyglib.c
@@ -35,15 +35,6 @@ static struct _PyGLib_Functions *_PyGLib_API;
static int pyglib_thread_state_tls_key;
static PyObject *exception_table = NULL;
-static PyTypeObject *_PyGMainContext_Type;
-#define PyGMainContext_Type (*_PyGMainContext_Type)
-
-static PyTypeObject *_PyGOptionGroup_Type;
-#define PyGOptionGroup_Type (*_PyGOptionGroup_Type)
-
-static PyTypeObject *_PyGOptionContext_Type;
-#define PyGOptionContext_Type (*_PyGOptionContext_Type)
-
void
pyglib_init(void)
{
@@ -79,10 +70,6 @@ pyglib_init(void)
Py_DECREF(glib);
return;
}
-
- _PyGMainContext_Type = (PyTypeObject*)PyObject_GetAttrString(glib, "MainContext");
- _PyGOptionGroup_Type = (PyTypeObject*)PyObject_GetAttrString(glib, "OptionGroup");
- _PyGOptionContext_Type = (PyTypeObject*)PyObject_GetAttrString(glib, "OptionContext");
}
void
@@ -107,7 +94,11 @@ pyglib_gil_state_ensure(void)
if (!_PyGLib_API->threads_enabled)
return PyGILState_LOCKED;
+#ifdef DISABLE_THREADING
+ return PyGILState_LOCKED;
+#else
return PyGILState_Ensure();
+#endif
}
void
@@ -118,7 +109,9 @@ pyglib_gil_state_release(PyGILState_STATE state)
if (!_PyGLib_API->threads_enabled)
return;
+#ifndef DISABLE_THREADING
PyGILState_Release(state);
+#endif
}
/**
@@ -184,13 +177,19 @@ _pyglib_notify_on_enabling_threads(PyGLibThreadsEnabledFunc callback)
int
pyglib_gil_state_ensure_py23 (void)
{
+#ifdef DISABLE_THREADING
+ return 0;
+#else
return PyGILState_Ensure();
+#endif
}
void
pyglib_gil_state_release_py23 (int flag)
{
+#ifndef DISABLE_THREADING
PyGILState_Release(flag);
+#endif
}
/**
@@ -359,7 +358,7 @@ pyglib_gerror_exception_check(GError **error)
}
g_set_error(error, g_quark_from_string(PYGLIB_PyUnicode_AsString(py_domain)),
- PYGLIB_PyLong_AsLong(py_code), PYGLIB_PyUnicode_AsString(py_message));
+ PYGLIB_PyLong_AsLong(py_code), "%s", PYGLIB_PyUnicode_AsString(py_message));
Py_DECREF(py_message);
Py_DECREF(py_code);
@@ -414,15 +413,7 @@ pyglib_register_exception_for_domain(gchar *name,
PyObject *
pyglib_main_context_new(GMainContext *context)
{
- PyGMainContext *self;
-
- self = (PyGMainContext *)PyObject_NEW(PyGMainContext,
- &PyGMainContext_Type);
- if (self == NULL)
- return NULL;
-
- self->context = g_main_context_ref(context);
- return (PyObject *)self;
+ return _PyGLib_API->main_context_new(context);
}
/**
@@ -472,18 +463,7 @@ pyglib_option_group_transfer_group(PyObject *obj)
PyObject *
pyglib_option_group_new (GOptionGroup *group)
{
- PyGOptionGroup *self;
-
- self = (PyGOptionGroup *)PyObject_NEW(PyGOptionGroup,
- &PyGOptionGroup_Type);
- if (self == NULL)
- return NULL;
-
- self->group = group;
- self->other_owner = TRUE;
- self->is_in_context = FALSE;
-
- return (PyObject *)self;
+ return _PyGLib_API->option_group_new(group);
}
/**
@@ -495,17 +475,7 @@ pyglib_option_group_new (GOptionGroup *group)
PyObject *
pyglib_option_context_new (GOptionContext *context)
{
- PyGOptionContext *self;
-
- self = (PyGOptionContext *)PyObject_NEW(PyGOptionContext,
- &PyGOptionContext_Type);
- if (self == NULL)
- return NULL;
-
- self->context = context;
- self->main_group = NULL;
-
- return (PyObject *)self;
+ return _PyGLib_API->option_context_new(context);
}
/**
diff --git a/glib/pygmaincontext.c b/glib/pygmaincontext.c
index 43ca84e2..cfb7ddd0 100644
--- a/glib/pygmaincontext.c
+++ b/glib/pygmaincontext.c
@@ -33,6 +33,28 @@
PYGLIB_DEFINE_TYPE("glib.MainContext", PyGMainContext_Type, PyGMainContext)
+/**
+ * pyg_main_context_new:
+ * @context: a GMainContext.
+ *
+ * Creates a wrapper for a GMainContext.
+ *
+ * Returns: the GMainContext wrapper.
+ */
+PyObject *
+pyg_main_context_new(GMainContext *context)
+{
+ PyGMainContext *self;
+
+ self = (PyGMainContext *)PyObject_NEW(PyGMainContext, &PyGMainContext_Type);
+ if (self == NULL)
+ return NULL;
+
+ self->context = g_main_context_ref(context);
+
+ return (PyObject *)self;
+}
+
static int
pyg_main_context_init(PyGMainContext *self)
{
diff --git a/glib/pygmaincontext.h b/glib/pygmaincontext.h
index 038cb376..4ffa3c90 100644
--- a/glib/pygmaincontext.h
+++ b/glib/pygmaincontext.h
@@ -32,7 +32,7 @@ typedef struct {
extern PyTypeObject PyGMainContext_Type;
-PyObject * pyglib_main_context_new(GMainContext *context);
+PyObject* pyg_main_context_new(GMainContext *context);
void pyglib_maincontext_register_types(PyObject *d);
diff --git a/glib/pygmainloop.c b/glib/pygmainloop.c
index de749711..219b6a38 100644
--- a/glib/pygmainloop.c
+++ b/glib/pygmainloop.c
@@ -158,8 +158,9 @@ pyg_signal_watch_check(GSource *source)
PySignalWatchSource *real_source = (PySignalWatchSource *)source;
GPollFD *poll_fd = &real_source->fd;
unsigned char dummy;
+ gssize ret;
if (poll_fd->revents & G_IO_IN)
- read(poll_fd->fd, &dummy, 1);
+ ret = read(poll_fd->fd, &dummy, 1);
#endif
state = pyglib_gil_state_ensure();
@@ -301,7 +302,7 @@ pyg_main_loop_richcompare(PyObject *self, PyObject *other, int op)
static PyObject *
_wrap_g_main_loop_get_context (PyGMainLoop *loop)
{
- return pyglib_main_context_new(g_main_loop_get_context(loop->loop));
+ return pyg_main_context_new(g_main_loop_get_context(loop->loop));
}
static PyObject *
diff --git a/glib/pygoptioncontext.c b/glib/pygoptioncontext.c
index 93d9b242..444625ca 100644
--- a/glib/pygoptioncontext.c
+++ b/glib/pygoptioncontext.c
@@ -30,6 +30,27 @@
PYGLIB_DEFINE_TYPE("glib.OptionContext", PyGOptionContext_Type, PyGOptionContext)
+/**
+ * pyg_option_context_new:
+ * @context: a GOptionContext
+ *
+ * Returns: A new GOptionContext wrapper.
+ */
+PyObject *
+pyg_option_context_new (GOptionContext *context)
+{
+ PyGOptionContext *self;
+
+ self = (PyGOptionContext *)PyObject_NEW(PyGOptionContext, &PyGOptionContext_Type);
+ if (self == NULL)
+ return NULL;
+
+ self->context = context;
+ self->main_group = NULL;
+
+ return (PyObject *)self;
+}
+
static int
pyg_option_context_init(PyGOptionContext *self,
PyObject *args,
diff --git a/glib/pygoptioncontext.h b/glib/pygoptioncontext.h
index 85d0a478..efe5ffa1 100644
--- a/glib/pygoptioncontext.h
+++ b/glib/pygoptioncontext.h
@@ -32,6 +32,8 @@ typedef struct {
GOptionContext *context;
} PyGOptionContext;
+PyObject* pyg_option_context_new(GOptionContext *context);
+
void pyglib_option_context_register_types(PyObject *d);
#endif /* __PYG_OPTIONCONTEXT_H__ */
diff --git a/glib/pygoptiongroup.c b/glib/pygoptiongroup.c
index 93458697..2a693549 100644
--- a/glib/pygoptiongroup.c
+++ b/glib/pygoptiongroup.c
@@ -30,6 +30,32 @@
PYGLIB_DEFINE_TYPE("glib.OptionGroup", PyGOptionGroup_Type, PyGOptionGroup)
+/**
+ * pyg_option_group_new:
+ * @group: a GOptionGroup
+ *
+ * The returned GOptionGroup can't be used to set any hooks, translation domains
+ * or add entries. It's only intend is, to use for GOptionContext.add_group().
+ *
+ * Returns: the GOptionGroup wrapper.
+ */
+PyObject *
+pyg_option_group_new (GOptionGroup *group)
+{
+ PyGOptionGroup *self;
+
+ self = (PyGOptionGroup *)PyObject_NEW(PyGOptionGroup,
+ &PyGOptionGroup_Type);
+ if (self == NULL)
+ return NULL;
+
+ self->group = group;
+ self->other_owner = TRUE;
+ self->is_in_context = FALSE;
+
+ return (PyObject *)self;
+}
+
static gboolean
check_if_owned(PyGOptionGroup *self)
{
diff --git a/glib/pygoptiongroup.h b/glib/pygoptiongroup.h
index cba6a79c..872b9c68 100644
--- a/glib/pygoptiongroup.h
+++ b/glib/pygoptiongroup.h
@@ -33,6 +33,8 @@ typedef struct {
GOptionGroup.destroy() */
} PyGOptionGroup;
+PyObject* pyg_option_group_new(GOptionGroup *group);
+
void pyglib_option_group_register_types(PyObject *d);
#endif /* __PYG_OPTIONGROUP_H__ */
diff --git a/glib/pygsource.c b/glib/pygsource.c
index 3587c388..684711e8 100644
--- a/glib/pygsource.c
+++ b/glib/pygsource.c
@@ -182,7 +182,7 @@ pyg_source_get_context(PyGSource *self)
context = g_source_get_context(self->source);
if (context) {
- return pyglib_main_context_new(context);
+ return pyg_main_context_new(context);
} else {
Py_INCREF(Py_None);
return Py_None;
diff --git a/gobject/.gitignore b/gobject/.gitignore
deleted file mode 100644
index 5efe4c79..00000000
--- a/gobject/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-# Autogenerated with 'generate-constants.c'.
-generate-constants
-constants.py
diff --git a/gobject/Makefile.am b/gobject/Makefile.am
index f2efde11..7208329d 100644
--- a/gobject/Makefile.am
+++ b/gobject/Makefile.am
@@ -4,7 +4,7 @@ PLATFORM_VERSION = 2.0
pkgincludedir = $(includedir)/pygtk-$(PLATFORM_VERSION)
pkginclude_HEADERS = pygobject.h
-pkgpyexecdir = $(pyexecdir)/gtk-2.0
+pkgpyexecdir = $(pyexecdir)
# gobject python scripts
pygobjectdir = $(pkgpyexecdir)/gobject
@@ -71,3 +71,5 @@ clean-local:
rm -f $(pygobject_LTLIBRARIES:.la=.so)
.la.so:
$(LN_S) .libs/$@ $@ || true
+
+-include $(top_srcdir)/git.mk
diff --git a/gobject/__init__.py b/gobject/__init__.py
index 31e6bb0e..1858d188 100644
--- a/gobject/__init__.py
+++ b/gobject/__init__.py
@@ -106,9 +106,9 @@ class GObjectMeta(type):
if '__gtype__' in namespace:
return
- if not ('__gproperties__' in namespace or
- '__gsignals__' in namespace or
- '__gtype_name__' in namespace):
+ # Do not register a new GType for the overrides, as this would sort of
+ # defeat the purpose of overrides...
+ if cls.__module__.startswith('gi.overrides.'):
return
type_register(cls, namespace.get('__gtype_name__'))
diff --git a/gobject/gobjectmodule.c b/gobject/gobjectmodule.c
index f830251a..2a84606e 100644
--- a/gobject/gobjectmodule.c
+++ b/gobject/gobjectmodule.c
@@ -1003,18 +1003,18 @@ get_type_name_for_class(PyTypeObject *class)
}
-static GStaticPrivate pygobject_contruction_wrapper = G_STATIC_PRIVATE_INIT;
+static GStaticPrivate pygobject_construction_wrapper = G_STATIC_PRIVATE_INIT;
static inline void
pygobject_init_wrapper_set(PyObject *wrapper)
{
- g_static_private_set(&pygobject_contruction_wrapper, wrapper, NULL);
+ g_static_private_set(&pygobject_construction_wrapper, wrapper, NULL);
}
static inline PyObject *
pygobject_init_wrapper_get(void)
{
- return (PyObject *) g_static_private_get(&pygobject_contruction_wrapper);
+ return (PyObject *) g_static_private_get(&pygobject_construction_wrapper);
}
static void
@@ -1048,6 +1048,7 @@ pygobject__g_instance_init(GTypeInstance *instance,
kwargs = PyDict_New();
if (Py_TYPE(wrapper)->tp_init(wrapper, args, kwargs))
PyErr_Print();
+ Py_DECREF(wrapper);
Py_DECREF(args);
Py_DECREF(kwargs);
pyglib_gil_state_release(state);
@@ -1175,16 +1176,21 @@ pyg_type_register(PyTypeObject *class, const char *type_name)
/* create new typecode */
instance_type = g_type_register_static(parent_type, new_type_name,
&type_info, 0);
- if (type_name == NULL)
- g_free(new_type_name);
if (instance_type == 0) {
PyErr_Format(PyExc_RuntimeError,
"could not create new GType: %s (subclass of %s)",
new_type_name,
g_type_name(parent_type));
+
+ if (type_name == NULL)
+ g_free(new_type_name);
+
return -1;
}
+ if (type_name == NULL)
+ g_free(new_type_name);
+
/* store pointer to the class with the GType */
Py_INCREF(class);
g_type_set_qdata(instance_type, g_quark_from_string("PyGObject::class"),
@@ -1689,7 +1695,7 @@ pyg_object_new (PyGObject *self, PyObject *args, PyObject *kwargs)
GType type;
GObject *obj = NULL;
GObjectClass *class;
- int n_params = 0, i;
+ guint n_params = 0, i;
GParameter *params = NULL;
if (!PyArg_ParseTuple (args, "O:gobject.new", &pytype)) {
@@ -1711,37 +1717,8 @@ pyg_object_new (PyGObject *self, PyObject *args, PyObject *kwargs)
return NULL;
}
- if (kwargs) {
- Py_ssize_t pos = 0;
- PyObject *key;
- PyObject *value;
-
- params = g_new0(GParameter, PyDict_Size(kwargs));
- while (PyDict_Next (kwargs, &pos, &key, &value)) {
- GParamSpec *pspec;
- const gchar *key_str = PYGLIB_PyUnicode_AsString (key);
-
- pspec = g_object_class_find_property (class, key_str);
- if (!pspec) {
- PyErr_Format(PyExc_TypeError,
- "gobject `%s' doesn't support property `%s'",
- g_type_name(type), key_str);
- goto cleanup;
- }
- g_value_init(&params[n_params].value,
- G_PARAM_SPEC_VALUE_TYPE(pspec));
- if (pyg_param_gvalue_from_pyobject(&params[n_params].value,
- value, pspec) < 0) {
- PyErr_Format(PyExc_TypeError,
- "could not convert value for property `%s' from %s to %s",
- key_str, Py_TYPE(value)->tp_name,
- g_type_name(G_PARAM_SPEC_VALUE_TYPE(pspec)));
- goto cleanup;
- }
- params[n_params].name = g_strdup(key_str);
- n_params++;
- }
- }
+ if (!pygobject_prepare_construct_properties (class, kwargs, &n_params, &params))
+ goto cleanup;
obj = g_object_newv(type, n_params, params);
if (!obj)
@@ -2501,8 +2478,8 @@ struct _PyGObject_Functions pygobject_api_functions = {
pyg_type_register_custom_callback,
pyg_gerror_exception_check,
- pyglib_option_group_new
-
+ pyglib_option_group_new,
+ pyg_type_from_object_strict
};
/* for addon libraries ... */
diff --git a/gobject/propertyhelper.py b/gobject/propertyhelper.py
index 2f77a4d6..9643c82b 100644
--- a/gobject/propertyhelper.py
+++ b/gobject/propertyhelper.py
@@ -144,7 +144,6 @@ class property(object):
self.name = None
- self._values = {}
self._exc = None
def __repr__(self):
@@ -176,30 +175,30 @@ class property(object):
self._exc = None
raise exc
- def _type_from_python(self, type):
- if type == _long:
+ def _type_from_python(self, type_):
+ if type_ == _long:
return TYPE_LONG
- elif type == int:
+ elif type_ == int:
return TYPE_INT
- elif type == bool:
+ elif type_ == bool:
return TYPE_BOOLEAN
- elif type == float:
+ elif type_ == float:
return TYPE_DOUBLE
- elif type == str:
+ elif type_ == str:
return TYPE_STRING
- elif type == object:
+ elif type_ == object:
return TYPE_PYOBJECT
- elif type == _gobject.GObject:
- return TYPE_OBJECT
- elif type in [TYPE_NONE, TYPE_INTERFACE, TYPE_CHAR, TYPE_UCHAR,
+ elif isinstance(type_, type) and issubclass(type_, _gobject.GObject):
+ return type_.__gtype__
+ elif type_ in [TYPE_NONE, TYPE_INTERFACE, TYPE_CHAR, TYPE_UCHAR,
TYPE_INT, TYPE_UINT, TYPE_BOOLEAN, TYPE_LONG,
TYPE_ULONG, TYPE_INT64, TYPE_UINT64, TYPE_ENUM,
TYPE_FLAGS, TYPE_FLOAT, TYPE_DOUBLE, TYPE_POINTER,
TYPE_BOXED, TYPE_PARAM, TYPE_OBJECT, TYPE_STRING,
TYPE_PYOBJECT]:
- return type
+ return type_
else:
- raise TypeError("Unsupported type: %r" % (type,))
+ raise TypeError("Unsupported type: %r" % (type_,))
def _get_default(self, default):
ptype = self.type
@@ -270,10 +269,10 @@ class property(object):
#
def _default_setter(self, instance, value):
- self._values[instance] = value
+ setattr(instance, '_property_helper_'+self.name, value)
def _default_getter(self, instance):
- return self._values.get(instance, self.default)
+ return getattr(instance, '_property_helper_'+self.name, self.default)
def _readonly_setter(self, instance, value):
self._exc = TypeError("%s property of %s is read-only" % (
@@ -296,7 +295,7 @@ class property(object):
args = (self.default,)
elif ptype == TYPE_PYOBJECT:
args = ()
- elif ptype == TYPE_OBJECT:
+ elif ptype.is_a(TYPE_OBJECT):
args = ()
else:
raise NotImplementedError(ptype)
diff --git a/gobject/pygflags.c b/gobject/pygflags.c
index 936f314e..8c00f150 100644
--- a/gobject/pygflags.c
+++ b/gobject/pygflags.c
@@ -161,7 +161,7 @@ pyg_flags_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
return NULL;
}
- if (!PyDict_Check(values) || PyDict_Size(values) != eclass->n_values) {
+ if (!PyDict_Check(values)) {
PyErr_SetString(PyExc_TypeError, "__flags_values__ badly formed");
Py_DECREF(values);
g_type_class_unref(eclass);
@@ -172,13 +172,18 @@ pyg_flags_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
pyint = PYGLIB_PyLong_FromLong(value);
ret = PyDict_GetItem(values, pyint);
+ if (!ret) {
+ PyErr_Clear();
+
+ ret = pyg_flags_val_new((PyObject *)type, gtype, pyint);
+ g_assert(ret != NULL);
+ } else {
+ Py_INCREF(ret);
+ }
+
Py_DECREF(pyint);
Py_DECREF(values);
- if (ret)
- Py_INCREF(ret);
- else
- PyErr_Format(PyExc_ValueError, "invalid flag value: %ld", value);
return ret;
}
diff --git a/gobject/pygobject-private.h b/gobject/pygobject-private.h
index 26cb5f24..6f3a25ef 100644
--- a/gobject/pygobject-private.h
+++ b/gobject/pygobject-private.h
@@ -32,12 +32,18 @@ extern struct _PyGObject_Functions pygobject_api_functions;
#define pyg_threads_enabled (pygobject_api_functions.threads_enabled)
+#ifdef DISABLE_THREADING
+#define pyg_gil_state_ensure() 0
+#define pyg_gil_state_release(state) G_STMT_START { \
+ } G_STMT_END
+#else
#define pyg_gil_state_ensure() (pygobject_api_functions.threads_enabled? (PyGILState_Ensure()) : 0)
#define pyg_gil_state_release(state) G_STMT_START { \
if (pygobject_api_functions.threads_enabled) \
PyGILState_Release(state); \
} G_STMT_END
+#endif
#define pyg_begin_allow_threads \
G_STMT_START { \
@@ -98,6 +104,7 @@ gboolean pyg_gerror_exception_check(GError **error);
extern PyTypeObject PyGTypeWrapper_Type;
PyObject *pyg_type_wrapper_new (GType type);
+GType pyg_type_from_object_strict (PyObject *obj, gboolean strict);
GType pyg_type_from_object (PyObject *obj);
gint pyg_enum_get_value (GType enum_type, PyObject *obj, gint *val);
@@ -149,6 +156,7 @@ void pygobject_register_class (PyObject *dict,
void pygobject_register_wrapper (PyObject *self);
PyObject * pygobject_new (GObject *obj);
PyObject * pygobject_new_full (GObject *obj, gboolean sink, gpointer g_class);
+PyObject * pygobject_new_sunk (GObject *obj);
void pygobject_sink (GObject *obj);
PyTypeObject *pygobject_lookup_class (GType gtype);
void pygobject_watch_closure (PyObject *self, GClosure *closure);
@@ -225,5 +233,10 @@ pyg_object_peek_inst_data(GObject *obj)
g_object_get_qdata(obj, pygobject_instance_data_key));
}
+gboolean pygobject_prepare_construct_properties (GObjectClass *class,
+ PyObject *kwargs,
+ guint *n_params,
+ GParameter **params);
+
#endif
diff --git a/gobject/pygobject.c b/gobject/pygobject.c
index eee39634..e6dfbc4b 100644
--- a/gobject/pygobject.c
+++ b/gobject/pygobject.c
@@ -50,6 +50,7 @@ GQuark pygobject_class_init_key;
GQuark pygobject_wrapper_key;
GQuark pygobject_has_updated_constructor_key;
GQuark pygobject_instance_data_key;
+GQuark pygobject_ref_sunk_key;
/* -------------- class <-> wrapper manipulation --------------- */
@@ -135,6 +136,12 @@ PyTypeObject *PyGObject_MetaType = NULL;
void
pygobject_sink(GObject *obj)
{
+ gboolean sunk = FALSE;
+
+ /* We use a gobject data key to avoid running the sink funcs more than once. */
+ if (g_object_get_qdata (obj, pygobject_ref_sunk_key))
+ return;
+
if (sink_funcs) {
gint i;
@@ -142,18 +149,17 @@ pygobject_sink(GObject *obj)
if (g_type_is_a(G_OBJECT_TYPE(obj),
g_array_index(sink_funcs, SinkFunc, i).type)) {
g_array_index(sink_funcs, SinkFunc, i).sinkfunc(obj);
- return;
+
+ sunk = TRUE;
+ break;
}
}
}
- if (G_IS_INITIALLY_UNOWNED(obj) && !g_object_is_floating(obj)) {
- /* GtkWindow and GtkInvisible does not return a ref to caller of
- * g_object_new.
- */
- g_object_ref(obj);
- } else if (g_object_is_floating(obj)) {
+
+ if (!sunk && G_IS_INITIALLY_UNOWNED (obj))
g_object_ref_sink(obj);
- }
+
+ g_object_set_qdata (obj, pygobject_ref_sunk_key, GINT_TO_POINTER (1));
}
/**
@@ -982,6 +988,13 @@ pygobject_new(GObject *obj)
return pygobject_new_full(obj, TRUE, NULL);
}
+PyObject *
+pygobject_new_sunk(GObject *obj)
+{
+ g_object_set_qdata (obj, pygobject_ref_sunk_key, GINT_TO_POINTER (1));
+ return pygobject_new_full(obj, TRUE, NULL);
+}
+
static void
pygobject_unwatch_closure(gpointer data, GClosure *closure)
{
@@ -1137,6 +1150,45 @@ pygobject_free(PyObject *op)
PyObject_GC_Del(op);
}
+gboolean
+pygobject_prepare_construct_properties(GObjectClass *class, PyObject *kwargs,
+ guint *n_params, GParameter **params)
+{
+ *n_params = 0;
+ *params = NULL;
+
+ if (kwargs) {
+ Py_ssize_t pos = 0;
+ PyObject *key;
+ PyObject *value;
+
+ *params = g_new0(GParameter, PyDict_Size(kwargs));
+ while (PyDict_Next(kwargs, &pos, &key, &value)) {
+ GParamSpec *pspec;
+ GParameter *param = &(*params)[*n_params];
+ const gchar *key_str = PYGLIB_PyUnicode_AsString(key);
+
+ pspec = g_object_class_find_property(class, key_str);
+ if (!pspec) {
+ PyErr_Format(PyExc_TypeError,
+ "gobject `%s' doesn't support property `%s'",
+ G_OBJECT_CLASS_NAME(class), key_str);
+ return FALSE;
+ }
+ g_value_init(&param->value, G_PARAM_SPEC_VALUE_TYPE(pspec));
+ if (pyg_param_gvalue_from_pyobject(&param->value, value, pspec) < 0) {
+ PyErr_Format(PyExc_TypeError,
+ "could not convert value for property `%s' from %s to %s",
+ key_str, Py_TYPE(value)->tp_name,
+ g_type_name(G_PARAM_SPEC_VALUE_TYPE(pspec)));
+ return FALSE;
+ }
+ param->name = g_strdup(key_str);
+ ++(*n_params);
+ }
+ }
+ return TRUE;
+}
/* ---------------- PyGObject methods ----------------- */
@@ -1167,35 +1219,9 @@ pygobject_init(PyGObject *self, PyObject *args, PyObject *kwargs)
return -1;
}
- if (kwargs) {
- Py_ssize_t pos = 0;
- PyObject *key;
- PyObject *value;
-
- params = g_new0(GParameter, PyDict_Size(kwargs));
- while (PyDict_Next (kwargs, &pos, &key, &value)) {
- GParamSpec *pspec;
- gchar *key_str = PYGLIB_PyUnicode_AsString(key);
-
- pspec = g_object_class_find_property (class, key_str);
- if (!pspec) {
- PyErr_Format(PyExc_TypeError,
- "object of type `%s' doesn't support property `%s'",
- g_type_name(object_type), key_str);
- goto cleanup;
- }
- g_value_init(&params[n_params].value,
- G_PARAM_SPEC_VALUE_TYPE(pspec));
- if (pyg_value_from_pyobject(&params[n_params].value, value)) {
- PyErr_Format(PyExc_TypeError,
- "could not convert value for property `%s'",
- key_str);
- goto cleanup;
- }
- params[n_params].name = g_strdup(key_str);
- n_params++;
- }
- }
+ if (!pygobject_prepare_construct_properties (class, kwargs, &n_params, &params))
+ goto cleanup;
+
if (pygobject_constructv(self, n_params, params))
PyErr_SetString(PyExc_RuntimeError, "could not create object");
@@ -1509,7 +1535,11 @@ pygobject_connect(PyGObject *self, PyObject *args)
extra_args = PySequence_GetSlice(args, 2, len);
if (extra_args == NULL)
return NULL;
- closure = pyg_closure_new(callback, extra_args, NULL);
+
+ closure = pygi_signal_closure_new(self, name, callback, extra_args, NULL);
+ if (closure == NULL)
+ closure = pyg_closure_new(callback, extra_args, NULL);
+
pygobject_watch_closure((PyObject *)self, closure);
handlerid = g_signal_connect_closure_by_id(self->obj, sigid, detail,
closure, FALSE);
@@ -1558,7 +1588,11 @@ pygobject_connect_after(PyGObject *self, PyObject *args)
extra_args = PySequence_GetSlice(args, 2, len);
if (extra_args == NULL)
return NULL;
- closure = pyg_closure_new(callback, extra_args, NULL);
+
+ closure = pygi_signal_closure_new(self, name, callback, extra_args, NULL);
+ if (closure == NULL)
+ closure = pyg_closure_new(callback, extra_args, NULL);
+
pygobject_watch_closure((PyObject *)self, closure);
handlerid = g_signal_connect_closure_by_id(self->obj, sigid, detail,
closure, TRUE);
@@ -1607,7 +1641,11 @@ pygobject_connect_object(PyGObject *self, PyObject *args)
extra_args = PySequence_GetSlice(args, 3, len);
if (extra_args == NULL)
return NULL;
- closure = pyg_closure_new(callback, extra_args, object);
+
+ closure = pygi_signal_closure_new(self, name, callback, extra_args, object);
+ if (closure == NULL)
+ closure = pyg_closure_new(callback, extra_args, object);
+
pygobject_watch_closure((PyObject *)self, closure);
handlerid = g_signal_connect_closure_by_id(self->obj, sigid, detail,
closure, FALSE);
@@ -1656,7 +1694,11 @@ pygobject_connect_object_after(PyGObject *self, PyObject *args)
extra_args = PySequence_GetSlice(args, 3, len);
if (extra_args == NULL)
return NULL;
- closure = pyg_closure_new(callback, extra_args, object);
+
+ closure = pygi_signal_closure_new(self, name, callback, extra_args, object);
+ if (closure == NULL)
+ closure = pyg_closure_new(callback, extra_args, object);
+
pygobject_watch_closure((PyObject *)self, closure);
handlerid = g_signal_connect_closure_by_id(self->obj, sigid, detail,
closure, TRUE);
@@ -2286,6 +2328,7 @@ pygobject_object_register_types(PyObject *d)
pygobject_has_updated_constructor_key =
g_quark_from_static_string("PyGObject::has-updated-constructor");
pygobject_instance_data_key = g_quark_from_static_string("PyGObject::instance-data");
+ pygobject_ref_sunk_key = g_quark_from_static_string("PyGObject::ref-sunk");
/* GObject */
if (!PY_TYPE_OBJECT)
diff --git a/gobject/pygobject.h b/gobject/pygobject.h
index afbc665f..21743bab 100644
--- a/gobject/pygobject.h
+++ b/gobject/pygobject.h
@@ -198,6 +198,7 @@ struct _PyGObject_Functions {
gpointer data);
gboolean (*gerror_exception_check) (GError **error);
PyObject* (*option_group_new) (GOptionGroup *group);
+ GType (* type_from_object_strict) (PyObject *obj, gboolean strict);
};
#ifndef _INSIDE_PYGOBJECT_
@@ -218,6 +219,7 @@ struct _PyGObject_Functions *_PyGObject_API;
#define pygobject_watch_closure (_PyGObject_API->object_watch_closure)
#define pyg_closure_set_exception_handler (_PyGObject_API->closure_set_exception_handler)
#define pyg_destroy_notify (_PyGObject_API->destroy_notify)
+#define pyg_type_from_object_strict (_PyGObject_API->type_from_object_strict)
#define pyg_type_from_object (_PyGObject_API->type_from_object)
#define pyg_type_wrapper_new (_PyGObject_API->type_wrapper_new)
#define pyg_enum_get_value (_PyGObject_API->enum_get_value)
diff --git a/gobject/pygpointer.c b/gobject/pygpointer.c
index fbe3e105..575c7511 100644
--- a/gobject/pygpointer.c
+++ b/gobject/pygpointer.c
@@ -44,7 +44,7 @@ pyg_pointer_dealloc(PyGPointer *self)
static PyObject*
pyg_pointer_richcompare(PyObject *self, PyObject *other, int op)
{
- if (Py_TYPE(self) == Py_TYPE(other) && Py_TYPE(self) == &PyGPointer_Type)
+ if (Py_TYPE(self) == Py_TYPE(other))
return _pyglib_generic_ptr_richcompare(((PyGPointer*)self)->pointer,
((PyGPointer*)other)->pointer,
op);
diff --git a/gobject/pygtype.c b/gobject/pygtype.c
index 32f8640c..5ee54aac 100644
--- a/gobject/pygtype.c
+++ b/gobject/pygtype.c
@@ -349,16 +349,20 @@ pyg_type_wrapper_new(GType type)
}
/**
- * pyg_type_from_object:
+ * pyg_type_from_object_strict:
* obj: a Python object
+ * strict: if set to TRUE, raises an exception if it can't perform the
+ * conversion
*
- * converts a python object to a GType. Raises an exception if it
- * can't perform the conversion.
+ * converts a python object to a GType. If strict is set, raises an
+ * exception if it can't perform the conversion, otherwise returns
+ * PY_TYPE_OBJECT.
*
* Returns: the corresponding GType, or 0 on error.
*/
+
GType
-pyg_type_from_object(PyObject *obj)
+pyg_type_from_object_strict(PyObject *obj, gboolean strict)
{
PyObject *gtype;
GType type;
@@ -416,10 +420,35 @@ pyg_type_from_object(PyObject *obj)
}
PyErr_Clear();
+
+ /* Some API like those that take GValues can hold a python object as
+ * a pointer. This is potentially dangerous becuase everything is
+ * passed in as a PyObject so we can't actually type check it. Only
+ * fallback to PY_TYPE_OBJECT if strict checking is disabled
+ */
+ if (!strict)
+ return PY_TYPE_OBJECT;
+
PyErr_SetString(PyExc_TypeError, "could not get typecode from object");
return 0;
}
+/**
+ * pyg_type_from_object:
+ * obj: a Python object
+ *
+ * converts a python object to a GType. Raises an exception if it
+ * can't perform the conversion.
+ *
+ * Returns: the corresponding GType, or 0 on error.
+ */
+GType
+pyg_type_from_object(PyObject *obj)
+{
+ /* Legacy call always defaults to strict type checking */
+ return pyg_type_from_object_strict(obj, TRUE);
+}
+
/* -------------- GValue marshalling ------------------ */
/**
@@ -736,13 +765,20 @@ pyg_value_from_pyobject(GValue *value, PyObject *obj)
}
break;
case G_TYPE_CHAR:
- if ((tmp = PyObject_Str(obj)))
- g_value_set_char(value, PYGLIB_PyUnicode_AsString(tmp)[0]);
- else {
+#if PY_VERSION_HEX < 0x03000000
+ if (PyString_Check(obj)) {
+ g_value_set_char(value, PyString_AsString(obj)[0]);
+ } else
+#endif
+ if (PyUnicode_Check(obj)) {
+ tmp = PyUnicode_AsUTF8String(obj);
+ g_value_set_char(value, PYGLIB_PyBytes_AsString(tmp)[0]);
+ Py_DECREF(tmp);
+ } else {
PyErr_Clear();
return -1;
}
- Py_DECREF(tmp);
+
break;
case G_TYPE_UCHAR:
if (PYGLIB_PyLong_Check(obj)) {
@@ -752,8 +788,13 @@ pyg_value_from_pyobject(GValue *value, PyObject *obj)
g_value_set_uchar(value, (guchar)PYGLIB_PyLong_AsLong (obj));
else
return -1;
- } else if ((tmp = PyObject_Str(obj))) {
- g_value_set_uchar(value, PYGLIB_PyUnicode_AsString(tmp)[0]);
+#if PY_VERSION_HEX < 0x03000000
+ } else if (PyString_Check(obj)) {
+ g_value_set_uchar(value, PyString_AsString(obj)[0]);
+#endif
+ } else if (PyUnicode_Check(obj)) {
+ tmp = PyUnicode_AsUTF8String(obj);
+ g_value_set_uchar(value, PYGLIB_PyBytes_AsString(tmp)[0]);
Py_DECREF(tmp);
} else {
PyErr_Clear();
@@ -848,14 +889,29 @@ pyg_value_from_pyobject(GValue *value, PyObject *obj)
g_value_set_double(value, PyFloat_AsDouble(obj));
break;
case G_TYPE_STRING:
- if (obj == Py_None)
+ if (obj == Py_None) {
g_value_set_string(value, NULL);
- else if ((tmp = PyObject_Str(obj))) {
- g_value_set_string(value, PYGLIB_PyUnicode_AsString(tmp));
- Py_DECREF(tmp);
} else {
- PyErr_Clear();
- return -1;
+ PyObject* tmp_str = PyObject_Str(obj);
+ if (tmp_str == NULL) {
+ PyErr_Clear();
+ if (PyUnicode_Check(obj)) {
+ tmp = PyUnicode_AsUTF8String(obj);
+ g_value_set_string(value, PYGLIB_PyBytes_AsString(tmp));
+ Py_DECREF(tmp);
+ } else {
+ return -1;
+ }
+ } else {
+#if PY_VERSION_HEX < 0x03000000
+ g_value_set_string(value, PyString_AsString(tmp_str));
+#else
+ tmp = PyUnicode_AsUTF8String(tmp_str);
+ g_value_set_string(value, PyBytes_AsString(tmp));
+ Py_DECREF(tmp);
+#endif
+ }
+ Py_XDECREF(tmp_str);
}
break;
case G_TYPE_POINTER:
@@ -966,7 +1022,7 @@ pyg_value_as_pyobject(const GValue *value, gboolean copy_boxed)
switch (G_TYPE_FUNDAMENTAL(G_VALUE_TYPE(value))) {
case G_TYPE_INTERFACE:
if (g_type_is_a(G_VALUE_TYPE(value), G_TYPE_OBJECT))
- return pygobject_new(g_value_get_object(value));
+ return pygobject_new_sunk(g_value_get_object(value));
else
break;
case G_TYPE_CHAR: {
@@ -975,7 +1031,7 @@ pyg_value_as_pyobject(const GValue *value, gboolean copy_boxed)
}
case G_TYPE_UCHAR: {
guint8 val = g_value_get_uchar(value);
- return PYGLIB_PyUnicode_FromStringAndSize((char *)&val, 1);
+ return PYGLIB_PyBytes_FromStringAndSize((char *)&val, 1);
}
case G_TYPE_BOOLEAN: {
return PyBool_FromLong(g_value_get_boolean(value));
@@ -1084,7 +1140,7 @@ pyg_value_as_pyobject(const GValue *value, gboolean copy_boxed)
case G_TYPE_PARAM:
return pyg_param_spec_new(g_value_get_param(value));
case G_TYPE_OBJECT:
- return pygobject_new(g_value_get_object(value));
+ return pygobject_new_sunk(g_value_get_object(value));
default:
{
PyGTypeMarshal *bm;
@@ -1280,7 +1336,7 @@ pyg_signal_class_closure_marshal(GClosure *closure,
g_return_if_fail(object != NULL && G_IS_OBJECT(object));
/* get the wrapper for this object */
- object_wrapper = pygobject_new(object);
+ object_wrapper = pygobject_new_sunk(object);
g_return_if_fail(object_wrapper != NULL);
/* construct method name for this class closure */
diff --git a/m4/python.m4 b/m4/python.m4
index 372627bd..523e45a2 100644
--- a/m4/python.m4
+++ b/m4/python.m4
@@ -45,13 +45,14 @@ AC_MSG_CHECKING(for headers required to compile python extensions)
dnl deduce PYTHON_INCLUDES
py_prefix=`$PYTHON -c "import sys; sys.stdout.write(sys.prefix)"`
py_exec_prefix=`$PYTHON -c "import sys; sys.stdout.write(sys.exec_prefix)"`
-if test -x "$PYTHON-config"; then
-PYTHON_INCLUDES=`$PYTHON-config --includes 2>/dev/null`
+PYTHON_CONFIG=`which $PYTHON`-config
+if test -x "$PYTHON_CONFIG"; then
+PYTHON_INCLUDES=`$PYTHON_CONFIG --includes 2>/dev/null`
else
PYTHON_INCLUDES="-I${py_prefix}/include/python${PYTHON_VERSION}"
if test "$py_prefix" != "$py_exec_prefix"; then
PYTHON_INCLUDES="$PYTHON_INCLUDES -I${py_exec_prefix}/include/python${PYTHON_VERSION}"
->fi
+fi
fi
AC_SUBST(PYTHON_INCLUDES)
dnl check if the headers exist:
@@ -226,32 +227,3 @@ python2.1 python2.0])
fi
])
-
-dnl a macro to check for ability to create python extensions
-dnl JD_CHECK_PYTHON_HEADERS([ACTION-IF-POSSIBLE], [ACTION-IF-NOT-POSSIBLE])
-dnl function also defines PYTHON_INCLUDES
-AC_DEFUN([JD_CHECK_PYTHON_HEADERS],
-[AC_REQUIRE([AM_PATH_PYTHON])
-AC_MSG_CHECKING(for headers required to compile python extensions)
-dnl deduce PYTHON_INCLUDES
-py_prefix=`$PYTHON -c "import sys; sys.stdout.write(sys.prefix)"`
-py_exec_prefix=`$PYTHON -c "import sys; sys.stdout.write(sys.exec_prefix)"`
-if test -x "$PYTHON-config"; then
-PYTHON_INCLUDES=`$PYTHON-config --includes 2>/dev/null`
-else
-PYTHON_INCLUDES="-I${py_prefix}/include/python${PYTHON_VERSION}"
-if test "$py_prefix" != "$py_exec_prefix"; then
- PYTHON_INCLUDES="$PYTHON_INCLUDES -I${py_exec_prefix}/include/python${PYTHON_VERSION}"
-fi
-fi
-AC_SUBST(PYTHON_INCLUDES)
-dnl check if the headers exist:
-save_CPPFLAGS="$CPPFLAGS"
-CPPFLAGS="$CPPFLAGS $PYTHON_INCLUDES"
-AC_TRY_CPP([#include <Python.h>],dnl
-[AC_MSG_RESULT(found)
-$1],dnl
-[AC_MSG_RESULT(not found)
-$2])
-CPPFLAGS="$save_CPPFLAGS"
-])
diff --git a/pygi-convert.sh b/pygi-convert.sh
index 4273c283..3eaa2491 100644
--- a/pygi-convert.sh
+++ b/pygi-convert.sh
@@ -1,6 +1,10 @@
#!/bin/sh
-FILES_TO_CONVERT="$(find sugar-pygi -iname \*.py) $(find sugar-toolkit-pygi -iname \*.py) sugar-pygi/bin/sugar-session"
+if [ -n "$1" ]; then
+ FILES_TO_CONVERT="$@"
+else
+ FILES_TO_CONVERT="$(find . -name '*.py')"
+fi
for f in $FILES_TO_CONVERT; do
perl -i -0 \
@@ -12,36 +16,62 @@ for f in $FILES_TO_CONVERT; do
-pe "s/gconf_client.notify_add\('\/desktop\/sugar\/collaboration\/publish_gadget',/return;gconf_client.notify_add\('\/desktop\/sugar\/collaboration\/publish_gadget',/g;" \
\
-pe "s/import gtk\n/from gi.repository import Gtk\n/g;" \
- -pe "s/gtk\./Gtk\./g;" \
- -pe "s/Gtk.SIZE_GROUP_/Gtk.SizeGroupMode./g;" \
- -pe "s/Gtk.POLICY_/Gtk.PolicyType./g;" \
- -pe "s/Gtk.STATE_/Gtk.StateType./g;" \
- -pe "s/Gtk.TARGET_/Gtk.TargetFlags./g;" \
- -pe "s/Gtk.SHADOW_NONE/Gtk.ShadowType.NONE/g;" \
+ -pe "s/(?<!\.)gtk\./Gtk\./g;" \
+ -pe "s/Gtk.ARROW_/Gtk.ArrowType./g;" \
+ -pe "s/Gtk.ASSISTANT_PAGE_/Gtk.AssistantPageType./g;" \
+ -pe "s/Gtk.BUTTONBOX_/Gtk.ButtonBoxStyle./g;" \
+ -pe "s/Gtk.BUTTONS_/Gtk.ButtonsType./g;" \
+ -pe "s/Gtk.CELL_RENDERER_MODE_/Gtk.CellRendererMode./g;" \
+ -pe "s/Gtk.CORNER_/Gtk.CornerType./g;" \
+ -pe "s/Gtk.DIALOG_/Gtk.DialogFlags./g;" \
+ -pe "s/Gtk.ENTRY_ICON_/Gtk.EntryIconPosition./g;" \
+ -pe "s/Gtk.EXPAND/Gtk.AttachOptions.EXPAND/g;" \
+ -pe "s/Gtk.FILE_CHOOSER_ACTION_/Gtk.FileChooserAction./g;" \
+ -pe "s/Gtk.FILL/Gtk.AttachOptions.FILL/g;" \
+ -pe "s/Gtk.ICON_LOOKUP_/Gtk.IconLookupFlags./g;" \
-pe "s/Gtk.ICON_SIZE_/Gtk.IconSize./g;" \
-pe "s/Gtk.IMAGE_/Gtk.ImageType./g;" \
- -pe "s/Gtk.SELECTION_/Gtk.SelectionMode./g;" \
- -pe "s/Gtk.CELL_RENDERER_MODE_/Gtk.CellRendererMode./g;" \
- -pe "s/Gtk.TREE_VIEW_COLUMN_/Gtk.TreeViewColumnSizing./g;" \
- -pe "s/Gtk.TEXT_DIR_/Gtk.TextDirection./g;" \
+ -pe "s/Gtk.JUSTIFY_/Gtk.Justification./g;" \
+ -pe "s/Gtk.MESSAGE_/Gtk.MessageType./g;" \
+ -pe "s/Gtk.MOVEMENT_/Gtk.MovementStep./g;" \
+ -pe "s/Gtk.POLICY_/Gtk.PolicyType./g;" \
-pe "s/Gtk.POS_/Gtk.PositionType./g;" \
+ -pe "s/Gtk.RELIEF_/Gtk.ReliefStyle./g;" \
+ -pe "s/Gtk.RESPONSE_/Gtk.ResponseType./g;" \
+ -pe "s/Gtk.SELECTION_/Gtk.SelectionMode./g;" \
-pe "s/Gtk.SHADOW_/Gtk.ShadowType./g;" \
- -pe "s/Gtk.BUTTONBOX_/Gtk.ButtonBoxStyle./g;" \
+ -pe "s/Gtk.SHADOW_NONE/Gtk.ShadowType.NONE/g;" \
-pe "s/Gtk.SHRINK/Gtk.AttachOptions.SHRINK/g;" \
- -pe "s/Gtk.FILL/Gtk.AttachOptions.FILL/g;" \
- -pe "s/Gtk.JUSTIFY_/Gtk.Justification./g;" \
- -pe "s/Gtk.RESPONSE_/Gtk.ResponseType./g;" \
- -pe "s/Gtk.CORNER_/Gtk.CornerType./g;" \
- -pe "s/Gtk.ENTRY_ICON_/Gtk.EntryIconPosition./g;" \
- -pe "s/Gtk.settings_get_default/Gtk.Settings.get_default/g;" \
+ -pe "s/Gtk.SIZE_GROUP_/Gtk.SizeGroupMode./g;" \
+ -pe "s/Gtk.SORT_/Gtk.SortType./g;" \
+ -pe "s/Gtk.STATE_/Gtk.StateType./g;" \
+ -pe "s/Gtk.TARGET_/Gtk.TargetFlags./g;" \
+ -pe "s/Gtk.TEXT_DIR_/Gtk.TextDirection./g;" \
+ -pe "s/Gtk.TEXT_SEARCH_/Gtk.TextSearchFlags./g;" \
+ -pe "s/Gtk.TEXT_WINDOW_/Gtk.TextWindowType./g;" \
+ -pe "s/Gtk.TREE_VIEW_COLUMN_/Gtk.TreeViewColumnSizing./g;" \
+ -pe "s/Gtk.WINDOW_/Gtk.WindowType./g;" \
+ -pe "s/Gtk.WIN_POS_/Gtk.WindowPosition./g;" \
+ -pe "s/Gtk.WRAP_/Gtk.WrapMode./g;" \
+ -pe "s/Gtk.UI_MANAGER_/Gtk.UIManagerItemType./g;" \
+ -pe "s/Gtk.accel_map_/Gtk.AccelMap./g;" \
+ -pe "s/Gtk.settings_get_/Gtk.Settings.get_/g;" \
-pe "s/Gtk.icon_theme_get_default/Gtk.IconTheme.get_default/g;" \
- -pe "s/.window.set_type_hint/.set_type_hint/g;" \
+ -pe "s/Gtk.image_new_from_stock/Gtk.Image.new_from_stock/g;" \
+ -pe "s/Gtk.image_new_from_icon_name/Gtk.Image.new_from_icon_name/g;" \
+ -pe "s/Gtk.window_set_default_icon_name/Gtk.Window.set_default_icon_name/g; " \
+ -pe "s/Gtk.combo_box_new_text/Gtk.ComboBoxText/g;" \
+ -pe "s/Gtk.keysyms./Gdk.KEY_/g;" \
+ -pe "s/set_flags\(Gtk.CAN_DEFAULT\)/set_can_default\(True\)/g;" \
+ -pe "s/.flags\(\) & Gtk.MAPPED/.get_mapped\(\)/g;" \
+ -pe "s/.flags\(\) & Gtk.REALIZED/.get_realized\(\)/g;" \
+ -pe "s/\.window\.set_type_hint/.set_type_hint/g;" \
+ -pe "s/\.window\.set_skip_taskbar_hint/.set_skip_taskbar_hint/g;" \
+ -pe "s/\.window\.set_transient_for/.set_transient_for/g;" \
-pe "s/self.drag_source_unset\(\)/Gtk.drag_source_unset\(self\)/g;" \
-pe "s/self.drag_dest_unset\(\)/Gtk.drag_dest_unset\(self\)/g;" \
- -pe "s/Gtk.ListStore\(([^\)]*)\)/Gtk.ListStore.newv\(\[\1\]\)/g;" \
-pe "s/Gtk.Alignment\(/Gtk.Alignment.new\(/g;" \
-pe "s/self._model.filter_new\(\)/Gtk.TreeModelFilter.new\(self._model, None\)/g;" \
- -pe "#s/Gtk.ScrolledWindow\(\)/Gtk.ScrolledWindow\(None, None\)/g;" \
-pe "#s/Gtk.Window.__init__\(self\)/Gtk.Window.__init__\(Gtk.WindowType.TOPLEVEL\)/g;" \
-pe "s/\.child([^_a-z])/.get_child\(\)\1/g;" \
\
@@ -52,15 +82,19 @@ for f in $FILES_TO_CONVERT; do
-pe "s/pack_start\(([^,]*),(\s*)padding=([A-Za-z0-9._]*)\)/pack_start\(\1,\2True, True,\2\3\)/g;" \
-pe "#s/Gtk.HBox\(\)/Gtk.HBox\(False, 0\)/g;" \
-pe "#s/Gtk.VBox\(\)/Gtk.VBox\(False, 0\)/g;" \
- -pe "s/Gtk.Label\(([^,\)]+)\)/Gtk.Label\(label=\1\)/g;" \
- -pe "s/Gtk.AccelLabel\(([^,\)]+)\)/Gtk.AccelLabel\(label=\1\)/g;" \
+ -pe "s/Gtk.Label\s*\(([^,\)]+)\)/Gtk.Label\(label=\1\)/g;" \
+ -pe "s/Gtk.AccelLabel\s*\(([^,\)]+)\)/Gtk.AccelLabel\(label=\1\)/g;" \
+ -pe "s/Gtk.((?:Accel)?Label)\(label=label=/Gtk.\1\(label=/g;" \
-pe "s/len\(self._content.get_children\(\)\) > 0/self._content.get_children\(\)/g;" \
-pe "s/len\(self.menu.get_children\(\)\) > 0/self.menu.get_children\(\)/g;" \
-pe "s/([^\.^ ]*)\.drag_dest_set\(/Gtk.drag_dest_set\(\1, /g;" \
-pe "s/Gtk\..*\.__init__/gobject.GObject.__init__/g;" \
\
+ -pe "s/from gtk import gdk\n/from gi.repository import Gdk\n/g;" \
+ -pe "s/import gtk.gdk as gdk\n/from gi.repository import Gdk\n/g;" \
-pe "s/Gtk.gdk.x11_/GdkX11\./g;" \
-pe "s/Gtk.gdk\./Gdk\./g;" \
+ -pe "s/(?<!\.)gdk\./Gdk\./g;" \
-pe "s/Gdk.screen_width/Gdk.Screen.width/g;" \
-pe "s/Gdk.screen_height/Gdk.Screen.height/g;" \
-pe "s/Gdk.screen_get_default/Gdk.Screen.get_default/g;" \
@@ -71,15 +105,74 @@ for f in $FILES_TO_CONVERT; do
-pe "s/Gdk.([A-Z_0-9]*)_MASK/Gdk.EventMask.\1_MASK/g;" \
-pe "s/Gdk.VISIBILITY_FULLY_OBSCURED/Gdk.VisibilityState.FULLY_OBSCURED/g;" \
-pe "s/Gdk.BUTTON_PRESS/Gdk.EventType.BUTTON_PRESS/g;" \
+ -pe "s/Gdk.ACTION_/Gdk.DragAction./g;" \
+ -pe "s/Gdk.GRAB_/Gdk.GrabStatus./g;" \
+ -pe "s/Gdk.SCROLL_(DOWN|LEFT|RIGHT|UP)/Gdk.ScrollDirection.\1/g;" \
+ -pe "s/Gdk.([A-Z]+_(PTR|CURSOR))/Gdk.CursorType.\1/g;" \
+ -pe "s/Gdk.Cursor\s*\(/Gdk.Cursor.new\(/g;" \
-pe "s/#Gdk.Rectangle\(([^,\)]*), ([^,\)]*), ([^,\)]*), ([^,\)]*)\)/\1, \2, \3, \4/g;" \
-pe "s/Gdk.Rectangle//g;" \
-pe "s/intersection = child_rect.intersect/intersects_, intersection = child_rect.intersect/g;" \
-pe "s/event.state/event.get_state\(\)/g;" \
\
+ -pe "s/Gdk.pixbuf_/GdkPixbuf.Pixbuf./g;" \
+ -pe "s/Gdk.Pixbuf/GdkPixbuf.Pixbuf/g;" \
+ -pe "s/Gdk.INTERP_/GdkPixbuf.InterpType./g;" \
+ -pe "s/Gdk.COLORSPACE_RGB/GdkPixbuf.Colorspace.RGB/g;" \
+\
-pe "s/import pango\n/from gi.repository import Pango\n/g;" \
-pe "s/pango\./Pango\./g;" \
- -pe "s/Pango\.FontDescription/Pango\.Font\.description_from_string/g;" \
-pe "s/Pango.ELLIPSIZE_/Pango.EllipsizeMode./g;" \
+ -pe "s/Pango.STYLE_/Pango.Style./g;" \
+ -pe "s/Pango.UNDERLINE_/Pango.Underline./g;" \
+ -pe "s/Pango.WEIGHT_/Pango.Weight./g;" \
+ -pe "s/Pango.WRAP_/Pango.WrapMode./g;" \
+ -pe "s/Pango.TAB_/Pango.TabAlign./g;" \
+\
+ -pe "s/import atk\n/from gi.repository import Atk\n/g;" \
+ -pe "s/atk\./Atk\./g;" \
+ -pe "s/Atk.HYPERLINK_/Atk.HyperlinkStateFlags./g;" \
+ -pe "s/Atk.KEY_EVENT_/Atk.KeyEventType./g;" \
+ -pe "s/Atk.LAYER_/Atk.Layer./g;" \
+ -pe "s/Atk.RELATION_/Atk.RelationType./g;" \
+ -pe "s/Atk.ROLE_/Atk.Role./g;" \
+ -pe "s/Atk.STATE_/Atk.StateType./g;" \
+ -pe "s/Atk.TEXT_ATTR_/Atk.TextAttribute./g;" \
+ -pe "s/Atk.TEXT_BOUNDARY_/Atk.TextBoundary./g;" \
+ -pe "s/Atk.TEXT_CLIP_/Atk.TextClipType./g;" \
+\
+ -pe "s/import gio\n/from gi.repository import Gio\n/g;" \
+ -pe "s/gio\./Gio\./g;" \
+ -pe "s/Gio.FILE_COPY_/Gio.FileCopyFlags./g;" \
+ -pe "s/Gio.FILE_CREATE_/Gio.FileCreateFlags./g;" \
+ -pe "s/Gio.FILE_MONITOR_EVENT_/Gio.FileMonitorEvent./g;" \
+ -pe "s/Gio.FILE_MONITOR_/Gio.FileMonitorFlags./g;" \
+ -pe "s/Gio.FILE_TYPE_/Gio.FileType./g;" \
+ -pe "s/Gio.FILE_QUERY_INFO_/Gio.FileQueryInfoFlags./g;" \
+ -pe "s/Gio.MOUNT_MOUNT_/Gio.MountMountFlags./g;" \
+ -pe "s/Gio.MOUNT_OPERATION_/Gio.MountOperationResult./g;" \
+ -pe "s/Gio.MOUNT_UNMOUNT_/Gio.MountUnmountFlags./g;" \
+ -pe "s/Gio.OUTPUT_STREAM_SPLICE_/Gio.OutputStreamSpliceFlags./g;" \
+ -pe "s/Gio.vfs_/Gio.Vfs./g;" \
+\
+ -pe "# GLib is not introspectable very well, for now we keep using the static bindings" \
+ -pe "s/glib.GError\b/gobject.GError/g;" \
+ -pe "s/glib.timeout_add\b/gobject.timeout_add/g;" \
+ -pe "s/glib.source_remove\b/gobject.source_remove/g;" \
+ -pe "s/glib.MainLoop\b/gobject.MainLoop/g;" \
+ -pe "#s/import glib\n/from gi.repository import GLib\n/g;" \
+ -pe "#s/(?<!\.)glib\./GLib\./g;" \
+ -pe "#s/GLib.IO_(ERR|HUP|IN|NVAL|OUT|PRI)/GLib.IOCondition./g;" \
+ -pe "#s/GLib.IO_FLAG_/GLib.IOFlags./g;" \
+ -pe "#s/GLib.IO_STATUS_/GLib.IOStatus./g;" \
+ -pe "#s/GLib.OPTION_ERROR_/GLib.OptionError./g;" \
+ -pe "#s/GLib.OPTION_FLAG_/GLib.OptionFlags./g;" \
+ -pe "#s/GLib.SPAWN_/GLib.SpawnFlags./g;" \
+ -pe "#s/GLib.USER_DIRECTORY_/GLib.UserDirectory.DIRECTORY_/g;" \
+\
+ -pe "s/(?<!\.)gobject\./GObject\./g;" \
+ -pe "s/GObject.SIGNAL_/GObject.SignalFlags./g;" \
+ -pe "s/GObject.TYPE_NONE/None/g;" \
\
-pe "s/import hippo\n/from gi.repository import Hippo\n/g;" \
-pe "s/hippo\./Hippo\./g;" \
@@ -105,6 +198,13 @@ for f in $FILES_TO_CONVERT; do
-pe "s/_sugarext\./SugarExt\./g;" \
\
-pe "s/import gtksourceview2\n/from gi.repository import GtkSource\n/g;" \
+ -pe "s/import gtksourceview2 as gsv\n/from gi.repository import GtkSource\n/g;" \
+ -pe "s/gtksourceview2\./GtkSource\./g;" \
+ -pe "s/gsv\./GtkSource\./g;" \
+ -pe "s/GtkSource.DRAW_SPACES_/GtkSource.DrawSpacesFlags./g;" \
+ -pe "s/GtkSource.SMART_HOME_END_/GtkSource.SmartHomeEndType./g;" \
+ -pe "s/GtkSource.style_scheme_manager_get_default/GtkSource.StyleSchemeManager.get_default/g;" \
+ -pe "s/GtkSource.language_manager_get_default/GtkSource.LanguageManager.get_default/g;" \
\
-pe "#s/import cairo\n/from gi.repository import cairo\n/g;" \
\
@@ -121,56 +221,13 @@ for f in $FILES_TO_CONVERT; do
-pe "s/gobject.GObject.__init__\(self, self._model_filter\)/gobject.GObject.__init__\(self, model=self._model_filter\)/g;" \
-pe "s/self._model_filter.set_visible_func/return;self._model_filter.set_visible_func/g;" \
-pe "s/buddies_column.set_cell_data_func/return;buddies_column.set_cell_data_func/g;" \
- -pe "s/ column.set_cell_data_func/# column.set_cell_data_func/g;" \
-pe "s/Hippo\.cairo_surface_from_gdk_pixbuf/SugarExt\.cairo_surface_from_pixbuf/g;" \
+\
+ -pe "s/import pynotify\n/from gi.repository import Notify\n/g;" \
+ -pe "s/pynotify\./Notify\./g;" \
+\
+ -pe "s/import webkit\n/from gi.repository import WebKit\n/g;" \
$f
done
-NEED_GOBJECT=`grep -R -l gobject\. $FILES_TO_CONVERT | xargs grep -nL import\ gobject`
-for f in $NEED_GOBJECT; do
- sed -i "/import Gdk/ i\import gobject" $f
-done
-
-NEED_GOBJECT=`grep -R -l gobject\. $FILES_TO_CONVERT | xargs grep -nL import\ gobject`
-for f in $NEED_GOBJECT; do
- sed -i "/import Gtk/ i\import gobject" $f
-done
-
-NEED_GOBJECT=`grep -R -l gobject\. $FILES_TO_CONVERT | xargs grep -nL import\ gobject`
-for f in $NEED_GOBJECT; do
- sed -i "/import Hippo/ i\import gobject" $f
-done
-
-NEED_GDK=`grep -R -l Gdk\. $FILES_TO_CONVERT | xargs grep -nL import\ Gdk`
-for f in $NEED_GDK; do
- sed -i "/import Gtk/ i\from gi.repository import Gdk" $f
-done
-
-NEED_GDK_X11=`grep -R -l GdkX11\. $FILES_TO_CONVERT | xargs grep -nL import\ GdkX11`
-for f in $NEED_GDK_X11; do
- sed -i "/import Gdk/ i\from gi.repository import GdkX11" $f
-done
-
-NEED_SUGAR_EXT=`grep -R -l SugarExt\. $FILES_TO_CONVERT | xargs grep -nL import\ SugarExt`
-for f in $NEED_SUGAR_EXT; do
- sed -i "/import cairo/ i\from gi.repository import SugarExt" $f
-done
-
-for f in sugar-pygi/src/jarabe/util/emulator.py sugar-pygi/bin/sugar-session; do
- sed -i "/import Gdk/ a\Gdk.init_check([])" $f
- sed -i "/import Gtk/ a\Gtk.init_check([])" $f
-done
-
-sed -i "/Gdk.threads_init()/ i\gobject.threads_init()" sugar-pygi/bin/sugar-session
-
-# Disable treeview stuff
-sed -i 's/class CellRendererIcon(Gtk.GenericCellRenderer):/class CellRendererIcon(Gtk.CellRenderer):/g' sugar-toolkit-pygi/src/sugar/graphics/icon.py
-
-#sed -i '/def get_icon_state(base_name, perc, step=5):/ i\"""' sugar-toolkit-pygi/src/sugar/graphics/icon.py
-
-#sed -i 's/from sugar.graphics.icon import Icon, CellRendererIcon/from sugar.graphics.icon import Icon/g' sugar-pygi/src/jarabe/desktop/activitieslist.py
-
-#sed -i '/class CellRendererFavorite(CellRendererIcon):/ i\"""' sugar-pygi/src/jarabe/desktop/activitieslist.py
-#sed -i '/def get_icon_state(base_name, perc, step=5):/ i\"""' sugar-toolkit-pygi/src/sugar/graphics/icon.py
-
diff --git a/pygobject-2.0.pc.in b/pygobject-2.0.pc.in
index 5188a12c..a47b685d 100644
--- a/pygobject-2.0.pc.in
+++ b/pygobject-2.0.pc.in
@@ -13,6 +13,7 @@ fixxref=${datadir}/pygobject/xsl/fixxref.py
pygdocs=${datadir}/gtk-doc/html/pygobject
defsdir=${datadir}/pygobject/2.0/defs
codegendir=${datadir}/pygobject/2.0/codegen
+overridesdir=@pyexecdir@/gi/overrides
Name: PyGObject
Description: Python bindings for GObject
diff --git a/pygobject_postinstall.py b/pygobject_postinstall.py
index 0b66773d..bd546bf6 100644
--- a/pygobject_postinstall.py
+++ b/pygobject_postinstall.py
@@ -1,62 +1,9 @@
+# -*- coding: utf-8 -*-
-"""pygobject is now installed on your machine.
-Local configuration files were successfully updated."""
+import sys
-import os, re, sys
-
-pkgconfig_file = os.path.normpath(
- os.path.join(sys.prefix,
- 'lib/pkgconfig/pygobject-2.0.pc'))
-
-prefix_pattern=re.compile("^prefix=.*")
-version_pattern=re.compile("Version: ([0-9]+\.[0-9]+\.[0-9]+)")
-
-def replace_prefix(s):
- if prefix_pattern.match(s):
- s='prefix='+sys.prefix.replace("\\","/")+'\n'
- s=s.replace("@DATADIR@",
- os.path.join(sys.prefix,'share').replace("\\","/"))
-
- return s
-
-def get_doc_url(pkgconfig_file, base_url):
- try:
- f = open(pkgconfig_file).read()
- ver = version_pattern.search(f).groups()[0]
- majv,minv,micv = ver.split('.')
- doc_url = "%s/%s.%s/" % (base_url,majv,minv)
- except:
- doc_url = base_url + "/stable/"
- return doc_url
-
-# TODO : Check that shortcuts are created system-wide when the user
-# has admin rights (hint: see pywin32 postinstall)
-def create_shortcuts():
- progs_folder= get_special_folder_path("CSIDL_COMMON_PROGRAMS")
- site_packages_dir = os.path.join(sys.prefix , 'lib','site-packages')
-
- pygtk_shortcuts = os.path.join(progs_folder, 'PyGTK')
- if not os.path.isdir(pygtk_shortcuts):
- os.mkdir(pygtk_shortcuts)
-
- # link to specific documentation version by parsing the
- # pkgconfig file
- doc_url = get_doc_url(pkgconfig_file,
- "http://library.gnome.org/devel/pygobject")
- pygobject_doc_link=os.path.join(pygtk_shortcuts,
- 'PyGObject Documentation.lnk')
- if os.path.isfile(pygobject_doc_link):
- os.remove(pygobject_doc_link)
- create_shortcut(doc_url,'PyGObject Documentation',pygobject_doc_link)
- file_created(pygobject_doc_link)
if len(sys.argv) == 2:
- if sys.argv[1] == "-install":
- # fixup the pkgconfig file
- lines=open(pkgconfig_file).readlines()
- open(pkgconfig_file, 'w').writelines(map(replace_prefix,lines))
- # TODO: Add an installer option for shortcut creation
- # create_shortcuts()
- print __doc__
-
+ if sys.argv[1] == '-install':
+ print ('pygobject is now installed on your machine.\n')
diff --git a/setup.py b/setup.py
index 5135a9d7..74b9141f 100755
--- a/setup.py
+++ b/setup.py
@@ -1,100 +1,132 @@
#!/usr/bin/env python
+# -*- coding: utf-8 -*-
#
# setup.py - distutils configuration for pygobject
-#
-"""Python Bindings for GObject."""
+
+
+'''Python Bindings for GObject.
+
+PyGObject is a set of bindings for the glib, gobject and gio libraries.
+It provides an object oriented interface that is slightly higher level than
+the C one. It automatically does all the type casting and reference
+counting that you would have to do normally with the C API. You can
+find out more on the official homepage, http://www.pygtk.org/'''
+
+
+import os
+import sys
+import glob
from distutils.command.build import build
from distutils.command.build_clib import build_clib
+from distutils.command.build_scripts import build_scripts
from distutils.sysconfig import get_python_inc
+from distutils.extension import Extension
from distutils.core import setup
-import glob
-import os
-import sys
-from dsextras import get_m4_define, getoutput, have_pkgconfig, \
- GLOBAL_INC, GLOBAL_MACROS, InstallLib, InstallData, BuildExt, \
- PkgConfigExtension, TemplateExtension, \
- pkgc_get_libraries, pkgc_get_library_dirs, pkgc_get_include_dirs
+from dsextras import GLOBAL_MACROS, GLOBAL_INC, get_m4_define, getoutput, \
+ have_pkgconfig, pkgc_get_libraries, \
+ pkgc_get_library_dirs, pkgc_get_include_dirs, \
+ PkgConfigExtension, TemplateExtension, \
+ BuildExt, InstallLib, InstallData
-if '--yes-i-know-its-not-supported' in sys.argv:
- sys.argv.remove('--yes-i-know-its-not-supported')
-else:
- print '*'*70
- print 'Building PyGObject using distutils is NOT SUPPORTED.'
- print "It's mainly included to be able to easily build win32 installers"
- print "You may continue, but only if you agree to not ask any questions"
- print "To build PyGObject in a supported way, read the INSTALL file"
- print
- print "Build fixes are of course welcome and should be filed in bugzilla"
- print '*'*70
- input = raw_input('Not supported, ok [y/N]? ')
- if not input.startswith('y'):
- raise SystemExit("Aborted")
+
+if sys.platform != 'win32':
+ msg = '*' * 68 + '\n'
+ msg += '* Building PyGObject using distutils is only supported on windows. *\n'
+ msg += '* To build PyGObject in a supported way, read the INSTALL file. *\n'
+ msg += '*' * 68
+ raise SystemExit(msg)
MIN_PYTHON_VERSION = (2, 6, 0)
-MAJOR_VERSION = int(get_m4_define('pygobject_major_version'))
-MINOR_VERSION = int(get_m4_define('pygobject_minor_version'))
-MICRO_VERSION = int(get_m4_define('pygobject_micro_version'))
+if sys.version_info[:3] < MIN_PYTHON_VERSION:
+ raise SystemExit('ERROR: Python %s or higher is required, %s found.' % (
+ '.'.join(map(str, MIN_PYTHON_VERSION)),
+ '.'.join(map(str, sys.version_info[:3]))))
+
+if not have_pkgconfig():
+ raise SystemExit('ERROR: Could not find pkg-config: '
+ 'Please check your PATH environment variable.')
-VERSION = "%d.%d.%d" % (MAJOR_VERSION, MINOR_VERSION, MICRO_VERSION)
-GLIB_REQUIRED = get_m4_define('glib_required_version')
+PYGTK_SUFFIX = '2.0'
+PYGTK_SUFFIX_LONG = 'gtk-' + PYGTK_SUFFIX
-PYGOBJECT_SUFFIX = '2.0'
-PYGOBJECT_SUFFIX_LONG = 'gtk-' + PYGOBJECT_SUFFIX
+GLIB_REQUIRED = get_m4_define('glib_required_version')
+
+MAJOR_VERSION = int(get_m4_define('pygobject_major_version'))
+MINOR_VERSION = int(get_m4_define('pygobject_minor_version'))
+MICRO_VERSION = int(get_m4_define('pygobject_micro_version'))
+VERSION = '%d.%d.%d' % (MAJOR_VERSION, MINOR_VERSION, MICRO_VERSION)
GLOBAL_INC += ['gobject']
GLOBAL_MACROS += [('PYGOBJECT_MAJOR_VERSION', MAJOR_VERSION),
('PYGOBJECT_MINOR_VERSION', MINOR_VERSION),
- ('PYGOBJECT_MICRO_VERSION', MICRO_VERSION)]
+ ('PYGOBJECT_MICRO_VERSION', MICRO_VERSION),
+ ('VERSION', '\\"%s\\"' % VERSION)]
-if sys.platform == 'win32':
- GLOBAL_MACROS.append(('VERSION', '\\"%s\\"' % VERSION))
-else:
- raise SystemExit("Error: distutils build only supported on windows")
+BIN_DIR = os.path.join('Scripts')
+INCLUDE_DIR = os.path.join('include', 'pygtk-%s' % PYGTK_SUFFIX)
+DEFS_DIR = os.path.join('share', 'pygobject', PYGTK_SUFFIX, 'defs')
+XSL_DIR = os.path.join('share', 'pygobject','xsl')
+HTML_DIR = os.path.join('share', 'gtk-doc', 'html', 'pygobject')
-if sys.version_info[:3] < MIN_PYTHON_VERSION:
- raise SystemExit("Python %s or higher is required, %s found" % (
- ".".join(map(str,MIN_PYTHON_VERSION)),
- ".".join(map(str,sys.version_info[:3]))))
-
-if not have_pkgconfig():
- raise SystemExit("Error, could not find pkg-config")
-
-DEFS_DIR = os.path.join('share', 'pygobject', PYGOBJECT_SUFFIX, 'defs')
-INCLUDE_DIR = os.path.join('include', 'pygtk-%s' % PYGOBJECT_SUFFIX)
class PyGObjectInstallLib(InstallLib):
def run(self):
-
# Install pygtk.pth, pygtk.py[c] and templates
self.install_pth()
self.install_pygtk()
# Modify the base installation dir
- install_dir = os.path.join(self.install_dir, PYGOBJECT_SUFFIX_LONG)
+ install_dir = os.path.join(self.install_dir, PYGTK_SUFFIX_LONG)
self.set_install_dir(install_dir)
+ # Install tests
+ self.install_tests()
+
InstallLib.run(self)
def install_pth(self):
- """Write the pygtk.pth file"""
+ '''Create the pygtk.pth file'''
file = os.path.join(self.install_dir, 'pygtk.pth')
self.mkpath(self.install_dir)
- open(file, 'w').write(PYGOBJECT_SUFFIX_LONG)
+ open(file, 'w').write(PYGTK_SUFFIX_LONG)
self.local_outputs.append(file)
self.local_inputs.append('pygtk.pth')
def install_pygtk(self):
- """install pygtk.py in the right place."""
+ '''Install pygtk.py in the right place.'''
self.copy_file('pygtk.py', self.install_dir)
pygtk = os.path.join(self.install_dir, 'pygtk.py')
self.byte_compile([pygtk])
self.local_outputs.append(pygtk)
self.local_inputs.append('pygtk.py')
+ def copy_test(self, srcfile, dstfile=None):
+ if dstfile is None:
+ dstfile = os.path.join(self.test_dir, srcfile)
+ else:
+ dstfile = os.path.join(self.test_dir, dstfile)
+
+ srcfile = os.path.join('tests', srcfile)
+
+ self.copy_file(srcfile, os.path.abspath(dstfile))
+ self.local_outputs.append(dstfile)
+ self.local_inputs.append('srcfile')
+
+ def install_tests(self):
+ self.test_dir = os.path.join(self.install_dir, 'tests', 'pygobject')
+ self.mkpath(self.test_dir)
+
+ self.copy_test('runtests-windows.py', 'runtests.py')
+ self.copy_test('compathelper.py')
+
+ for testfile in glob.glob('tests/test*.py'):
+ self.copy_test(os.path.basename(testfile))
+
+
class PyGObjectInstallData(InstallData):
def run(self):
self.add_template_option('VERSION', VERSION)
@@ -108,18 +140,46 @@ class PyGObjectInstallData(InstallData):
InstallData.run(self)
def install_templates(self):
- self.install_template('pygobject-2.0.pc.in',
- os.path.join(self.install_dir,
- 'lib', 'pkgconfig'))
+ self.install_template('pygobject-%s.pc.in' % PYGTK_SUFFIX,
+ os.path.join(self.install_dir, 'lib', 'pkgconfig'))
+
+ self.install_template('docs/xsl/fixxref.py.in',
+ os.path.join(self.install_dir, XSL_DIR))
+
class PyGObjectBuild(build):
- enable_threading = 1
+ enable_threading = True
+
PyGObjectBuild.user_options.append(('enable-threading', None,
- 'enable threading support'))
+ 'enable threading support'))
+
+
+class PyGObjectBuildScripts(build_scripts):
+ '''
+ Overrides distutils' build_script command so we can generate
+ a valid pygobject-codegen script that works on windows.
+ '''
+
+ def run(self):
+ self.mkpath(self.build_dir)
+ self.install_codegen_script()
+ build_scripts.run(self)
+
+ def install_codegen_script(self):
+ '''Create pygobject-codegen'''
+ script = ('#!/bin/sh\n\n'
+ 'codegendir=`pkg-config pygobject-%s --variable=codegendir`\n\n'
+ 'PYTHONPATH=$codegendir\n'
+ 'export PYTHONPATH\n\n'
+ 'exec pythonw.exe "$codegendir/codegen.py" "$@"\n' % PYGTK_SUFFIX)
+
+ outfile = os.path.join(self.build_dir, 'pygobject-codegen-%s' % PYGTK_SUFFIX)
+ open(outfile, 'w').write(script)
+
# glib
glib = PkgConfigExtension(name='glib._glib',
- pkc_name='glib-2.0',
+ pkc_name='glib-%s' % PYGTK_SUFFIX,
pkc_version=GLIB_REQUIRED,
pygobject_pkc=None,
include_dirs=['glib'],
@@ -136,7 +196,7 @@ glib = PkgConfigExtension(name='glib._glib',
# GObject
gobject = PkgConfigExtension(name='gobject._gobject',
- pkc_name='gobject-2.0',
+ pkc_name='gobject-%s' % PYGTK_SUFFIX,
pkc_version=GLIB_REQUIRED,
pygobject_pkc=None,
include_dirs=['glib','gi'],
@@ -154,7 +214,7 @@ gobject = PkgConfigExtension(name='gobject._gobject',
# gio
gio = TemplateExtension(name='gio',
- pkc_name='gio-2.0',
+ pkc_name='gio-%s' % PYGTK_SUFFIX,
pkc_version=GLIB_REQUIRED,
output='gio._gio',
defs='gio/gio.defs',
@@ -170,8 +230,7 @@ clibs = []
data_files = []
ext_modules = []
-#Install dsextras and codegen so that the pygtk installer
-#can find them
+#Install dsextras and codegen so that the pygtk installer can find them
py_modules = ['dsextras']
packages = ['codegen']
@@ -183,33 +242,52 @@ if glib.can_build():
#subclass
#
#So we are stuck with this ugly thing
- clibs.append((
- 'pyglib',{
- 'sources':['glib/pyglib.c'],
- 'macros':GLOBAL_MACROS,
- 'include_dirs':
- ['glib', get_python_inc()]+pkgc_get_include_dirs('glib-2.0')}))
- #this library is not installed, so probbably should not include its header
+ clibs.append(('pyglib', {'sources': ['glib/pyglib.c'],
+ 'macros': GLOBAL_MACROS,
+ 'include_dirs': ['glib', get_python_inc()] +
+ pkgc_get_include_dirs('glib-%s' % PYGTK_SUFFIX)}))
+ #this library is not installed, so probably should not include its header
#data_files.append((INCLUDE_DIR, ('glib/pyglib.h',)))
-
+
ext_modules.append(glib)
py_modules += ['glib.__init__', 'glib.option']
else:
- raise SystemExit("ERROR: Nothing to do, glib could not be found and is essential.")
+ raise SystemExit('ERROR: Nothing to do, glib could not be found and is essential.')
if gobject.can_build():
ext_modules.append(gobject)
data_files.append((INCLUDE_DIR, ('gobject/pygobject.h',)))
+ data_files.append((HTML_DIR, glob.glob('docs/html/*.html')))
+ data_files.append((HTML_DIR, ['docs/style.css']))
+ data_files.append((XSL_DIR, glob.glob('docs/xsl/*.xsl')))
py_modules += ['gobject.__init__', 'gobject.propertyhelper', 'gobject.constants']
else:
- raise SystemExit("ERROR: Nothing to do, gobject could not be found and is essential.")
+ raise SystemExit('ERROR: Nothing to do, gobject could not be found and is essential.')
if gio.can_build():
ext_modules.append(gio)
py_modules += ['gio.__init__']
data_files.append((DEFS_DIR,('gio/gio.defs', 'gio/gio-types.defs',)))
else:
- raise SystemExit("ERROR: Nothing to do, gio could not be found and is essential.")
+ raise SystemExit, 'ERROR: Nothing to do, gio could not be found and is essential.'
+
+# Build testhelper library
+testhelper = Extension(name='testhelper',
+ sources=['tests/testhelpermodule.c',
+ 'tests/test-floating.c',
+ 'tests/test-thread.c',
+ 'tests/test-unknown.c'],
+ libraries=['pyglib'] +
+ pkgc_get_libraries('glib-%s' % PYGTK_SUFFIX) +
+ pkgc_get_libraries('gobject-%s' % PYGTK_SUFFIX),
+ include_dirs=['tests', 'glib',
+ 'gobject', get_python_inc()] +
+ pkgc_get_include_dirs('glib-%s' % PYGTK_SUFFIX) +
+ pkgc_get_include_dirs('gobject-%s' % PYGTK_SUFFIX),
+ library_dirs=pkgc_get_library_dirs('glib%s' % PYGTK_SUFFIX) +
+ pkgc_get_library_dirs('gobject-%s' % PYGTK_SUFFIX))
+
+ext_modules.append(testhelper)
# Threading support
if '--disable-threading' in sys.argv:
@@ -221,13 +299,13 @@ else:
try:
import thread
except ImportError:
- print "Warning: Could not import thread module, disabling threading"
+ print ('* Could not import thread module, disabling threading')
enable_threading = False
else:
enable_threading = True
if enable_threading:
- name = 'gthread-2.0'
+ name = 'gthread-%s' % PYGTK_SUFFIX
for module in ext_modules:
raw = getoutput('pkg-config --libs-only-l %s' % name)
for arg in raw.split():
@@ -244,29 +322,30 @@ if enable_threading:
else:
GLOBAL_MACROS.append(('DISABLE_THREADING', 1))
+doclines = __doc__.split('\n')
+options = {'bdist_wininst': {'install_script': 'pygobject_postinstall.py',
+ 'user_access_control': 'auto'}}
-doclines = __doc__.split("\n")
-
-options = {"bdist_wininst": {"install_script": "pygobject_postinstall.py"}}
-
-setup(name="pygobject",
+setup(name='pygobject',
url='http://www.pygtk.org/',
version=VERSION,
license='LGPL',
- platforms=['yes'],
- maintainer="Johan Dahlin",
- maintainer_email="johan@gnome.org",
- description = doclines[0],
- long_description = "\n".join(doclines[2:]),
+ platforms=['MS Windows'],
+ maintainer='Johan Dahlin',
+ maintainer_email='johan@gnome.org',
+ description=doclines[0],
+ long_description='\n'.join(doclines[2:]),
+ provides=['codegen', 'dsextras', 'gio', 'glib', 'gobject'],
py_modules=py_modules,
packages=packages,
ext_modules=ext_modules,
libraries=clibs,
data_files=data_files,
- scripts = ["pygobject_postinstall.py"],
+ scripts=['pygobject_postinstall.py'],
options=options,
cmdclass={'install_lib': PyGObjectInstallLib,
'install_data': PyGObjectInstallData,
+ 'build_scripts': PyGObjectBuildScripts,
'build_clib' : build_clib,
'build_ext': BuildExt,
'build': PyGObjectBuild})
diff --git a/tests/.gitignore b/tests/.gitignore
deleted file mode 100644
index d2f2b41f..00000000
--- a/tests/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-file.txt~
-stream.txt
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 77bc0202..bad15f0b 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -40,7 +40,10 @@ GIMarshallingTests-1.0.gir: libgimarshallingtests.la Makefile
GIMarshallingTests-1.0.typelib: GIMarshallingTests-1.0.gir Makefile
$(AM_V_GEN) g-ir-compiler $< -o $@
-CLEANFILES += Regress-1.0.gir Regress-1.0.typelib GIMarshallingTests-1.0.gir GIMarshallingTests-1.0.typelib
+gschemas.compiled: org.gnome.test.gschema.xml
+ glib-compile-schemas --targetdir=. --schema-file=$<
+
+CLEANFILES += Regress-1.0.gir Regress-1.0.typelib GIMarshallingTests-1.0.gir GIMarshallingTests-1.0.typelib gschemas.compiled
endif
noinst_LTLIBRARIES += testhelper.la
@@ -65,7 +68,7 @@ testhelper.la: $(testhelper_la_OBJECTS) $(testhelper_la_DEPENDENCIES)
all: $(LTLIBRARIES:.la=.so)
-TEST_FILES = \
+TEST_FILES_STATIC = \
test_gobject.py \
test_interface.py \
test_mainloop.py \
@@ -78,7 +81,7 @@ TEST_FILES = \
test_uris.py
if BUILD_GIO
-TEST_FILES += \
+TEST_FILES_GIO = \
test_gio.py \
test_gresolver.py \
test_gsocket.py \
@@ -87,9 +90,10 @@ TEST_FILES += \
endif
if ENABLE_INTROSPECTION
-TEST_FILES += \
+TEST_FILES_GI = \
test_everything.py \
test_gi.py \
+ test_gdbus.py \
test_overrides.py
endif
@@ -99,19 +103,35 @@ EXTRA_DIST = \
testmodule.py \
test-floating.h \
test-thread.h \
- test-unknown.h
+ test-unknown.h \
+ org.gnome.test.gschema.xml
-EXTRA_DIST += $(TEST_FILES)
+EXTRA_DIST += $(TEST_FILES_STATIC) $(TEST_FILES_GI) $(TEST_FILES_GIO)
clean-local:
rm -f $(LTLIBRARIES:.la=.so) file.txt~
-
-check-local: $(LTLIBRARIES:.la=.so) Regress-1.0.typelib GIMarshallingTests-1.0.typelib
- TEST_FILES="$(TEST_FILES)" PYTHONPATH=$(top_builddir):$(top_builddir)/tests:$${PYTHONPATH:+:$$PYTHONPATH} LD_LIBRARY_PATH=$(builddir)/.libs:$$LD_LIBRARY_PATH GI_TYPELIB_PATH=$(builddir) $(EXEC_NAME) $(PYTHON) $(srcdir)/runtests.py
+DBUS_LAUNCH=$(shell which dbus-launch)
+RUN_TESTS_ENV_VARS= \
+ PYTHONPATH=$(top_builddir):$(top_builddir)/tests:$${PYTHONPATH:+:$$PYTHONPATH} \
+ LD_LIBRARY_PATH=$(builddir)/.libs:$$LD_LIBRARY_PATH \
+ GI_TYPELIB_PATH=$(builddir):$$GI_TYPELIB_PATH \
+ XDG_DATA_DIRS=$$XDG_DATA_DIRS:/usr/share \
+ TESTS_BUILDDIR=$(builddir)
+RUN_TESTS_LAUNCH=$(RUN_TESTS_ENV_VARS) $(DBUS_LAUNCH) $(EXEC_NAME) $(PYTHON) $(srcdir)/runtests.py
+
+# run tests in separately to avoid loading static and introspection bindings in the same process
+check-local: $(LTLIBRARIES:.la=.so) Regress-1.0.typelib GIMarshallingTests-1.0.typelib gschemas.compiled
+ TEST_FILES="$(TEST_FILES_STATIC)" $(RUN_TESTS_LAUNCH)
+ TEST_FILES="$(TEST_FILES_GI)" $(RUN_TESTS_LAUNCH)
+if BUILD_GIO
+ TEST_FILES="$(TEST_FILES_GIO)" $(RUN_TESTS_LAUNCH)
+endif
check.gdb:
EXEC_NAME="gdb --args" $(MAKE) check
check.valgrind:
- EXEC_NAME="valgrind" G_SLICE=always-malloc G_DEBUG=gc-friendly $(MAKE) check
+ EXEC_NAME="valgrind --suppressions=python.supp" G_SLICE=always-malloc G_DEBUG=gc-friendly $(MAKE) check
+
+-include $(top_srcdir)/git.mk
diff --git a/tests/compathelper.py b/tests/compathelper.py
index 754285ca..24657470 100644
--- a/tests/compathelper.py
+++ b/tests/compathelper.py
@@ -44,7 +44,26 @@ if sys.version_info >= (3, 0):
'''
_bytes = lambda s: s.encode()
+
+ '''
+ for tests that need to write to intefaces that take unicode in
+ python 2
+
+ python 3 strings are unicode encoded as UTF-8 so the unicode object
+ doesn't exist
+
+ python 2 differs between a string an unicode string and you must specify
+ an encoding. This macro will specify UTF-8 in python 2
+
+ any tests that need to use unicode should do this
+
+ from compathelper import _unicode
+ unicode_string = _unicode('this is a unicode string')
+ '''
+
+ _unicode = lambda s: str(s)
else:
_long = long
_basestring = basestring
_bytes = str
+ _unicode = lambda s: unicode(s, 'UTF-8')
diff --git a/tests/org.gnome.test.gschema.xml b/tests/org.gnome.test.gschema.xml
new file mode 100644
index 00000000..221b87a5
--- /dev/null
+++ b/tests/org.gnome.test.gschema.xml
@@ -0,0 +1,25 @@
+<schemalist>
+ <schema id="org.gnome.test" path="/tests/">
+ <key name="test-boolean" type="b">
+ <default>true</default>
+ </key>
+ <key name="test-string" type="s">
+ <default>"Hello"</default>
+ </key>
+ <key name="test-tuple" type="(ii)">
+ <default>(1,2)</default>
+ </key>
+ <key name="test-array" type="ai">
+ <default>[1,2]</default>
+ </key>
+ </schema>
+
+ <schema id="org.gnome.nopathtest">
+ <key name="np-int" type="i">
+ <default>42</default>
+ </key>
+ </schema>
+
+ <schema id="org.gnome.empty" path="/tests/">
+ </schema>
+</schemalist>
diff --git a/tests/python.supp b/tests/python.supp
new file mode 100644
index 00000000..f9d4eed5
--- /dev/null
+++ b/tests/python.supp
@@ -0,0 +1,387 @@
+#
+# This is a valgrind suppression file that should be used when using valgrind.
+#
+# Here's an example of running valgrind:
+#
+# cd python/dist/src
+# valgrind --tool=memcheck --suppressions=Misc/valgrind-python.supp \
+# ./python -E -tt ./Lib/test/regrtest.py -u bsddb,network
+#
+# You must edit Objects/obmalloc.c and uncomment Py_USING_MEMORY_DEBUGGER
+# to use the preferred suppressions with Py_ADDRESS_IN_RANGE.
+#
+# If you do not want to recompile Python, you can uncomment
+# suppressions for PyObject_Free and PyObject_Realloc.
+#
+# See Misc/README.valgrind for more information.
+
+# all tool names: Addrcheck,Memcheck,cachegrind,helgrind,massif
+{
+ ADDRESS_IN_RANGE/Invalid read of size 4
+ Memcheck:Addr4
+ fun:Py_ADDRESS_IN_RANGE
+}
+
+{
+ ADDRESS_IN_RANGE/Invalid read of size 4
+ Memcheck:Value4
+ fun:Py_ADDRESS_IN_RANGE
+}
+
+{
+ ADDRESS_IN_RANGE/Invalid read of size 8 (x86_64 aka amd64)
+ Memcheck:Value8
+ fun:Py_ADDRESS_IN_RANGE
+}
+
+{
+ ADDRESS_IN_RANGE/Conditional jump or move depends on uninitialised value
+ Memcheck:Cond
+ fun:Py_ADDRESS_IN_RANGE
+}
+
+#
+# Leaks (including possible leaks)
+# Hmmm, I wonder if this masks some real leaks. I think it does.
+# Will need to fix that.
+#
+
+{
+ Suppress leaking the GIL. Happens once per process, see comment in ceval.c.
+ Memcheck:Leak
+ fun:malloc
+ fun:PyThread_allocate_lock
+ fun:PyEval_InitThreads
+}
+
+{
+ Suppress leaking the GIL after a fork.
+ Memcheck:Leak
+ fun:malloc
+ fun:PyThread_allocate_lock
+ fun:PyEval_ReInitThreads
+}
+
+{
+ Suppress leaking the autoTLSkey. This looks like it shouldn't leak though.
+ Memcheck:Leak
+ fun:malloc
+ fun:PyThread_create_key
+ fun:_PyGILState_Init
+ fun:Py_InitializeEx
+ fun:Py_Main
+}
+
+{
+ Hmmm, is this a real leak or like the GIL?
+ Memcheck:Leak
+ fun:malloc
+ fun:PyThread_ReInitTLS
+}
+
+{
+ Handle PyMalloc confusing valgrind (possibly leaked)
+ Memcheck:Leak
+ fun:realloc
+ fun:_PyObject_GC_Resize
+ fun:COMMENT_THIS_LINE_TO_DISABLE_LEAK_WARNING
+}
+
+{
+ Handle PyMalloc confusing valgrind (possibly leaked)
+ Memcheck:Leak
+ fun:malloc
+ fun:_PyObject_GC_New
+ fun:COMMENT_THIS_LINE_TO_DISABLE_LEAK_WARNING
+}
+
+{
+ Handle PyMalloc confusing valgrind (possibly leaked)
+ Memcheck:Leak
+ fun:malloc
+ fun:_PyObject_GC_NewVar
+ fun:COMMENT_THIS_LINE_TO_DISABLE_LEAK_WARNING
+}
+
+#
+# Non-python specific leaks
+#
+
+{
+ Handle pthread issue (possibly leaked)
+ Memcheck:Leak
+ fun:calloc
+ fun:allocate_dtv
+ fun:_dl_allocate_tls_storage
+ fun:_dl_allocate_tls
+}
+
+{
+ Handle pthread issue (possibly leaked)
+ Memcheck:Leak
+ fun:memalign
+ fun:_dl_allocate_tls_storage
+ fun:_dl_allocate_tls
+}
+
+{
+ ADDRESS_IN_RANGE/Invalid read of size 4
+ Memcheck:Addr4
+ fun:PyObject_Free
+}
+
+{
+ ADDRESS_IN_RANGE/Invalid read of size 4
+ Memcheck:Value4
+ fun:PyObject_Free
+}
+
+{
+ ADDRESS_IN_RANGE/Conditional jump or move depends on uninitialised value
+ Memcheck:Cond
+ fun:PyObject_Free
+}
+
+{
+ ADDRESS_IN_RANGE/Invalid read of size 4
+ Memcheck:Addr4
+ fun:PyObject_Realloc
+}
+
+{
+ ADDRESS_IN_RANGE/Invalid read of size 4
+ Memcheck:Value4
+ fun:PyObject_Realloc
+}
+
+{
+ ADDRESS_IN_RANGE/Conditional jump or move depends on uninitialised value
+ Memcheck:Cond
+ fun:PyObject_Realloc
+}
+
+{
+ Generic ubuntu ld problems
+ Memcheck:Addr8
+ obj:/lib/ld-2.4.so
+ obj:/lib/ld-2.4.so
+ obj:/lib/ld-2.4.so
+ obj:/lib/ld-2.4.so
+}
+
+{
+ Generic gentoo ld problems
+ Memcheck:Cond
+ obj:/lib/ld-2.3.4.so
+ obj:/lib/ld-2.3.4.so
+ obj:/lib/ld-2.3.4.so
+ obj:/lib/ld-2.3.4.so
+}
+
+{
+ DBM problems, see test_dbm
+ Memcheck:Param
+ write(buf)
+ fun:write
+ obj:/usr/lib/libdb1.so.2
+ obj:/usr/lib/libdb1.so.2
+ obj:/usr/lib/libdb1.so.2
+ obj:/usr/lib/libdb1.so.2
+ fun:dbm_close
+}
+
+{
+ DBM problems, see test_dbm
+ Memcheck:Value8
+ fun:memmove
+ obj:/usr/lib/libdb1.so.2
+ obj:/usr/lib/libdb1.so.2
+ obj:/usr/lib/libdb1.so.2
+ obj:/usr/lib/libdb1.so.2
+ fun:dbm_store
+ fun:dbm_ass_sub
+}
+
+{
+ DBM problems, see test_dbm
+ Memcheck:Cond
+ obj:/usr/lib/libdb1.so.2
+ obj:/usr/lib/libdb1.so.2
+ obj:/usr/lib/libdb1.so.2
+ fun:dbm_store
+ fun:dbm_ass_sub
+}
+
+{
+ DBM problems, see test_dbm
+ Memcheck:Cond
+ fun:memmove
+ obj:/usr/lib/libdb1.so.2
+ obj:/usr/lib/libdb1.so.2
+ obj:/usr/lib/libdb1.so.2
+ obj:/usr/lib/libdb1.so.2
+ fun:dbm_store
+ fun:dbm_ass_sub
+}
+
+{
+ GDBM problems, see test_gdbm
+ Memcheck:Param
+ write(buf)
+ fun:write
+ fun:gdbm_open
+
+}
+
+{
+ ZLIB problems, see test_gzip
+ Memcheck:Cond
+ obj:/lib/libz.so.1.2.3
+ obj:/lib/libz.so.1.2.3
+ fun:deflate
+}
+
+{
+ Avoid problems w/readline doing a putenv and leaking on exit
+ Memcheck:Leak
+ fun:malloc
+ fun:xmalloc
+ fun:sh_set_lines_and_columns
+ fun:_rl_get_screen_size
+ fun:_rl_init_terminal_io
+ obj:/lib/libreadline.so.4.3
+ fun:rl_initialize
+}
+
+
+{
+ somewhere in SSL stuff
+ Memcheck:Cond
+ fun:memset
+}
+{
+ somewhere in SSL stuff
+ Memcheck:Value4
+ fun:memset
+}
+
+{
+ somewhere in SSL stuff
+ Memcheck:Cond
+ fun:MD5_Update
+}
+
+{
+ somewhere in SSL stuff
+ Memcheck:Value4
+ fun:MD5_Update
+}
+
+#
+# All of these problems come from using test_socket_ssl
+#
+{
+ from test_socket_ssl
+ Memcheck:Cond
+ fun:BN_bin2bn
+}
+
+{
+ from test_socket_ssl
+ Memcheck:Cond
+ fun:BN_num_bits_word
+}
+
+{
+ from test_socket_ssl
+ Memcheck:Value4
+ fun:BN_num_bits_word
+}
+
+{
+ from test_socket_ssl
+ Memcheck:Cond
+ fun:BN_mod_exp_mont_word
+}
+
+{
+ from test_socket_ssl
+ Memcheck:Cond
+ fun:BN_mod_exp_mont
+}
+
+{
+ from test_socket_ssl
+ Memcheck:Param
+ write(buf)
+ fun:write
+ obj:/usr/lib/libcrypto.so.0.9.7
+}
+
+{
+ from test_socket_ssl
+ Memcheck:Cond
+ fun:RSA_verify
+}
+
+{
+ from test_socket_ssl
+ Memcheck:Value4
+ fun:RSA_verify
+}
+
+{
+ from test_socket_ssl
+ Memcheck:Value4
+ fun:DES_set_key_unchecked
+}
+
+{
+ from test_socket_ssl
+ Memcheck:Value4
+ fun:DES_encrypt2
+}
+
+{
+ from test_socket_ssl
+ Memcheck:Cond
+ obj:/usr/lib/libssl.so.0.9.7
+}
+
+{
+ from test_socket_ssl
+ Memcheck:Value4
+ obj:/usr/lib/libssl.so.0.9.7
+}
+
+{
+ from test_socket_ssl
+ Memcheck:Cond
+ fun:BUF_MEM_grow_clean
+}
+
+{
+ from test_socket_ssl
+ Memcheck:Cond
+ fun:memcpy
+ fun:ssl3_read_bytes
+}
+
+{
+ from test_socket_ssl
+ Memcheck:Cond
+ fun:SHA1_Update
+}
+
+{
+ from test_socket_ssl
+ Memcheck:Value4
+ fun:SHA1_Update
+}
+
+{
+ convertitem
+ Memcheck:Addr4
+ fun:convertitem
+}
+
diff --git a/tests/runtests-windows.py b/tests/runtests-windows.py
new file mode 100644
index 00000000..90154b42
--- /dev/null
+++ b/tests/runtests-windows.py
@@ -0,0 +1,47 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+
+import os
+import sys
+import glob
+import unittest
+
+os.environ['PYGTK_USE_GIL_STATE_API'] = ''
+sys.path.insert(0, os.path.dirname(__file__))
+sys.argv.append('--g-fatal-warnings')
+
+import gobject
+gobject.threads_init()
+
+
+SKIP_FILES = ['runtests',
+ 'test_gio', # python crash
+ 'test_gresolver', # python crash
+ 'test_gsocket', # blocks on test_socket_condition_wait
+ 'test_mainloop', # no os.fork on windows
+ 'test_subprocess'] # blocks on testChildWatch
+
+
+if __name__ == '__main__':
+ testdir = os.path.split(os.path.abspath(__file__))[0]
+ os.chdir(testdir)
+
+ def gettestnames():
+ files = glob.glob('*.py')
+ names = map(lambda x: x[:-3], files)
+ map(names.remove, SKIP_FILES)
+ return names
+
+ suite = unittest.TestSuite()
+ loader = unittest.TestLoader()
+
+ for name in gettestnames():
+ try:
+ suite.addTest(loader.loadTestsFromName(name))
+ except Exception, e:
+ print 'Could not load %s: %s' % (name, e)
+
+ testRunner = unittest.TextTestRunner()
+ testRunner.verbosity = 2
+ testRunner.run(suite)
diff --git a/tests/runtests.py b/tests/runtests.py
index d99f0cc7..2bb86376 100644
--- a/tests/runtests.py
+++ b/tests/runtests.py
@@ -2,10 +2,14 @@
import os
import glob
+import sys
import unittest
+# force untranslated messages, as we check for them in some tests
+os.environ['LC_MESSAGES'] = 'C'
+
# Load tests.
if 'TEST_NAMES' in os.environ:
names = os.environ['TEST_NAMES'].split()
@@ -24,5 +28,7 @@ suite = loader.loadTestsFromNames(names)
# Run tests.
runner = unittest.TextTestRunner(verbosity=2)
-runner.run(suite)
+result = runner.run(suite)
+if not result.wasSuccessful():
+ sys.exit(1) # exit code so "make check" reports error
diff --git a/tests/test-floating.c b/tests/test-floating.c
index 918a42d0..8e8ba5de 100644
--- a/tests/test-floating.c
+++ b/tests/test-floating.c
@@ -93,3 +93,69 @@ test_floating_without_sink_func_init (TestFloatingWithoutSinkFunc *self)
{
}
+/* TestOwnedByLibrary */
+
+G_DEFINE_TYPE(TestOwnedByLibrary, test_owned_by_library, G_TYPE_OBJECT)
+
+static GSList *obl_instance_list = NULL;
+
+static void
+test_owned_by_library_class_init (TestOwnedByLibraryClass *klass)
+{
+}
+
+static void
+test_owned_by_library_init (TestOwnedByLibrary *self)
+{
+ g_object_ref (self);
+ obl_instance_list = g_slist_prepend (obl_instance_list, self);
+}
+
+void
+test_owned_by_library_release (TestOwnedByLibrary *self)
+{
+ obl_instance_list = g_slist_remove (obl_instance_list, self);
+ g_object_unref (self);
+}
+
+GSList *
+test_owned_by_library_get_instance_list (void)
+{
+ return obl_instance_list;
+}
+
+/* TestFloatingAndSunk
+ * This object is mimicking the GtkWindow behaviour, ie a GInitiallyUnowned subclass
+ * whose floating reference has already been sunk when g_object_new() returns it.
+ * The reference is already sunk because the instance is already owned by the instance
+ * list.
+ */
+
+G_DEFINE_TYPE(TestFloatingAndSunk, test_floating_and_sunk, G_TYPE_INITIALLY_UNOWNED)
+
+static GSList *fas_instance_list = NULL;
+
+static void
+test_floating_and_sunk_class_init (TestFloatingAndSunkClass *klass)
+{
+}
+
+static void
+test_floating_and_sunk_init (TestFloatingAndSunk *self)
+{
+ g_object_ref_sink (self);
+ fas_instance_list = g_slist_prepend (fas_instance_list, self);
+}
+
+void
+test_floating_and_sunk_release (TestFloatingAndSunk *self)
+{
+ fas_instance_list = g_slist_remove (fas_instance_list, self);
+ g_object_unref (self);
+}
+
+GSList *
+test_floating_and_sunk_get_instance_list (void)
+{
+ return fas_instance_list;
+}
diff --git a/tests/test-floating.h b/tests/test-floating.h
index e53df32f..bf4e1019 100644
--- a/tests/test-floating.h
+++ b/tests/test-floating.h
@@ -58,3 +58,44 @@ typedef struct {
GType test_floating_without_sink_func_get_type (void);
+/* TestOwnedByLibrary */
+
+typedef struct {
+ GObject parent;
+} TestOwnedByLibrary;
+
+typedef struct {
+ GObjectClass parent_class;
+} TestOwnedByLibraryClass;
+
+#define TEST_TYPE_OWNED_BY_LIBRARY (test_owned_by_library_get_type())
+#define TEST_OWNED_BY_LIBRARY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TEST_TYPE_OWNED_BY_LIBRARY, TestOwnedByLibrary))
+#define TEST_OWNED_BY_LIBRARY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TEST_TYPE_OWNED_BY_LIBRARY, TestOwnedByLibraryClass))
+#define TEST_IS_OWNED_BY_LIBRARY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TEST_TYPE_OWNED_BY_LIBRARY))
+#define TEST_IS_OWNED_BY_LIBRARY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), TEST_TYPE_OWNED_BY_LIBRARY))
+#define TEST_OWNED_BY_LIBRARY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), TEST_TYPE_OWNED_BY_LIBRARY, TestOwnedByLibraryClass))
+
+GType test_owned_by_library_get_type (void);
+void test_owned_by_library_release (TestOwnedByLibrary *self);
+GSList *test_owned_by_library_get_instance_list (void);
+
+/* TestFloatingAndSunk */
+
+typedef struct {
+ GInitiallyUnowned parent;
+} TestFloatingAndSunk;
+
+typedef struct {
+ GInitiallyUnownedClass parent_class;
+} TestFloatingAndSunkClass;
+
+#define TEST_TYPE_FLOATING_AND_SUNK (test_floating_and_sunk_get_type())
+#define TEST_FLOATING_AND_SUNK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TEST_TYPE_FLOATING_AND_SUNK, TestFloatingAndSunk))
+#define TEST_FLOATING_AND_SUNK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TEST_TYPE_FLOATING_AND_SUNK, TestFloatingAndSunkClass))
+#define TEST_IS_FLOATING_AND_SUNK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TEST_TYPE_FLOATING_AND_SUNK))
+#define TEST_IS_FLOATING_AND_SUNK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), TEST_TYPE_FLOATING_AND_SUNK))
+#define TEST_FLOATING_AND_SUNK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), TEST_TYPE_FLOATING_AND_SUNK, TestFloatingAndSunkClass))
+
+GType test_floating_and_sunk_get_type (void);
+void test_floating_and_sunk_release (TestFloatingAndSunk *self);
+GSList *test_floating_and_sunk_get_instance_list (void);
diff --git a/tests/test_everything.py b/tests/test_everything.py
index 6e77a3dc..74d917a0 100644
--- a/tests/test_everything.py
+++ b/tests/test_everything.py
@@ -1,4 +1,5 @@
# -*- Mode: Python; py-indent-offset: 4 -*-
+# coding=utf-8
# vim: tabstop=4 shiftwidth=4 expandtab
import unittest
@@ -13,6 +14,13 @@ from gi.repository import GObject
from gi.repository import GLib
from gi.repository import Regress as Everything
+if sys.version_info < (3, 0):
+ UNICHAR = "\xe2\x99\xa5"
+ PY2_UNICODE_UNICHAR = unicode(UNICHAR, 'UTF-8')
+else:
+ UNICHAR = "♥"
+
+
class TestEverything(unittest.TestCase):
def test_cairo_context(self):
@@ -48,8 +56,25 @@ class TestEverything(unittest.TestCase):
self.assertEquals(surface.get_width(), 10)
self.assertEquals(surface.get_height(), 10)
+ def test_unichar(self):
+ self.assertEquals("c", Everything.test_unichar("c"))
+
+ if sys.version_info < (3, 0):
+ self.assertEquals(UNICHAR, Everything.test_unichar(PY2_UNICODE_UNICHAR))
+ self.assertEquals(UNICHAR, Everything.test_unichar(UNICHAR))
+ self.assertRaises(TypeError, Everything.test_unichar, "")
+ self.assertRaises(TypeError, Everything.test_unichar, "morethanonechar")
+
+
def test_floating(self):
- Everything.TestFloating()
+ e = Everything.TestFloating()
+ self.assertEquals(e.__grefcount__, 1)
+
+ e = GObject.new(Everything.TestFloating)
+ self.assertEquals(e.__grefcount__, 1)
+
+ e = Everything.TestFloating.new()
+ self.assertEquals(e.__grefcount__, 1)
def test_caller_allocates(self):
struct_a = Everything.TestStructA()
@@ -113,6 +138,24 @@ class TestEverything(unittest.TestCase):
gtype = Everything.test_gtype(ARegisteredClass)
self.assertEquals(ARegisteredClass.__gtype__, gtype)
self.assertRaises(TypeError, Everything.test_gtype, 'ARegisteredClass')
+
+ def test_dir(self):
+ attr_list = dir(Everything)
+
+ # test that typelib attributes are listed
+ self.assertTrue('TestStructA' in attr_list)
+
+ # test that class attributes and methods are listed
+ self.assertTrue('__class__' in attr_list)
+ self.assertTrue('__dir__' in attr_list)
+ self.assertTrue('__repr__' in attr_list)
+
+ # test that instance members are listed
+ self.assertTrue('_namespace' in attr_list)
+ self.assertTrue('_version' in attr_list)
+
+ # test that there are no duplicates returned
+ self.assertEqual(len(attr_list), len(set(attr_list)))
class TestNullableArgs(unittest.TestCase):
def test_in_nullable_hash(self):
@@ -356,3 +399,70 @@ class TestProperties(unittest.TestCase):
self.assertTrue(isinstance(object_.props.boxed, Everything.TestBoxed))
self.assertEquals(object_.props.boxed.some_int8, 42)
+
+class TestTortureProfile(unittest.TestCase):
+ def test_torture_profile(self):
+ import time
+ total_time = 0
+ print("")
+ object_ = Everything.TestObj()
+ sys.stdout.write("\ttorture test 1 (10000 iterations): ")
+
+ start_time = time.clock()
+ for i in range(10000):
+ (y,z,q) = object_.torture_signature_0(5000,
+ "Torture Test 1",
+ 12345)
+
+ end_time = time.clock()
+ delta_time = end_time - start_time
+ total_time += delta_time
+ print("%f secs" % delta_time)
+
+ sys.stdout.write("\ttorture test 2 (10000 iterations): ")
+
+ start_time = time.clock()
+ for i in range(10000):
+ (y,z,q) = Everything.TestObj().torture_signature_0(5000,
+ "Torture Test 2",
+ 12345)
+
+ end_time = time.clock()
+ delta_time = end_time - start_time
+ total_time += delta_time
+ print("%f secs" % delta_time)
+
+
+ sys.stdout.write("\ttorture test 3 (10000 iterations): ")
+ start_time = time.clock()
+ for i in range(10000):
+ try:
+ (y,z,q) = object_.torture_signature_1(5000,
+ "Torture Test 3",
+ 12345)
+ except:
+ pass
+ end_time = time.clock()
+ delta_time = end_time - start_time
+ total_time += delta_time
+ print("%f secs" % delta_time)
+
+ sys.stdout.write("\ttorture test 4 (10000 iterations): ")
+ def callback(userdata):
+ pass
+
+ userdata = [1,2,3,4]
+ start_time = time.clock()
+ for i in range(10000):
+ (y,z,q) = Everything.test_torture_signature_2(5000,
+ callback,
+ userdata,
+ "Torture Test 4",
+ 12345)
+ end_time = time.clock()
+ delta_time = end_time - start_time
+ total_time += delta_time
+ print("%f secs" % delta_time)
+ print("\t====")
+ print("\tTotal: %f sec" % total_time)
+
diff --git a/tests/test_gdbus.py b/tests/test_gdbus.py
new file mode 100644
index 00000000..a9442fea
--- /dev/null
+++ b/tests/test_gdbus.py
@@ -0,0 +1,218 @@
+# -*- Mode: Python; py-indent-offset: 4 -*-
+# vim: tabstop=4 shiftwidth=4 expandtab
+
+import unittest
+
+import sys
+sys.path.insert(0, "../")
+
+import gobject
+from gi.repository import GLib
+from gi.repository import Gio
+
+class TestGDBusClient(unittest.TestCase):
+ def setUp(self):
+ self.bus = Gio.bus_get_sync(Gio.BusType.SESSION, None)
+
+ self.dbus_proxy = Gio.DBusProxy.new_sync(self.bus,
+ Gio.DBusProxyFlags.NONE, None,
+ 'org.freedesktop.DBus',
+ '/org/freedesktop/DBus',
+ 'org.freedesktop.DBus', None)
+
+ def test_native_calls_sync(self):
+ result = self.dbus_proxy.call_sync('ListNames', None,
+ Gio.DBusCallFlags.NO_AUTO_START, 500, None)
+ self.assertTrue(isinstance(result, GLib.Variant))
+ result = result.unpack()[0] # result is always a tuple
+ self.assertTrue(len(result) > 1)
+ self.assertTrue('org.freedesktop.DBus' in result)
+
+ result = self.dbus_proxy.call_sync('GetNameOwner',
+ GLib.Variant('(s)', ('org.freedesktop.DBus',)),
+ Gio.DBusCallFlags.NO_AUTO_START, 500, None)
+ self.assertTrue(isinstance(result, GLib.Variant))
+ self.assertEqual(type(result.unpack()[0]), type(''))
+
+ def test_native_calls_sync_errors(self):
+ # error case: invalid argument types
+ try:
+ self.dbus_proxy.call_sync('GetConnectionUnixProcessID', None,
+ Gio.DBusCallFlags.NO_AUTO_START, 500, None)
+ self.fail('call with invalid arguments should raise an exception')
+ except Exception:
+ etype, e = sys.exc_info()[:2]
+ self.assertTrue('InvalidArgs' in str(e))
+
+ # error case: invalid argument
+ try:
+ self.dbus_proxy.call_sync('GetConnectionUnixProcessID',
+ GLib.Variant('(s)', (' unknown',)),
+ Gio.DBusCallFlags.NO_AUTO_START, 500, None)
+ self.fail('call with invalid arguments should raise an exception')
+ except Exception:
+ etype, e = sys.exc_info()[:2]
+
+ self.assertTrue('NameHasNoOwner' in str(e))
+
+ # error case: unknown method
+ try:
+ self.dbus_proxy.call_sync('UnknownMethod', None,
+ Gio.DBusCallFlags.NO_AUTO_START, 500, None)
+ self.fail('call for unknown method should raise an exception')
+ except Exception:
+ etype, e = sys.exc_info()[:2]
+
+ self.assertTrue('UnknownMethod' in str(e))
+
+ def test_native_calls_async(self):
+ def call_done(obj, result, user_data):
+ user_data['result'] = obj.call_finish(result)
+ user_data['main_loop'].quit()
+
+ main_loop = gobject.MainLoop()
+ data = {'main_loop': main_loop}
+ self.dbus_proxy.call('ListNames', None,
+ Gio.DBusCallFlags.NO_AUTO_START, 500, None,
+ call_done, data)
+ main_loop.run()
+
+ self.assertTrue(isinstance(data['result'], GLib.Variant))
+ result = data['result'].unpack()[0] # result is always a tuple
+ self.assertTrue(len(result) > 1)
+ self.assertTrue('org.freedesktop.DBus' in result)
+
+ def test_native_calls_async_errors(self):
+ def call_done(obj, result, user_data):
+ try:
+ obj.call_finish(result)
+ self.fail('call_finish() for unknown method should raise an exception')
+ except Exception:
+ etype, e = sys.exc_info()[:2]
+
+ self.assertTrue('UnknownMethod' in str(e))
+ finally:
+ user_data['main_loop'].quit()
+
+ main_loop = gobject.MainLoop()
+ data = {'main_loop': main_loop}
+ self.dbus_proxy.call('UnknownMethod', None,
+ Gio.DBusCallFlags.NO_AUTO_START, 500, None, call_done, data)
+ main_loop.run()
+
+ def test_python_calls_sync(self):
+ # single value return tuples get unboxed to the one element
+ result = self.dbus_proxy.ListNames('()')
+ self.assertTrue(isinstance(result, list))
+ self.assertTrue(len(result) > 1)
+ self.assertTrue('org.freedesktop.DBus' in result)
+
+ result = self.dbus_proxy.GetNameOwner('(s)', 'org.freedesktop.DBus')
+ self.assertEqual(type(result), type(''))
+
+ # empty return tuples get unboxed to None
+ self.assertEqual(self.dbus_proxy.ReloadConfig('()'), None)
+
+ # multiple return values remain a tuple; unfortunately D-BUS itself
+ # does not have any method returning multiple results, so try talking
+ # to notification-daemon (and don't fail the test if it does not exist)
+ try:
+ notification_daemon = Gio.DBusProxy.new_sync(self.bus,
+ Gio.DBusProxyFlags.NONE, None,
+ 'org.freedesktop.Notifications',
+ '/org/freedesktop/Notifications',
+ 'org.freedesktop.Notifications', None)
+ result = notification_daemon.GetServerInformation('()')
+ self.assertTrue(isinstance(result, tuple))
+ self.assertEqual(len(result), 4)
+ for i in result:
+ self.assertEqual(type(i), type(''))
+ except Exception:
+ etype, e = sys.exc_info()[:2]
+
+ if 'Error.ServiceUnknown' not in str(e):
+ raise
+
+ # test keyword argument; timeout=0 will fail immediately
+ try:
+ self.dbus_proxy.GetConnectionUnixProcessID('()', timeout=0)
+ self.fail('call with timeout=0 should raise an exception')
+ except Exception:
+ etype, e = sys.exc_info()[:2]
+
+ self.assertTrue('Timeout' in str(e), str(e))
+
+ def test_python_calls_sync_noargs(self):
+ # methods without arguments don't need an explicit signature
+ result = self.dbus_proxy.ListNames()
+ self.assertTrue(isinstance(result, list))
+ self.assertTrue(len(result) > 1)
+ self.assertTrue('org.freedesktop.DBus' in result)
+
+ def test_python_calls_sync_errors(self):
+ # error case: invalid argument types
+ try:
+ self.dbus_proxy.GetConnectionUnixProcessID('()')
+ self.fail('call with invalid arguments should raise an exception')
+ except Exception:
+ etype, e = sys.exc_info()[:2]
+
+ self.assertTrue('InvalidArgs' in str(e), str(e))
+
+ try:
+ self.dbus_proxy.GetConnectionUnixProcessID(None, 'foo')
+ self.fail('call with None signature should raise an exception')
+ except TypeError as e:
+ self.assertTrue('signature' in str(e), str(e))
+
+ def test_python_calls_async(self):
+ def call_done(obj, result, user_data):
+ user_data['result'] = result
+ user_data['main_loop'].quit()
+
+ main_loop = gobject.MainLoop()
+ data = {'main_loop': main_loop}
+ self.dbus_proxy.ListNames('()', result_handler=call_done,
+ user_data=data)
+ main_loop.run()
+
+ result = data['result']
+ self.assertEqual(type(result), type([]))
+ self.assertTrue(len(result) > 1)
+ self.assertTrue('org.freedesktop.DBus' in result)
+
+ def test_python_calls_async_error_result(self):
+ # when only specifying a result handler, this will get the error
+ def call_done(obj, result, user_data):
+ user_data['result'] = result
+ user_data['main_loop'].quit()
+
+ main_loop = gobject.MainLoop()
+ data = {'main_loop': main_loop}
+ self.dbus_proxy.ListNames('(s)', 'invalid_argument',
+ result_handler=call_done, user_data=data)
+ main_loop.run()
+
+ self.assertTrue(isinstance(data['result'], Exception))
+ self.assertTrue('InvalidArgs' in str(data['result']), str(data['result']))
+
+ def test_python_calls_async_error(self):
+ # when specifying an explicit error handler, this will get the error
+ def call_done(obj, result, user_data):
+ user_data['main_loop'].quit()
+ self.fail('result handler should not be called')
+
+ def call_error(obj, error, user_data):
+ user_data['error'] = error
+ user_data['main_loop'].quit()
+
+ main_loop = gobject.MainLoop()
+ data = {'main_loop': main_loop}
+ self.dbus_proxy.ListNames('(s)', 'invalid_argument',
+ result_handler=call_done, error_handler=call_error,
+ user_data=data)
+ main_loop.run()
+
+ self.assertTrue(isinstance(data['error'], Exception))
+ self.assertTrue('InvalidArgs' in str(data['error']), str(data['error']))
+
diff --git a/tests/test_gi.py b/tests/test_gi.py
index fa9df70c..4aa55322 100644
--- a/tests/test_gi.py
+++ b/tests/test_gi.py
@@ -3,8 +3,6 @@
# vim: tabstop=4 shiftwidth=4 expandtab
import sys
-import pygtk
-pygtk.require("2.0")
import unittest
from gi.repository import GObject
@@ -12,10 +10,15 @@ from gi.repository import GObject
import gobject
from gi.repository import GIMarshallingTests
+from compathelper import _bytes
+
if sys.version_info < (3, 0):
CONSTANT_UTF8 = "const \xe2\x99\xa5 utf8"
+ PY2_UNICODE_UTF8 = unicode(CONSTANT_UTF8, 'UTF-8')
+ CHAR_255='\xff'
else:
CONSTANT_UTF8 = "const ♥ utf8"
+ CHAR_255=bytes([255])
CONSTANT_NUMBER = 42
@@ -120,9 +123,9 @@ class TestUInt8(unittest.TestCase):
number = Number(self.MAX)
GIMarshallingTests.uint8_in(number)
+ GIMarshallingTests.uint8_in(CHAR_255)
number.value += 1
-
self.assertRaises(ValueError, GIMarshallingTests.uint8_in, number)
self.assertRaises(ValueError, GIMarshallingTests.uint8_in, Number(-1))
@@ -402,6 +405,7 @@ class TestInt(unittest.TestCase):
def test_int_inout(self):
self.assertEquals(self.MIN, GIMarshallingTests.int_inout_max_min(Number(self.MAX)))
self.assertEquals(self.MAX, GIMarshallingTests.int_inout_min_max(Number(self.MIN)))
+ self.assertRaises(TypeError, GIMarshallingTests.int_inout_min_max, Number(self.MIN), CONSTANT_NUMBER)
class TestUInt(unittest.TestCase):
@@ -615,6 +619,8 @@ class TestUtf8(unittest.TestCase):
def test_utf8_none_in(self):
GIMarshallingTests.utf8_none_in(CONSTANT_UTF8)
+ if sys.version_info < (3, 0):
+ GIMarshallingTests.utf8_none_in(PY2_UNICODE_UTF8)
self.assertRaises(TypeError, GIMarshallingTests.utf8_none_in, CONSTANT_NUMBER)
self.assertRaises(TypeError, GIMarshallingTests.utf8_none_in, None)
@@ -667,6 +673,10 @@ class TestArray(unittest.TestCase):
def test_array_in(self):
GIMarshallingTests.array_in(Sequence([-1, 0, 1, 2]))
+ def test_array_uint8_in(self):
+ GIMarshallingTests.array_uint8_in(Sequence([97, 98, 99, 100]))
+ GIMarshallingTests.array_uint8_in(_bytes("abcd"))
+
def test_array_out(self):
self.assertEquals([-1, 0, 1, 2], GIMarshallingTests.array_out())
@@ -915,20 +925,30 @@ class TestGValue(unittest.TestCase):
def test_gvalue_in(self):
GIMarshallingTests.gvalue_in(42)
- self.assertRaises(TypeError, GIMarshallingTests.gvalue_in, None)
+ value = GObject.Value()
+ value.init(GObject.TYPE_INT)
+ value.set_int(42)
+ GIMarshallingTests.gvalue_in(value)
def test_gvalue_out(self):
self.assertEquals(42, GIMarshallingTests.gvalue_out())
def test_gvalue_inout(self):
self.assertEquals('42', GIMarshallingTests.gvalue_inout(42))
-
+ value = GObject.Value()
+ value.init(GObject.TYPE_INT)
+ value.set_int(42)
+ self.assertEquals('42', GIMarshallingTests.gvalue_inout(value))
class TestGClosure(unittest.TestCase):
def test_gclosure_in(self):
GIMarshallingTests.gclosure_in(lambda: 42)
+ # test passing a closure between two C calls
+ closure = GIMarshallingTests.gclosure_return()
+ GIMarshallingTests.gclosure_in(closure)
+
self.assertRaises(TypeError, GIMarshallingTests.gclosure_in, 42)
self.assertRaises(TypeError, GIMarshallingTests.gclosure_in, None)
@@ -947,6 +967,15 @@ class TestEnum(unittest.TestCase):
self.assertTrue(isinstance(GIMarshallingTests.Enum.VALUE3, GIMarshallingTests.Enum))
self.assertEquals(42, GIMarshallingTests.Enum.VALUE3)
+ def test_value_nick_and_name(self):
+ self.assertEqual(GIMarshallingTests.Enum.VALUE1.value_nick, 'value1')
+ self.assertEqual(GIMarshallingTests.Enum.VALUE2.value_nick, 'value2')
+ self.assertEqual(GIMarshallingTests.Enum.VALUE3.value_nick, 'value3')
+
+ self.assertEqual(GIMarshallingTests.Enum.VALUE1.value_name, 'GI_MARSHALLING_TESTS_ENUM_VALUE1')
+ self.assertEqual(GIMarshallingTests.Enum.VALUE2.value_name, 'GI_MARSHALLING_TESTS_ENUM_VALUE2')
+ self.assertEqual(GIMarshallingTests.Enum.VALUE3.value_name, 'GI_MARSHALLING_TESTS_ENUM_VALUE3')
+
def test_enum_in(self):
GIMarshallingTests.enum_in(GIMarshallingTests.Enum.VALUE3)
GIMarshallingTests.enum_in(42)
@@ -964,6 +993,16 @@ class TestEnum(unittest.TestCase):
self.assertTrue(isinstance(enum, GIMarshallingTests.Enum))
self.assertEquals(enum, GIMarshallingTests.Enum.VALUE1)
+ def test_enum_second(self):
+ # check for the bug where different non-gtype enums share the same class
+ self.assertNotEqual(GIMarshallingTests.Enum, GIMarshallingTests.SecondEnum)
+
+ # check that values are not being shared between different enums
+ self.assertTrue(hasattr(GIMarshallingTests.SecondEnum, "SECONDVALUE1"))
+ self.assertRaises(AttributeError, getattr, GIMarshallingTests.Enum, "SECONDVALUE1")
+ self.assertTrue(hasattr(GIMarshallingTests.Enum, "VALUE1"))
+ self.assertRaises(AttributeError, getattr, GIMarshallingTests.SecondEnum, "VALUE1")
+
class TestGEnum(unittest.TestCase):
@@ -974,6 +1013,15 @@ class TestGEnum(unittest.TestCase):
self.assertTrue(isinstance(GIMarshallingTests.GEnum.VALUE3, GIMarshallingTests.GEnum))
self.assertEquals(42, GIMarshallingTests.GEnum.VALUE3)
+ def test_value_nick_and_name(self):
+ self.assertEqual(GIMarshallingTests.GEnum.VALUE1.value_nick, 'value1')
+ self.assertEqual(GIMarshallingTests.GEnum.VALUE2.value_nick, 'value2')
+ self.assertEqual(GIMarshallingTests.GEnum.VALUE3.value_nick, 'value3')
+
+ self.assertEqual(GIMarshallingTests.GEnum.VALUE1.value_name, 'GI_MARSHALLING_TESTS_GENUM_VALUE1')
+ self.assertEqual(GIMarshallingTests.GEnum.VALUE2.value_name, 'GI_MARSHALLING_TESTS_GENUM_VALUE2')
+ self.assertEqual(GIMarshallingTests.GEnum.VALUE3.value_name, 'GI_MARSHALLING_TESTS_GENUM_VALUE3')
+
def test_genum_in(self):
GIMarshallingTests.genum_in(GIMarshallingTests.GEnum.VALUE3)
GIMarshallingTests.genum_in(42)
@@ -999,10 +1047,24 @@ class TestGFlags(unittest.TestCase):
self.assertTrue(isinstance(GIMarshallingTests.Flags.VALUE1, GIMarshallingTests.Flags))
self.assertTrue(isinstance(GIMarshallingTests.Flags.VALUE2, GIMarshallingTests.Flags))
self.assertTrue(isinstance(GIMarshallingTests.Flags.VALUE3, GIMarshallingTests.Flags))
+ # __or__() operation should still return an instance, not an int.
+ self.assertTrue(isinstance(GIMarshallingTests.Flags.VALUE1 | GIMarshallingTests.Flags.VALUE2,
+ GIMarshallingTests.Flags))
self.assertEquals(1 << 1, GIMarshallingTests.Flags.VALUE2)
+ def test_value_nick_and_name(self):
+ self.assertEqual(GIMarshallingTests.Flags.VALUE1.first_value_nick, 'value1')
+ self.assertEqual(GIMarshallingTests.Flags.VALUE2.first_value_nick, 'value2')
+ self.assertEqual(GIMarshallingTests.Flags.VALUE3.first_value_nick, 'value3')
+
+ self.assertEqual(GIMarshallingTests.Flags.VALUE1.first_value_name, 'GI_MARSHALLING_TESTS_FLAGS_VALUE1')
+ self.assertEqual(GIMarshallingTests.Flags.VALUE2.first_value_name, 'GI_MARSHALLING_TESTS_FLAGS_VALUE2')
+ self.assertEqual(GIMarshallingTests.Flags.VALUE3.first_value_name, 'GI_MARSHALLING_TESTS_FLAGS_VALUE3')
+
def test_flags_in(self):
GIMarshallingTests.flags_in(GIMarshallingTests.Flags.VALUE2)
+ # result of __or__() operation should still be valid instance, not an int.
+ GIMarshallingTests.flags_in(GIMarshallingTests.Flags.VALUE2 | GIMarshallingTests.Flags.VALUE2)
GIMarshallingTests.flags_in_zero(Number(0))
self.assertRaises(TypeError, GIMarshallingTests.flags_in, 1 << 1)
@@ -1018,6 +1080,45 @@ class TestGFlags(unittest.TestCase):
self.assertTrue(isinstance(flags, GIMarshallingTests.Flags))
self.assertEquals(flags, GIMarshallingTests.Flags.VALUE1)
+class TestNoTypeFlags(unittest.TestCase):
+
+ def test_flags(self):
+ self.assertTrue(issubclass(GIMarshallingTests.NoTypeFlags, GObject.GFlags))
+ self.assertTrue(isinstance(GIMarshallingTests.NoTypeFlags.VALUE1, GIMarshallingTests.NoTypeFlags))
+ self.assertTrue(isinstance(GIMarshallingTests.NoTypeFlags.VALUE2, GIMarshallingTests.NoTypeFlags))
+ self.assertTrue(isinstance(GIMarshallingTests.NoTypeFlags.VALUE3, GIMarshallingTests.NoTypeFlags))
+ # __or__() operation should still return an instance, not an int.
+ self.assertTrue(isinstance(GIMarshallingTests.NoTypeFlags.VALUE1 | GIMarshallingTests.NoTypeFlags.VALUE2,
+ GIMarshallingTests.NoTypeFlags))
+ self.assertEquals(1 << 1, GIMarshallingTests.NoTypeFlags.VALUE2)
+
+ def test_value_nick_and_name(self):
+ self.assertEqual(GIMarshallingTests.NoTypeFlags.VALUE1.first_value_nick, 'value1')
+ self.assertEqual(GIMarshallingTests.NoTypeFlags.VALUE2.first_value_nick, 'value2')
+ self.assertEqual(GIMarshallingTests.NoTypeFlags.VALUE3.first_value_nick, 'value3')
+
+ self.assertEqual(GIMarshallingTests.NoTypeFlags.VALUE1.first_value_name, 'GI_MARSHALLING_TESTS_NO_TYPE_FLAGS_VALUE1')
+ self.assertEqual(GIMarshallingTests.NoTypeFlags.VALUE2.first_value_name, 'GI_MARSHALLING_TESTS_NO_TYPE_FLAGS_VALUE2')
+ self.assertEqual(GIMarshallingTests.NoTypeFlags.VALUE3.first_value_name, 'GI_MARSHALLING_TESTS_NO_TYPE_FLAGS_VALUE3')
+
+ def test_flags_in(self):
+ GIMarshallingTests.no_type_flags_in(GIMarshallingTests.NoTypeFlags.VALUE2)
+ GIMarshallingTests.no_type_flags_in(GIMarshallingTests.NoTypeFlags.VALUE2 | GIMarshallingTests.NoTypeFlags.VALUE2)
+ GIMarshallingTests.no_type_flags_in_zero(Number(0))
+
+ self.assertRaises(TypeError, GIMarshallingTests.no_type_flags_in, 1 << 1)
+ self.assertRaises(TypeError, GIMarshallingTests.no_type_flags_in, 'GIMarshallingTests.NoTypeFlags.VALUE2')
+
+ def test_flags_out(self):
+ flags = GIMarshallingTests.no_type_flags_out()
+ self.assertTrue(isinstance(flags, GIMarshallingTests.NoTypeFlags))
+ self.assertEquals(flags, GIMarshallingTests.NoTypeFlags.VALUE2)
+
+ def test_flags_inout(self):
+ flags = GIMarshallingTests.no_type_flags_inout(GIMarshallingTests.NoTypeFlags.VALUE2)
+ self.assertTrue(isinstance(flags, GIMarshallingTests.NoTypeFlags))
+ self.assertEquals(flags, GIMarshallingTests.NoTypeFlags.VALUE1)
+
class TestStructure(unittest.TestCase):
@@ -1352,8 +1453,6 @@ class TestGObject(unittest.TestCase):
class TestPythonGObject(unittest.TestCase):
class Object(GIMarshallingTests.Object):
- __gtype_name__ = "Object"
-
def __init__(self, int):
GIMarshallingTests.Object.__init__(self)
self.val = None
@@ -1369,7 +1468,16 @@ class TestPythonGObject(unittest.TestCase):
return 42
def do_method_with_default_implementation(self, int8):
- self.props.int = int8 * 2
+ GIMarshallingTests.Object.do_method_with_default_implementation(self, int8)
+ self.props.int += int8
+
+ class SubObject(GIMarshallingTests.SubObject):
+ def __init__(self, int):
+ GIMarshallingTests.SubObject.__init__(self)
+ self.val = None
+
+ def do_method_with_default_implementation(self, int8):
+ self.val = int8
def test_object(self):
self.assertTrue(issubclass(self.Object, GIMarshallingTests.Object))
@@ -1387,11 +1495,9 @@ class TestPythonGObject(unittest.TestCase):
self.assertEqual(object_.method_int8_out(), 42)
object_.method_with_default_implementation(42)
- self.assertEqual(object_.val, 84)
+ self.assertEqual(object_.props.int, 84)
class ObjectWithoutVFunc(GIMarshallingTests.Object):
- __gtype_name__ = 'ObjectWithoutVFunc'
-
def __init__(self, int):
GIMarshallingTests.Object.__init__(self)
@@ -1399,6 +1505,11 @@ class TestPythonGObject(unittest.TestCase):
object_.method_with_default_implementation(84)
self.assertEqual(object_.props.int, 84)
+ def test_subobject_parent_vfunc(self):
+ object_ = self.SubObject(int = 81)
+ object_.method_with_default_implementation(87)
+ self.assertEquals(object_.val, 87)
+
def test_dynamic_module(self):
from gi.module import DynamicGObjectModule
self.assertTrue(isinstance(GObject, DynamicGObjectModule))
@@ -1407,6 +1518,44 @@ class TestPythonGObject(unittest.TestCase):
# compare a static gobject attr with a dynamic GObject attr
self.assertEquals(GObject.GObject, gobject.GObject)
+ def test_subobject_non_vfunc_do_method(self):
+ class PythonObjectWithNonVFuncDoMethod:
+ def do_not_a_vfunc(self):
+ return 5
+
+ class ObjectOverrideNonVFuncDoMethod(GIMarshallingTests.Object, PythonObjectWithNonVFuncDoMethod):
+ def do_not_a_vfunc(self):
+ value = super(ObjectOverrideNonVFuncDoMethod, self).do_not_a_vfunc()
+ return 13 + value
+
+ object_ = ObjectOverrideNonVFuncDoMethod()
+ self.assertEquals(18, object_.do_not_a_vfunc())
+
+ def test_native_function_not_set_in_subclass_dict(self):
+ # Previously, GI was setting virtual functions on the class as well
+ # as any *native* class that subclasses it. Here we check that it is only
+ # set on the class that the method is originally from.
+ self.assertTrue('do_method_with_default_implementation' in GIMarshallingTests.Object.__dict__)
+ self.assertTrue('do_method_with_default_implementation' not in GIMarshallingTests.SubObject.__dict__)
+
+ # Here we check that accessing a vfunc from the subclass returns the same wrapper object,
+ # meaning that multiple wrapper objects have not been created for the same vfunc.
+ func1 = GIMarshallingTests.Object.do_method_with_default_implementation
+ func2 = GIMarshallingTests.SubObject.do_method_with_default_implementation
+ if sys.version_info < (3,0):
+ func1 = func1.im_func
+ func2 = func2.im_func
+
+ self.assertTrue(func1 is func2)
+
+ def test_subobject_with_interface_and_non_vfunc_do_method(self):
+ # There was a bug for searching for vfuncs in interfaces. It was
+ # triggered by having a do_* method that wasn't overriding
+ # a native vfunc, as well as inheriting from an interface.
+ class GObjectSubclassWithInterface(GObject.GObject, GIMarshallingTests.Interface):
+ def do_method_not_a_vfunc(self):
+ pass
+
class TestMultiOutputArgs(unittest.TestCase):
def test_int_out_out(self):
@@ -1415,6 +1564,17 @@ class TestMultiOutputArgs(unittest.TestCase):
def test_int_return_out(self):
self.assertEquals((6, 7), GIMarshallingTests.int_return_out())
+class TestGErrorException(unittest.TestCase):
+ def test_gerror_exception(self):
+ self.assertRaises(GObject.GError, GIMarshallingTests.gerror)
+ try:
+ GIMarshallingTests.gerror()
+ except Exception:
+ etype, e = sys.exc_info()[:2]
+ self.assertEquals(e.domain, GIMarshallingTests.CONSTANT_GERROR_DOMAIN)
+ self.assertEquals(e.code, GIMarshallingTests.CONSTANT_GERROR_CODE)
+ self.assertEquals(e.message, GIMarshallingTests.CONSTANT_GERROR_MESSAGE)
+
# Interface
@@ -1428,7 +1588,6 @@ class TestInterfaces(unittest.TestCase):
def test_implementation(self):
class TestInterfaceImpl(GObject.GObject, GIMarshallingTests.Interface):
- __gtype_name__ = 'TestInterfaceImpl'
def __init__(self):
GObject.GObject.__init__(self)
self.val = None
@@ -1445,24 +1604,42 @@ class TestInterfaces(unittest.TestCase):
self.assertEquals(instance.val, 42)
class TestInterfaceImplA(TestInterfaceImpl):
- __gtype_name__ = 'TestInterfaceImplA'
+ pass
class TestInterfaceImplB(TestInterfaceImplA):
- __gtype_name__ = 'TestInterfaceImplB'
+ pass
instance = TestInterfaceImplA()
GIMarshallingTests.test_interface_test_int8_in(instance, 42)
self.assertEquals(instance.val, 42)
- def define_implementor_without_gtype():
- class TestInterfaceImpl(gobject.GObject, GIMarshallingTests.Interface):
- def __init__(self):
- gobject.GObject.__init__(self)
- self.val = None
+ def test_mro(self):
+ # there was a problem with Python bailing out because of
+ # http://en.wikipedia.org/wiki/Diamond_problem with interfaces,
+ # which shouldn't really be a problem.
+
+ class TestInterfaceImpl(GObject.GObject, GIMarshallingTests.Interface):
+ pass
+
+ class TestInterfaceImpl2(GIMarshallingTests.Interface,
+ TestInterfaceImpl):
+ pass
+
+ class TestInterfaceImpl3(TestInterfaceImpl,
+ GIMarshallingTests.Interface2):
+ pass
+
+class TestInterfaceClash(unittest.TestCase):
+
+ def test_clash(self):
+ def create_clash():
+ class TestClash(GObject.GObject, GIMarshallingTests.Interface, GIMarshallingTests.Interface2):
def do_test_int8_in(self, int8):
- self.val = int8
- self.assertRaises(RuntimeError, define_implementor_without_gtype)
+ pass
+ TestClash()
+
+ self.assertRaises(TypeError, create_clash)
class TestOverrides(unittest.TestCase):
@@ -1510,3 +1687,36 @@ class TestOverrides(unittest.TestCase):
def test_module_name(self):
self.assertEquals(GIMarshallingTests.OverridesStruct.__module__, 'gi.overrides.GIMarshallingTests')
self.assertEquals(GObject.InitiallyUnowned.__module__, 'gi.repository.GObject')
+
+class TestDir(unittest.TestCase):
+ def test_members_list(self):
+ list = dir(GIMarshallingTests)
+ self.assertTrue('OverridesStruct' in list)
+ self.assertTrue('BoxedStruct' in list)
+ self.assertTrue('OVERRIDES_CONSTANT' in list)
+ self.assertTrue('GEnum' in list)
+ self.assertTrue('int32_return_max' in list)
+
+ def test_modules_list(self):
+ import gi.repository
+ list = dir(gi.repository)
+ self.assertTrue('GIMarshallingTests' in list)
+
+ # FIXME: test to see if a module which was not imported is in the list
+ # we should be listing every typelib we find, not just the ones
+ # which are imported
+ #
+ # to test this I recommend we compile a fake module which
+ # our tests would never import and check to see if it is
+ # in the list:
+ #
+ # self.assertTrue('DoNotImportDummyTests' in list)
+
+class TestGErrorArrayInCrash(unittest.TestCase):
+ # Previously there was a bug in invoke, in which C arrays were unwrapped
+ # from inside GArrays to be passed to the C function. But when a GError was
+ # set, invoke would attempt to free the C array as if it were a GArray.
+ # This crash is only for C arrays. It does not happen for C functions which
+ # take in GArrays. See https://bugzilla.gnome.org/show_bug.cgi?id=642708
+ def test_gerror_array_in_crash(self):
+ self.assertRaises(GObject.GError, GIMarshallingTests.gerror_array_in, [1, 2, 3])
diff --git a/tests/test_gio.py b/tests/test_gio.py
index 7c8251ea..e14eddff 100644
--- a/tests/test_gio.py
+++ b/tests/test_gio.py
@@ -516,13 +516,13 @@ class TestFile(unittest.TestCase):
class TestGFileEnumerator(unittest.TestCase):
def setUp(self):
- self.file = gio.File(".")
+ self.file = gio.File(os.path.dirname(__file__))
def testEnumerateChildren(self):
enumerator = self.file.enumerate_children(
"standard::*", gio.FILE_QUERY_INFO_NOFOLLOW_SYMLINKS)
for file_info in enumerator:
- if file_info.get_name() == 'test_gio.py':
+ if file_info.get_name() == os.path.basename(__file__):
break
else:
raise AssertionError
@@ -531,7 +531,7 @@ class TestGFileEnumerator(unittest.TestCase):
def callback(gfile, result):
try:
for file_info in gfile.enumerate_children_finish(result):
- if file_info.get_name() == 'test_gio.py':
+ if file_info.get_name() == __file__:
break
else:
raise AssertionError
@@ -547,7 +547,7 @@ class TestGFileEnumerator(unittest.TestCase):
def callback(enumerator, result):
try:
for file_info in enumerator.next_files_finish(result):
- if file_info.get_name() == 'test_gio.py':
+ if file_info.get_name() == __file__:
break
else:
raise AssertionError
@@ -594,7 +594,7 @@ class TestInputStream(unittest.TestCase):
self.assertEquals(self.stream.read(), '')
self.stream = gio.MemoryInputStream()
- some_data = open("test_gio.py", "rb").read()
+ some_data = open(__file__, "rb").read()
self.stream.add_data(some_data)
self.assertEquals(self.stream.read(), some_data)
@@ -631,7 +631,7 @@ class TestInputStream(unittest.TestCase):
'testing')
stream = gio.MemoryInputStream()
- some_data = open('test_gio.py', 'rb').read()
+ some_data = open(__file__, 'rb').read()
stream.add_data(some_data)
self.assertEquals(self._read_in_loop(stream,
lambda: stream.read_part(50),
@@ -807,7 +807,7 @@ class TestOutputStream(unittest.TestCase):
def test_write_part(self):
stream = gio.MemoryOutputStream()
- some_data = open('test_gio.py', 'rb').read()
+ some_data = open(__file__, 'rb').read()
buffer = some_data
# In fact this makes only one looping (memory stream is fast,
@@ -963,7 +963,7 @@ class TestContentTypeGuess(unittest.TestCase):
class TestFileInfo(unittest.TestCase):
def setUp(self):
- self.fileinfo = gio.File("test_gio.py").query_info("*")
+ self.fileinfo = gio.File(__file__).query_info("*")
def testListAttributes(self):
attributes = self.fileinfo.list_attributes("standard")
diff --git a/tests/test_gobject.py b/tests/test_gobject.py
index f3cdf293..d628b0e0 100644
--- a/tests/test_gobject.py
+++ b/tests/test_gobject.py
@@ -3,6 +3,7 @@
import unittest
import gobject
+import sys
import testhelper
@@ -11,10 +12,16 @@ class TestGObjectAPI(unittest.TestCase):
obj = gobject.GObject()
self.assertEquals(obj.__module__,
'gobject._gobject')
+
+
+class TestReferenceCounting(unittest.TestCase):
+ def testRegularObject(self):
+ obj = gobject.GObject()
self.assertEquals(obj.__grefcount__, 1)
+ obj = gobject.new(gobject.GObject)
+ self.assertEquals(obj.__grefcount__, 1)
-class TestFloating(unittest.TestCase):
def testFloatingWithSinkFunc(self):
obj = testhelper.FloatingWithSinkFunc()
self.assertEquals(obj.__grefcount__, 1)
@@ -28,3 +35,152 @@ class TestFloating(unittest.TestCase):
obj = gobject.new(testhelper.FloatingWithoutSinkFunc)
self.assertEquals(obj.__grefcount__, 1)
+
+ def testOwnedByLibrary(self):
+ # Upon creation, the refcount of the object should be 2:
+ # - someone already has a reference on the new object.
+ # - the python wrapper should hold its own reference.
+ obj = testhelper.OwnedByLibrary()
+ self.assertEquals(obj.__grefcount__, 2)
+
+ # We ask the library to release its reference, so the only
+ # remaining ref should be our wrapper's. Once the wrapper
+ # will run out of scope, the object will get finalized.
+ obj.release()
+ self.assertEquals(obj.__grefcount__, 1)
+
+ def testOwnedByLibraryOutOfScope(self):
+ obj = testhelper.OwnedByLibrary()
+ self.assertEquals(obj.__grefcount__, 2)
+
+ # We are manually taking the object out of scope. This means
+ # that our wrapper has been freed, and its reference dropped. We
+ # cannot check it but the refcount should now be 1 (the ref held
+ # by the library is still there, we didn't call release()
+ obj = None
+
+ # When we get the object back from the lib, the wrapper is
+ # re-created, so our refcount will be 2 once again.
+ obj = testhelper.owned_by_library_get_instance_list()[0]
+ self.assertEquals(obj.__grefcount__, 2)
+
+ obj.release()
+ self.assertEquals(obj.__grefcount__, 1)
+
+ def testOwnedByLibraryUsingGObjectNew(self):
+ # Upon creation, the refcount of the object should be 2:
+ # - someone already has a reference on the new object.
+ # - the python wrapper should hold its own reference.
+ obj = gobject.new(testhelper.OwnedByLibrary)
+ self.assertEquals(obj.__grefcount__, 2)
+
+ # We ask the library to release its reference, so the only
+ # remaining ref should be our wrapper's. Once the wrapper
+ # will run out of scope, the object will get finalized.
+ obj.release()
+ self.assertEquals(obj.__grefcount__, 1)
+
+ def testOwnedByLibraryOutOfScopeUsingGobjectNew(self):
+ obj = gobject.new(testhelper.OwnedByLibrary)
+ self.assertEquals(obj.__grefcount__, 2)
+
+ # We are manually taking the object out of scope. This means
+ # that our wrapper has been freed, and its reference dropped. We
+ # cannot check it but the refcount should now be 1 (the ref held
+ # by the library is still there, we didn't call release()
+ obj = None
+
+ # When we get the object back from the lib, the wrapper is
+ # re-created, so our refcount will be 2 once again.
+ obj = testhelper.owned_by_library_get_instance_list()[0]
+ self.assertEquals(obj.__grefcount__, 2)
+
+ obj.release()
+ self.assertEquals(obj.__grefcount__, 1)
+
+ def testFloatingAndSunk(self):
+ # Upon creation, the refcount of the object should be 2:
+ # - someone already has a reference on the new object.
+ # - the python wrapper should hold its own reference.
+ obj = testhelper.FloatingAndSunk()
+ self.assertEquals(obj.__grefcount__, 2)
+
+ # We ask the library to release its reference, so the only
+ # remaining ref should be our wrapper's. Once the wrapper
+ # will run out of scope, the object will get finalized.
+ obj.release()
+ self.assertEquals(obj.__grefcount__, 1)
+
+ def testFloatingAndSunkOutOfScope(self):
+ obj = testhelper.FloatingAndSunk()
+ self.assertEquals(obj.__grefcount__, 2)
+
+ # We are manually taking the object out of scope. This means
+ # that our wrapper has been freed, and its reference dropped. We
+ # cannot check it but the refcount should now be 1 (the ref held
+ # by the library is still there, we didn't call release()
+ obj = None
+
+ # When we get the object back from the lib, the wrapper is
+ # re-created, so our refcount will be 2 once again.
+ obj = testhelper.floating_and_sunk_get_instance_list()[0]
+ self.assertEquals(obj.__grefcount__, 2)
+
+ obj.release()
+ self.assertEquals(obj.__grefcount__, 1)
+
+
+ def testFloatingAndSunkUsingGObjectNew(self):
+ # Upon creation, the refcount of the object should be 2:
+ # - someone already has a reference on the new object.
+ # - the python wrapper should hold its own reference.
+ obj = gobject.new(testhelper.FloatingAndSunk)
+ self.assertEquals(obj.__grefcount__, 2)
+
+ # We ask the library to release its reference, so the only
+ # remaining ref should be our wrapper's. Once the wrapper
+ # will run out of scope, the object will get finalized.
+ obj.release()
+ self.assertEquals(obj.__grefcount__, 1)
+
+ def testFloatingAndSunkOutOfScopeUsingGObjectNew(self):
+ obj = gobject.new(testhelper.FloatingAndSunk)
+ self.assertEquals(obj.__grefcount__, 2)
+
+ # We are manually taking the object out of scope. This means
+ # that our wrapper has been freed, and its reference dropped. We
+ # cannot check it but the refcount should now be 1 (the ref held
+ # by the library is still there, we didn't call release()
+ obj = None
+
+ # When we get the object back from the lib, the wrapper is
+ # re-created, so our refcount will be 2 once again.
+ obj = testhelper.floating_and_sunk_get_instance_list()[0]
+ self.assertEquals(obj.__grefcount__, 2)
+
+ obj.release()
+ self.assertEquals(obj.__grefcount__, 1)
+
+class A(gobject.GObject):
+ def __init__(self):
+ super(A, self).__init__()
+
+class TestPythonReferenceCounting(unittest.TestCase):
+ # Newly created instances should alwayshave two references: one for
+ # the GC, and one for the bound variable in the local scope.
+
+ def testNewInstanceHasTwoRefs(self):
+ obj = gobject.GObject()
+ self.assertEquals(sys.getrefcount(obj), 2)
+
+ def testNewInstanceHasTwoRefsUsingGObjectNew(self):
+ obj = gobject.new(gobject.GObject)
+ self.assertEquals(sys.getrefcount(obj), 2)
+
+ def testNewSubclassInstanceHasTwoRefs(self):
+ obj = A()
+ self.assertEquals(sys.getrefcount(obj), 2)
+
+ def testNewSubclassInstanceHasTwoRefsUsingGObjectNew(self):
+ obj = gobject.new(A)
+ self.assertEquals(sys.getrefcount(obj), 2)
diff --git a/tests/test_gresolver.py b/tests/test_gresolver.py
index ef8585c3..4238df77 100644
--- a/tests/test_gresolver.py
+++ b/tests/test_gresolver.py
@@ -46,6 +46,9 @@ class TestResolver(unittest.TestCase):
loop = glib.MainLoop()
loop.run()
+""" Commented out because this requires an active internet connection and a
+ router that supports SRV lookups
+
def test_resolver_lookup_service(self):
targets = self.resolver.lookup_service("xmpp-client", "tcp", "google.com")
self.failUnless(isinstance(targets[0], gio.SrvTarget))
@@ -62,4 +65,4 @@ class TestResolver(unittest.TestCase):
loop = glib.MainLoop()
loop.run()
-
+"""
diff --git a/tests/test_overrides.py b/tests/test_overrides.py
index b86222b9..3421c3a8 100644
--- a/tests/test_overrides.py
+++ b/tests/test_overrides.py
@@ -1,52 +1,360 @@
# -*- Mode: Python; py-indent-offset: 4 -*-
# vim: tabstop=4 shiftwidth=4 expandtab
-import pygtk
-pygtk.require("2.0")
-
import unittest
import sys
+import os
sys.path.insert(0, "../")
+from compathelper import _long, _unicode, _bytes
+
from gi.repository import GLib
from gi.repository import GObject
from gi.repository import Gdk
from gi.repository import Gtk
+from gi.repository import Gio
+from gi.repository import Pango
+from gi.repository import GdkPixbuf
import gi.overrides as overrides
+import gi.types
class TestGLib(unittest.TestCase):
- def test_gvariant(self):
+ def test_gvariant_create(self):
+ # simple values
+
variant = GLib.Variant('i', 42)
self.assertTrue(isinstance(variant, GLib.Variant))
self.assertEquals(variant.get_int32(), 42)
- variant = GLib.Variant('(ss)', 'mec', 'mac')
+ variant = GLib.Variant('s', '')
+ self.assertTrue(isinstance(variant, GLib.Variant))
+ self.assertEquals(variant.get_string(), '')
+
+ variant = GLib.Variant('s', 'hello')
+ self.assertTrue(isinstance(variant, GLib.Variant))
+ self.assertEquals(variant.get_string(), 'hello')
+
+ # boxed variant
+ variant = GLib.Variant('v', GLib.Variant('i', 42))
+ self.assertTrue(isinstance(variant, GLib.Variant))
+ self.assertTrue(isinstance(variant.get_variant(), GLib.Variant))
+ self.assertEqual(variant.get_type_string(), 'v')
+ self.assertEqual(variant.get_variant().get_type_string(), 'i')
+ self.assertEquals(variant.get_variant().get_int32(), 42)
+
+ variant = GLib.Variant('v', GLib.Variant('v', GLib.Variant('i', 42)))
+ self.assertEqual(variant.get_type_string(), 'v')
+ self.assertEqual(variant.get_variant().get_type_string(), 'v')
+ self.assertEqual(variant.get_variant().get_variant().get_type_string(), 'i')
+ self.assertEquals(variant.get_variant().get_variant().get_int32(), 42)
+
+ # tuples
+
+ variant = GLib.Variant('()', ())
+ self.assertEqual(variant.get_type_string(), '()')
+ self.assertEquals(variant.n_children(), 0)
+
+ variant = GLib.Variant('(i)', (3,))
+ self.assertEqual(variant.get_type_string(), '(i)')
+ self.assertTrue(isinstance(variant, GLib.Variant))
+ self.assertEquals(variant.n_children(), 1)
+ self.assertTrue(isinstance(variant.get_child_value(0), GLib.Variant))
+ self.assertEquals(variant.get_child_value(0).get_int32(), 3)
+
+ variant = GLib.Variant('(ss)', ('mec', 'mac'))
+ self.assertEqual(variant.get_type_string(), '(ss)')
self.assertTrue(isinstance(variant, GLib.Variant))
self.assertTrue(isinstance(variant.get_child_value(0), GLib.Variant))
self.assertTrue(isinstance(variant.get_child_value(1), GLib.Variant))
self.assertEquals(variant.get_child_value(0).get_string(), 'mec')
self.assertEquals(variant.get_child_value(1).get_string(), 'mac')
- variant = GLib.Variant('a{si}', {'key1': 1, 'key2': 2})
+ # nested tuples
+ variant = GLib.Variant('((si)(ub))', (('hello', -1), (42, True)))
+ self.assertEqual(variant.get_type_string(), '((si)(ub))')
+ self.assertEqual(variant.unpack(), (('hello', -1), (_long(42), True)))
+
+ # dictionaries
+
+ variant = GLib.Variant('a{si}', {})
+ self.assertTrue(isinstance(variant, GLib.Variant))
+ self.assertEqual(variant.get_type_string(), 'a{si}')
+ self.assertEquals(variant.n_children(), 0)
+
+ variant = GLib.Variant('a{si}', {'': 1, 'key1': 2, 'key2': 3})
+ self.assertEqual(variant.get_type_string(), 'a{si}')
+ self.assertTrue(isinstance(variant, GLib.Variant))
+ self.assertTrue(isinstance(variant.get_child_value(0), GLib.Variant))
+ self.assertTrue(isinstance(variant.get_child_value(1), GLib.Variant))
+ self.assertTrue(isinstance(variant.get_child_value(2), GLib.Variant))
+ self.assertEqual(variant.unpack(), {'': 1, 'key1': 2, 'key2': 3})
+
+ # nested dictionaries
+ variant = GLib.Variant('a{sa{si}}', {})
+ self.assertTrue(isinstance(variant, GLib.Variant))
+ self.assertEqual(variant.get_type_string(), 'a{sa{si}}')
+ self.assertEquals(variant.n_children(), 0)
+
+ d = {'': {'': 1, 'keyn1': 2},
+ 'key1': {'key11': 11, 'key12': 12}}
+ variant = GLib.Variant('a{sa{si}}', d)
+ self.assertEqual(variant.get_type_string(), 'a{sa{si}}')
+ self.assertTrue(isinstance(variant, GLib.Variant))
+ self.assertEqual(variant.unpack(), d)
+
+ # arrays
+
+ variant = GLib.Variant('ai', [])
+ self.assertEqual(variant.get_type_string(), 'ai')
+ self.assertEquals(variant.n_children(), 0)
+
+ variant = GLib.Variant('ai', [1, 2])
+ self.assertEqual(variant.get_type_string(), 'ai')
+ self.assertTrue(isinstance(variant, GLib.Variant))
+ self.assertTrue(isinstance(variant.get_child_value(0), GLib.Variant))
+ self.assertTrue(isinstance(variant.get_child_value(1), GLib.Variant))
+ self.assertEquals(variant.get_child_value(0).get_int32(), 1)
+ self.assertEquals(variant.get_child_value(1).get_int32(), 2)
+
+ variant = GLib.Variant('as', [])
+ self.assertEqual(variant.get_type_string(), 'as')
+ self.assertEquals(variant.n_children(), 0)
+
+ variant = GLib.Variant('as', [''])
+ self.assertEqual(variant.get_type_string(), 'as')
+ self.assertTrue(isinstance(variant, GLib.Variant))
+ self.assertTrue(isinstance(variant.get_child_value(0), GLib.Variant))
+ self.assertEquals(variant.get_child_value(0).get_string(), '')
+
+ variant = GLib.Variant('as', ['hello', 'world'])
+ self.assertEqual(variant.get_type_string(), 'as')
self.assertTrue(isinstance(variant, GLib.Variant))
self.assertTrue(isinstance(variant.get_child_value(0), GLib.Variant))
self.assertTrue(isinstance(variant.get_child_value(1), GLib.Variant))
- # Looks like order is not preserved
- self.assertEquals(variant.get_child_value(1).get_child_value(0).get_string(), 'key1')
- self.assertEquals(variant.get_child_value(1).get_child_value(1).get_int32(), 1)
- self.assertEquals(variant.get_child_value(0).get_child_value(0).get_string(), 'key2')
- self.assertEquals(variant.get_child_value(0).get_child_value(1).get_int32(), 2)
+ self.assertEquals(variant.get_child_value(0).get_string(), 'hello')
+ self.assertEquals(variant.get_child_value(1).get_string(), 'world')
+
+ # nested arrays
+ variant = GLib.Variant('aai', [])
+ self.assertEqual(variant.get_type_string(), 'aai')
+ self.assertEquals(variant.n_children(), 0)
+
+ variant = GLib.Variant('aai', [[]])
+ self.assertEqual(variant.get_type_string(), 'aai')
+ self.assertEquals(variant.n_children(), 1)
+ self.assertEquals(variant.get_child_value(0).n_children(), 0)
+
+ variant = GLib.Variant('aai', [[1, 2], [3, 4, 5]])
+ self.assertEqual(variant.get_type_string(), 'aai')
+ self.assertEquals(variant.unpack(), [[1, 2], [3, 4, 5]])
+
+ #
+ # complex types
+ #
+
+ variant = GLib.Variant('(as)', ([],))
+ self.assertEqual(variant.get_type_string(), '(as)')
+ self.assertEquals(variant.n_children(), 1)
+ self.assertEquals(variant.get_child_value(0).n_children(), 0)
+
+ variant = GLib.Variant('(as)', ([''],))
+ self.assertEqual(variant.get_type_string(), '(as)')
+ self.assertEquals(variant.n_children(), 1)
+ self.assertEquals(variant.get_child_value(0).n_children(), 1)
+ self.assertEquals(variant.get_child_value(0).get_child_value(0).get_string(), '')
+
+ variant = GLib.Variant('(as)', (['hello'],))
+ self.assertEqual(variant.get_type_string(), '(as)')
+ self.assertEquals(variant.n_children(), 1)
+ self.assertEquals(variant.get_child_value(0).n_children(), 1)
+ self.assertEquals(variant.get_child_value(0).get_child_value(0).get_string(), 'hello')
+
+ obj = {'a1': (1, True), 'a2': (2, False)}
+ variant = GLib.Variant('a{s(ib)}', obj)
+ self.assertEqual(variant.get_type_string(), 'a{s(ib)}')
+ self.assertEqual(variant.unpack(), obj)
+
+ obj = {'a1': (1, GLib.Variant('b', True)), 'a2': (2, GLib.Variant('y', 255))}
+ variant = GLib.Variant('a{s(iv)}', obj)
+ self.assertEqual(variant.get_type_string(), 'a{s(iv)}')
+ self.assertEqual(variant.unpack(), {'a1': (1, True), 'a2': (2, 255)})
+
+ obj = (1, {'a': {'a1': True, 'a2': False},
+ 'b': {'b1': False},
+ 'c': {}
+ },
+ 'foo')
+ variant = GLib.Variant('(ia{sa{sb}}s)', obj)
+ self.assertEqual(variant.get_type_string(), '(ia{sa{sb}}s)')
+ self.assertEqual(variant.unpack(), obj)
+
+ def test_gvariant_create_errors(self):
+ # excess arguments
+ self.assertRaises(TypeError, GLib.Variant, 'i', 42, 3)
+ self.assertRaises(TypeError, GLib.Variant, '(i)', (42, 3))
+
+ # not enough arguments
+ self.assertRaises(TypeError, GLib.Variant, '(ii)', (42,))
+
+ # data type mismatch
+ self.assertRaises(TypeError, GLib.Variant, 'i', 'hello')
+ self.assertRaises(TypeError, GLib.Variant, 's', 42)
+ self.assertRaises(TypeError, GLib.Variant, '(ss)', 'mec', 'mac')
+
+ # unimplemented data type
+ self.assertRaises(NotImplementedError, GLib.Variant, 'Q', 1)
+
+ def test_gvariant_unpack(self):
+ # simple values
+ res = GLib.Variant.new_int32(-42).unpack()
+ self.assertEqual(res, -42)
+
+ res = GLib.Variant.new_uint64(34359738368).unpack()
+ self.assertEqual(res, 34359738368)
+
+ res = GLib.Variant.new_boolean(True).unpack()
+ self.assertEqual(res, True)
+
+ res = GLib.Variant.new_object_path('/foo/Bar').unpack()
+ self.assertEqual(res, '/foo/Bar')
+
+ # variant
+ res = GLib.Variant('v', GLib.Variant.new_int32(-42)).unpack()
+ self.assertEqual(res, -42)
+
+ variant = GLib.Variant('v', GLib.Variant('v', GLib.Variant('i', 42)))
+ self.assertEqual(res, -42)
+
+ # tuple
+ res = GLib.Variant.new_tuple(GLib.Variant.new_int32(-1),
+ GLib.Variant.new_string('hello')).unpack()
+ self.assertEqual(res, (-1, 'hello'))
+
+ # array
+ vb = GLib.VariantBuilder()
+ vb.init(gi._gi.variant_type_from_string('ai'))
+ vb.add_value(GLib.Variant.new_int32(-1))
+ vb.add_value(GLib.Variant.new_int32(3))
+ res = vb.end().unpack()
+ self.assertEqual(res, [-1, 3])
+
+ # dictionary
+ res = GLib.Variant('a{si}', {'key1': 1, 'key2': 2}).unpack()
+ self.assertEqual(res, {'key1': 1, 'key2': 2})
+
+ def test_gvariant_iteration(self):
+ # array index access
+ vb = GLib.VariantBuilder()
+ vb.init(gi._gi.variant_type_from_string('ai'))
+ vb.add_value(GLib.Variant.new_int32(-1))
+ vb.add_value(GLib.Variant.new_int32(3))
+ v = vb.end()
+
+ self.assertEqual(len(v), 2)
+ self.assertEqual(v[0], -1)
+ self.assertEqual(v[1], 3)
+ self.assertEqual(v[-1], 3)
+ self.assertEqual(v[-2], -1)
+ self.assertRaises(IndexError, v.__getitem__, 2)
+ self.assertRaises(IndexError, v.__getitem__, -3)
+ self.assertRaises(ValueError, v.__getitem__, 'a')
+
+ # array iteration
+ self.assertEqual([x for x in v], [-1, 3])
+ self.assertEqual(list(v), [-1, 3])
+
+ # tuple index access
+ v = GLib.Variant.new_tuple(GLib.Variant.new_int32(-1),
+ GLib.Variant.new_string('hello'))
+ self.assertEqual(len(v), 2)
+ self.assertEqual(v[0], -1)
+ self.assertEqual(v[1], 'hello')
+ self.assertEqual(v[-1], 'hello')
+ self.assertEqual(v[-2], -1)
+ self.assertRaises(IndexError, v.__getitem__, 2)
+ self.assertRaises(IndexError, v.__getitem__, -3)
+ self.assertRaises(ValueError, v.__getitem__, 'a')
+
+ # tuple iteration
+ self.assertEqual([x for x in v], [-1, 'hello'])
+ self.assertEqual(tuple(v), (-1, 'hello'))
+
+ # dictionary index access
+ vsi = GLib.Variant('a{si}', {'key1': 1, 'key2': 2})
+ vis = GLib.Variant('a{is}', {1: 'val1', 5: 'val2'})
+
+ self.assertEqual(len(vsi), 2)
+ self.assertEqual(vsi['key1'], 1)
+ self.assertEqual(vsi['key2'], 2)
+ self.assertRaises(KeyError, vsi.__getitem__, 'unknown')
+
+ self.assertEqual(len(vis), 2)
+ self.assertEqual(vis[1], 'val1')
+ self.assertEqual(vis[5], 'val2')
+ self.assertRaises(KeyError, vsi.__getitem__, 3)
+
+ # dictionary iteration
+ self.assertEqual(set(vsi.keys()), set(['key1', 'key2']))
+ self.assertEqual(set(vis.keys()), set([1, 5]))
+
+ # string index access
+ v = GLib.Variant('s', 'hello')
+ self.assertEqual(len(v), 5)
+ self.assertEqual(v[0], 'h')
+ self.assertEqual(v[4], 'o')
+ self.assertEqual(v[-1], 'o')
+ self.assertEqual(v[-5], 'h')
+ self.assertRaises(IndexError, v.__getitem__, 5)
+ self.assertRaises(IndexError, v.__getitem__, -6)
+
+ # string iteration
+ self.assertEqual([x for x in v], ['h', 'e', 'l', 'l', 'o'])
+
+class TestPango(unittest.TestCase):
+
+ def test_default_font_description(self):
+ desc = Pango.FontDescription()
+ self.assertEquals(desc.get_variant(), Pango.Variant.NORMAL)
+
+ def test_font_description(self):
+ desc = Pango.FontDescription('monospace')
+ self.assertEquals(desc.get_family(), 'monospace')
+ self.assertEquals(desc.get_variant(), Pango.Variant.NORMAL)
+
+ def test_layout(self):
+ self.assertRaises(TypeError, Pango.Layout)
+ context = Pango.Context()
+ layout = Pango.Layout(context)
+ self.assertEquals(layout.get_context(), context)
class TestGdk(unittest.TestCase):
+ def test_constructor(self):
+ attribute = Gdk.WindowAttr()
+ attribute.window_type = Gdk.WindowType.CHILD
+ attributes_mask = Gdk.WindowAttributesType.X | \
+ Gdk.WindowAttributesType.Y
+ window = Gdk.Window(None, attribute, attributes_mask)
+ self.assertEquals(window.get_window_type(), Gdk.WindowType.CHILD)
+
def test_color(self):
color = Gdk.Color(100, 200, 300)
self.assertEquals(color.red, 100)
self.assertEquals(color.green, 200)
self.assertEquals(color.blue, 300)
+ def test_rgba(self):
+ self.assertEquals(Gdk.RGBA, overrides.Gdk.RGBA)
+ rgba = Gdk.RGBA(0.1, 0.2, 0.3, 0.4)
+ self.assertEquals(rgba.red, 0.1)
+ self.assertEquals(rgba.green, 0.2)
+ self.assertEquals(rgba.blue, 0.3)
+ self.assertEquals(rgba.alpha, 0.4)
+ rgba.green = 0.9
+ self.assertEquals(rgba.green, 0.9)
+
def test_event(self):
event = Gdk.Event.new(Gdk.EventType.CONFIGURE)
self.assertEquals(event.type, Gdk.EventType.CONFIGURE)
@@ -57,22 +365,93 @@ class TestGdk(unittest.TestCase):
self.assertEquals(event.x_root, 0)
self.assertEquals(event.y_root, 5)
+ event = Gdk.Event()
+ event.type = Gdk.EventType.SCROLL
+ self.assertRaises(AttributeError, lambda: getattr(event, 'foo_bar'))
+
+ def test_event_structures(self):
+ def button_press_cb(button, event):
+ self.assertTrue(isinstance(event, Gdk.EventButton))
+ self.assertTrue(event.type == Gdk.EventType.BUTTON_PRESS)
+ self.assertEquals(event.send_event, 0)
+ self.assertEquals(event.get_state(), Gdk.ModifierType.CONTROL_MASK)
+ self.assertEquals(event.get_root_coords(), (2, 5))
+
+ event.time = 12345
+ self.assertEquals(event.get_time(), 12345)
+
+ w = Gtk.Window()
+ b = Gtk.Button()
+ b.connect('button-press-event', button_press_cb)
+ w.add(b)
+ w.show_all()
+ Gdk.test_simulate_button(b.get_window(),
+ 2, 5,
+ 0,
+ Gdk.ModifierType.CONTROL_MASK,
+ Gdk.EventType.BUTTON_PRESS)
+
+ def test_cursor(self):
+ self.assertEquals(Gdk.Cursor, overrides.Gdk.Cursor)
+ c = Gdk.Cursor(Gdk.CursorType.WATCH)
+ self.assertNotEqual(c, None)
+ c = Gdk.Cursor(cursor_type = Gdk.CursorType.WATCH)
+ self.assertNotEqual(c, None)
+
+ display_manager = Gdk.DisplayManager.get()
+ display = display_manager.get_default_display()
+
+ test_pixbuf = GdkPixbuf.Pixbuf.new(GdkPixbuf.Colorspace.RGB,
+ False,
+ 8,
+ 5,
+ 10)
+
+ c = Gdk.Cursor(display,
+ test_pixbuf,
+ y=0, x=0)
+
+ self.assertNotEqual(c, None)
+ self.assertRaises(ValueError, Gdk.Cursor, 1, 2, 3)
+
class TestGtk(unittest.TestCase):
- def test_uimanager(self):
- self.assertEquals(Gtk.UIManager, overrides.Gtk.UIManager)
- ui = Gtk.UIManager()
- ui.add_ui_from_string(
-"""
-<ui>
- <menubar name="menubar1"></menubar>
-</ui>
-"""
-)
- menubar = ui.get_widget("/menubar1")
- self.assertEquals(type(menubar), Gtk.MenuBar)
+
+ def test_container(self):
+ box = Gtk.Box()
+ self.assertTrue(box)
+ label = Gtk.Label()
+ label2 = Gtk.Label()
+ box.add(label)
+ box.add(label2)
+ self.assertTrue(label in box)
+ self.assertTrue(label2 in box)
+ self.assertEqual(len(box), 2)
+ self.assertTrue(box)
+ l = [x for x in box]
+ self.assertEqual(l, [label, label2])
+
+ def test_actions(self):
+ self.assertEquals(Gtk.Action, overrides.Gtk.Action)
+ self.assertRaises(TypeError, Gtk.Action)
+ action = Gtk.Action("test", "Test", "Test Action", Gtk.STOCK_COPY)
+ self.assertEquals(action.get_name(), "test")
+ self.assertEquals(action.get_label(), "Test")
+ self.assertEquals(action.get_tooltip(), "Test Action")
+ self.assertEquals(action.get_stock_id(), Gtk.STOCK_COPY)
+
+ self.assertEquals(Gtk.RadioAction, overrides.Gtk.RadioAction)
+ self.assertRaises(TypeError, Gtk.RadioAction)
+ action = Gtk.RadioAction("test", "Test", "Test Action", Gtk.STOCK_COPY, 1)
+ self.assertEquals(action.get_name(), "test")
+ self.assertEquals(action.get_label(), "Test")
+ self.assertEquals(action.get_tooltip(), "Test Action")
+ self.assertEquals(action.get_stock_id(), Gtk.STOCK_COPY)
+ self.assertEquals(action.get_current_value(), 1)
def test_actiongroup(self):
self.assertEquals(Gtk.ActionGroup, overrides.Gtk.ActionGroup)
+ self.assertRaises(TypeError, Gtk.ActionGroup)
+
action_group = Gtk.ActionGroup (name = 'TestActionGroup')
callback_data = "callback data"
@@ -98,18 +477,40 @@ class TestGtk(unittest.TestCase):
test_radio_action_callback_data,
callback_data)
- expected_results = (('test-action1', Gtk.Action),
+ expected_results = [('test-action1', Gtk.Action),
('test-action2', Gtk.Action),
('test-toggle-action1', Gtk.ToggleAction),
('test-toggle-action2', Gtk.ToggleAction),
('test-radio-action1', Gtk.RadioAction),
- ('test-radio-action2', Gtk.RadioAction))
+ ('test-radio-action2', Gtk.RadioAction)]
- for action, cmp in zip(action_group.list_actions(), expected_results):
+ for action in action_group.list_actions():
a = (action.get_name(), type(action))
- self.assertEquals(a,cmp)
+ self.assertTrue(a in expected_results)
+ expected_results.remove(a)
action.activate()
+ def test_uimanager(self):
+ self.assertEquals(Gtk.UIManager, overrides.Gtk.UIManager)
+ ui = Gtk.UIManager()
+ ui.add_ui_from_string(
+"""
+<ui>
+ <menubar name="menubar1"></menubar>
+</ui>
+"""
+)
+ menubar = ui.get_widget("/menubar1")
+ self.assertEquals(type(menubar), Gtk.MenuBar)
+
+ ag = Gtk.ActionGroup (name="ag1")
+ ui.insert_action_group(ag)
+ ag2 = Gtk.ActionGroup (name="ag2")
+ ui.insert_action_group(ag2)
+ groups = ui.get_action_groups()
+ self.assertEquals(ag, groups[-2])
+ self.assertEquals(ag2, groups[-1])
+
def test_builder(self):
self.assertEquals(Gtk.Builder, overrides.Gtk.Builder)
@@ -213,6 +614,14 @@ class TestGtk(unittest.TestCase):
text = dialog.get_property('text')
self.assertEquals('dude!', text)
+ dialog.format_secondary_text('2nd text')
+ self.assertEqual(dialog.get_property('secondary-text'), '2nd text')
+ self.assertFalse(dialog.get_property('secondary-use-markup'))
+
+ dialog.format_secondary_markup('2nd markup')
+ self.assertEqual(dialog.get_property('secondary-text'), '2nd markup')
+ self.assertTrue(dialog.get_property('secondary-use-markup'))
+
# Gtk.ColorSelectionDialog
dialog = Gtk.ColorSelectionDialog("color selection dialog test")
self.assertEquals('color selection dialog test', dialog.get_title())
@@ -272,12 +681,46 @@ class TestGtk(unittest.TestCase):
self.assertEquals(Gtk.TreeModel, overrides.Gtk.TreeModel)
self.assertEquals(Gtk.TreeViewColumn, overrides.Gtk.TreeViewColumn)
- tree_store = Gtk.TreeStore(int, 'gchararray', TestGtk.TestClass)
+ class TestPyObject(object):
+ pass
+
+ test_pyobj = TestPyObject()
+ test_pydict = {1:1, "2":2, "3":"3"}
+ test_pylist = [1,"2", "3"]
+ tree_store = Gtk.TreeStore(int,
+ 'gchararray',
+ TestGtk.TestClass,
+ object,
+ object,
+ object,
+ bool,
+ bool,
+ GObject.TYPE_UINT,
+ GObject.TYPE_ULONG,
+ GObject.TYPE_INT64,
+ GObject.TYPE_UINT64,
+ GObject.TYPE_UCHAR,
+ GObject.TYPE_CHAR)
+
parent = None
for i in range(100):
label = 'this is child #%d' % i
testobj = TestGtk.TestClass(self, i, label)
- parent = tree_store.append(parent, (i, label, testobj))
+ parent = tree_store.append(parent, (i,
+ label,
+ testobj,
+ test_pyobj,
+ test_pydict,
+ test_pylist,
+ i % 2,
+ bool(i % 2),
+ i,
+ 9223372036854775808,
+ -9223372036854775808,
+ 0xffffffffffffffff,
+ 254,
+ _bytes('a')
+ ))
# len gets the number of children in the root node
# since we kept appending to the previous node
@@ -294,17 +737,162 @@ class TestGtk(unittest.TestCase):
s = tree_store.get_value(treeiter, 1)
obj = tree_store.get_value(treeiter, 2)
obj.check(i, s)
+
+ pyobj = tree_store.get_value(treeiter, 3)
+ self.assertEquals(pyobj, test_pyobj)
+ pydict = tree_store.get_value(treeiter, 4)
+ self.assertEquals(pydict, test_pydict)
+ pylist = tree_store.get_value(treeiter, 5)
+ self.assertEquals(pylist, test_pylist)
+
+ bool_1 = tree_store.get_value(treeiter, 6)
+ bool_2 = tree_store.get_value(treeiter, 7)
+ self.assertEquals(bool_1, bool_2)
+ self.assertTrue(isinstance(bool_1, bool))
+ self.assertTrue(isinstance(bool_2, bool))
+
+ uint_ = tree_store.get_value(treeiter, 8)
+ self.assertEquals(uint_, i)
+ ulong_ = tree_store.get_value(treeiter, 9)
+ self.assertEquals(ulong_, 9223372036854775808)
+ int64_ = tree_store.get_value(treeiter, 10)
+ self.assertEquals(int64_, -9223372036854775808)
+ uint64_ = tree_store.get_value(treeiter, 11)
+ self.assertEquals(uint64_, 0xffffffffffffffff)
+ uchar_ = tree_store.get_value(treeiter, 12)
+ self.assertEquals(ord(uchar_), 254)
+ char_ = tree_store.get_value(treeiter, 13)
+ self.assertEquals(char_, 'a')
+
parent = treeiter
treeiter = tree_store.iter_children(parent)
self.assertEquals(i, 99)
def test_list_store(self):
- list_store = Gtk.ListStore(int, str, 'GIOverrideTreeAPITest')
- for i in range(100):
+ class TestPyObject(object):
+ pass
+
+ class TestPyGObject(GObject.Object):
+ __gtype_name__ = 'TestPyGObject'
+ def __init__(self, i):
+ GObject.Object.__init__(self)
+ self.sentinal = i + 5
+
+ test_pyobj = TestPyObject()
+ test_pydict = {1:1, "2":2, "3":"3"}
+ test_pylist = [1,"2", "3"]
+
+ list_store = Gtk.ListStore(int, str, 'GIOverrideTreeAPITest', object, object, object, bool, bool, object)
+ for i in range(93):
label = 'this is row #%d' % i
testobj = TestGtk.TestClass(self, i, label)
- parent = list_store.append((i, label, testobj))
+ testpygobj = TestPyGObject(i)
+ parent = list_store.append((i,
+ label,
+ testobj,
+ test_pyobj,
+ test_pydict,
+ test_pylist,
+ i % 2,
+ bool(i % 2),
+ testpygobj))
+
+ i = 93
+ label = _unicode('this is row #93')
+ treeiter = list_store.append()
+ list_store.set_value(treeiter, 0, i)
+ list_store.set_value(treeiter, 1, label)
+ list_store.set_value(treeiter, 2, TestGtk.TestClass(self, i, label))
+ list_store.set_value(treeiter, 3, test_pyobj)
+ list_store.set_value(treeiter, 4, test_pydict)
+ list_store.set_value(treeiter, 5, test_pylist)
+ list_store.set_value(treeiter, 6, 1)
+ list_store.set_value(treeiter, 7, True)
+ list_store.set_value(treeiter, 8, TestPyGObject(i))
+
+ # test automatic unicode->str conversion
+ i = 94
+ label = _unicode('this is row #94')
+ treeiter = list_store.append((i,
+ label,
+ TestGtk.TestClass(self, i, label),
+ test_pyobj,
+ test_pydict,
+ test_pylist,
+ 0,
+ False,
+ TestPyGObject(i)))
+
+ # add sorted items out of order to test insert* apis
+ # also test sending in None to not set a column
+ i = 97
+ label = 'this is row #97'
+ treeiter = list_store.append((None,
+ None,
+ None,
+ test_pyobj,
+ None,
+ test_pylist,
+ 1,
+ None,
+ TestPyGObject(i)))
+
+ list_store.set_value(treeiter, 0, i)
+ list_store.set_value(treeiter, 1, label)
+ list_store.set_value(treeiter, 2, TestGtk.TestClass(self, i, label))
+ list_store.set_value(treeiter, 4, test_pydict)
+ list_store.set_value(treeiter, 7, True)
+
+ # this should append
+ i = 99
+ label = 'this is row #99'
+ list_store.insert(9999, (i,
+ label,
+ TestGtk.TestClass(self, i, label),
+ test_pyobj,
+ test_pydict,
+ test_pylist,
+ 1,
+ True,
+ TestPyGObject(i)))
+
+ i = 96
+ label = 'this is row #96'
+ list_store.insert_before(treeiter, (i,
+ label,
+ TestGtk.TestClass(self, i, label),
+ test_pyobj,
+ test_pydict,
+ test_pylist,
+ 0,
+ False,
+ TestPyGObject(i)))
+
+ i = 98
+ label = 'this is row #98'
+ list_store.insert_after(treeiter, (i,
+ label,
+ TestGtk.TestClass(self, i, label),
+ test_pyobj,
+ test_pydict,
+ test_pylist,
+ 0,
+ False,
+ TestPyGObject(i)))
+
+
+ i = 95
+ label = 'this is row #95'
+ list_store.insert(95, (i,
+ label,
+ TestGtk.TestClass(self, i, label),
+ test_pyobj,
+ test_pydict,
+ test_pylist,
+ 1,
+ True,
+ TestPyGObject(i)))
self.assertEquals(len(list_store), 100)
@@ -312,15 +900,60 @@ class TestGtk(unittest.TestCase):
i = 0
treeiter = list_store.get_iter_first()
+ counter = 0
while treeiter:
i = list_store.get_value(treeiter, 0)
+ self.assertEquals(i, counter)
s = list_store.get_value(treeiter, 1)
obj = list_store.get_value(treeiter, 2)
obj.check(i, s)
+
+ pyobj = list_store.get_value(treeiter, 3)
+ self.assertEquals(pyobj, test_pyobj)
+ pydict = list_store.get_value(treeiter, 4)
+ self.assertEquals(pydict, test_pydict)
+ pylist = list_store.get_value(treeiter, 5)
+ self.assertEquals(pylist, test_pylist)
+
+ bool_1 = list_store.get_value(treeiter, 6)
+ bool_2 = list_store.get_value(treeiter, 7)
+ self.assertEquals(bool_1, bool_2)
+ self.assertTrue(isinstance(bool_1, bool))
+ self.assertTrue(isinstance(bool_2, bool))
+
+ pygobj = list_store.get_value(treeiter, 8)
+ self.assertEquals(pygobj.sentinal, i + 5)
+
treeiter = list_store.iter_next(treeiter)
+ counter += 1
+
self.assertEquals(i, 99)
+ def test_tree_path(self):
+ p1 = Gtk.TreePath()
+ p2 = Gtk.TreePath.new_first()
+ self.assertEqual(p1, p2)
+ self.assertEqual(str(p1), '0')
+ p1 = Gtk.TreePath(2)
+ p2 = Gtk.TreePath.new_from_string('2')
+ self.assertEqual(p1, p2)
+ self.assertEqual(str(p1), '2')
+ p1 = Gtk.TreePath('1:2:3')
+ p2 = Gtk.TreePath.new_from_string('1:2:3')
+ self.assertEqual(p1, p2)
+ self.assertEqual(str(p1), '1:2:3')
+ p1 = Gtk.TreePath((1,2,3))
+ p2 = Gtk.TreePath.new_from_string('1:2:3')
+ self.assertEqual(p1, p2)
+ self.assertEqual(str(p1), '1:2:3')
+ self.assertTrue(p1 != None)
+ self.assertFalse(p1 == None)
+ self.assertTrue(p1 > None)
+ self.assertTrue(p1 >= None)
+ self.assertFalse(p1 < None)
+ self.assertFalse(p1 <= None)
+
def test_tree_model(self):
tree_store = Gtk.TreeStore(int, str)
@@ -351,9 +984,15 @@ class TestGtk(unittest.TestCase):
for row in tree_store:
self.fail("Should not be reached")
+ class DerivedIntType(int):
+ pass
+
+ class DerivedStrType(str):
+ pass
+
for i in range(100):
label = 'this is row #%d' % i
- parent = tree_store.append(None, (i, label,))
+ parent = tree_store.append(None, (DerivedIntType(i), DerivedStrType(label),))
self.assertNotEquals(parent, None)
for j in range(20):
label = 'this is child #%d of node #%d' % (j, i)
@@ -469,6 +1108,12 @@ class TestGtk(unittest.TestCase):
for childrow in last_row.iterchildren():
self.fail("Should not be reached")
+ aiter = tree_store.get_iter(10)
+ self.assertRaises(TypeError, tree_store.get, aiter, 1, 'a')
+ self.assertRaises(ValueError, tree_store.get, aiter, 1, -1)
+ self.assertRaises(ValueError, tree_store.get, aiter, 1, 100)
+ self.assertEqual(tree_store.get(aiter, 0, 1), (10, 'this is row #10'))
+
def test_tree_view_column(self):
cell = Gtk.CellRendererText()
column = Gtk.TreeViewColumn(title='This is just a test',
@@ -476,6 +1121,35 @@ class TestGtk(unittest.TestCase):
text=0,
style=2)
+ def test_tree_selection(self):
+ store = Gtk.ListStore(int, str)
+ for i in range(10):
+ store.append((i, "foo"))
+ view = Gtk.TreeView()
+ view.set_model(store)
+ firstpath = store.get_path(store.get_iter_first())
+ sel = view.get_selection()
+
+ sel.select_path(firstpath)
+ (m, s) = sel.get_selected()
+ self.assertEqual(m, store)
+ self.assertEqual(store.get_path(s), firstpath)
+
+ sel.select_path(0)
+ (m, s) = sel.get_selected()
+ self.assertEqual(m, store)
+ self.assertEqual(store.get_path(s), firstpath)
+
+ sel.select_path("0:0")
+ (m, s) = sel.get_selected()
+ self.assertEqual(m, store)
+ self.assertEqual(store.get_path(s), firstpath)
+
+ sel.select_path((0,0))
+ (m, s) = sel.get_selected()
+ self.assertEqual(m, store)
+ self.assertEqual(store.get_path(s), firstpath)
+
def test_text_buffer(self):
self.assertEquals(Gtk.TextBuffer, overrides.Gtk.TextBuffer)
buffer = Gtk.TextBuffer()
@@ -486,6 +1160,19 @@ class TestGtk(unittest.TestCase):
(start, end) = buffer.get_bounds()
+ mark = buffer.create_mark(None, start)
+ self.assertFalse(mark.get_left_gravity())
+
+ buffer.set_text('Hello Jane Hello Bob')
+ (start, end) = buffer.get_bounds()
+ text = buffer.get_text(start, end, False)
+ self.assertEquals(text, 'Hello Jane Hello Bob')
+
+ buffer.set_text('')
+ (start, end) = buffer.get_bounds()
+ text = buffer.get_text(start, end, False)
+ self.assertEquals(text, '')
+
buffer.insert(end, 'HelloHello')
buffer.insert(end, ' Bob')
@@ -496,9 +1183,47 @@ class TestGtk(unittest.TestCase):
(start, end) = buffer.get_bounds()
text = buffer.get_text(start, end, False)
-
self.assertEquals(text, 'Hello Jane Hello Bob')
+ sel = buffer.get_selection_bounds()
+ self.assertEquals(sel, ())
+ buffer.select_range(start, end)
+ sel = buffer.get_selection_bounds()
+ self.assertTrue(sel[0].equal(start))
+ self.assertTrue(sel[1].equal(end))
+
+ buffer.set_text('')
+ buffer.insert_with_tags(buffer.get_start_iter(), 'HelloHello', tag)
+ (start, end) = buffer.get_bounds()
+ self.assertTrue(start.begins_tag(tag))
+ self.assertTrue(start.has_tag(tag))
+
+ buffer.set_text('')
+ buffer.insert_with_tags_by_name(buffer.get_start_iter(), 'HelloHello', 'title')
+ (start, end) = buffer.get_bounds()
+ self.assertTrue(start.begins_tag(tag))
+ self.assertTrue(start.has_tag(tag))
+
+ self.assertRaises(ValueError, buffer.insert_with_tags_by_name,
+ buffer.get_start_iter(), 'HelloHello', 'unknowntag')
+
+ def test_text_iter(self):
+ self.assertEquals(Gtk.TextIter, overrides.Gtk.TextIter)
+ buffer = Gtk.TextBuffer()
+ buffer.set_text('Hello Jane Hello Bob')
+ tag = buffer.create_tag ('title', font = 'Sans 18')
+ (start, end) = buffer.get_bounds()
+ start.forward_chars(10)
+ buffer.apply_tag(tag, start, end)
+ self.assertTrue(start.begins_tag())
+ self.assertTrue(end.ends_tag())
+ self.assertTrue(start.toggles_tag())
+ self.assertTrue(end.toggles_tag())
+ start.backward_chars(1)
+ self.assertFalse(start.begins_tag())
+ self.assertFalse(start.ends_tag())
+ self.assertFalse(start.toggles_tag())
+
def test_buttons(self):
self.assertEquals(Gtk.Button, overrides.Gtk.Button)
@@ -508,3 +1233,254 @@ class TestGtk(unittest.TestCase):
self.assertEquals(Gtk.STOCK_CLOSE, button.get_label())
self.assertTrue(button.get_use_stock())
self.assertTrue(button.get_use_underline())
+
+ # test Gtk.LinkButton
+ self.assertRaises(TypeError, Gtk.LinkButton)
+ button = Gtk.LinkButton('http://www.gtk.org', 'Gtk')
+ self.assertEquals('http://www.gtk.org', button.get_uri())
+ self.assertEquals('Gtk', button.get_label())
+
+ def test_inheritance(self):
+ for name in overrides.Gtk.__all__:
+ over = getattr(overrides.Gtk, name)
+ for element in dir(Gtk):
+ try:
+ klass = getattr(Gtk, element)
+ info = klass.__info__
+ except (NotImplementedError, AttributeError):
+ continue
+
+ # Get all parent classes and interfaces klass inherits from
+ if isinstance(info, gi.types.ObjectInfo):
+ classes = list(info.get_interfaces())
+ parent = info.get_parent()
+ while parent.get_name() != "Object":
+ classes.append(parent)
+ parent = parent.get_parent()
+ classes = [kl for kl in classes if kl.get_namespace() == "Gtk"]
+ else:
+ continue
+
+ for kl in classes:
+ if kl.get_name() == name:
+ self.assertTrue(issubclass(klass, over,),
+ "%r does not inherit from override %r" % (klass, over,))
+
+ def test_editable(self):
+ self.assertEquals(Gtk.Editable, overrides.Gtk.Editable)
+
+ # need to use Gtk.Entry because Editable is an interface
+ entry=Gtk.Entry()
+ pos = entry.insert_text('HeWorld', 0)
+ self.assertEquals(pos, 7)
+ pos = entry.insert_text('llo ', 2)
+ self.assertEquals(pos, 6)
+ text = entry.get_chars(0, 11)
+ self.assertEquals('Hello World', text)
+
+ def test_label(self):
+ label = Gtk.Label('Hello')
+ self.assertEquals(label.get_text(), 'Hello')
+
+ def adjustment_check(self, adjustment, value=0.0, lower=0.0, upper=0.0,
+ step_increment=0.0, page_increment=0.0, page_size=0.0):
+ self.assertEquals(adjustment.get_value(), value)
+ self.assertEquals(adjustment.get_lower(), lower)
+ self.assertEquals(adjustment.get_upper(), upper)
+ self.assertEquals(adjustment.get_step_increment(), step_increment)
+ self.assertEquals(adjustment.get_page_increment(), page_increment)
+ self.assertEquals(adjustment.get_page_size(), page_size)
+
+ def test_adjustment(self):
+ adjustment = Gtk.Adjustment(1, 0, 6, 4, 5, 3)
+ self.adjustment_check(adjustment, 1, 0, 6, 4, 5, 3)
+
+ adjustment = Gtk.Adjustment(1, 0, 6, 4, 5)
+ self.adjustment_check(adjustment, 1, 0, 6, 4, 5)
+
+ adjustment = Gtk.Adjustment(1, 0, 6, 4)
+ self.adjustment_check(adjustment, 1, 0, 6, 4)
+
+ adjustment = Gtk.Adjustment(1, 0, 6)
+ self.adjustment_check(adjustment, 1, 0, 6)
+
+ adjustment = Gtk.Adjustment()
+ self.adjustment_check(adjustment)
+
+ adjustment = Gtk.Adjustment(value=1, lower=0, upper=6,
+ step_increment=4, page_increment=5, page_size=3)
+ self.adjustment_check(adjustment, 1, 0, 6, 4, 5, 3)
+
+ def test_table(self):
+ table = Gtk.Table()
+ self.assertEquals(table.get_size(), (1,1))
+ self.assertEquals(table.get_homogeneous(), False)
+ table = Gtk.Table(2, 3)
+ self.assertEquals(table.get_size(), (2,3))
+ self.assertEquals(table.get_homogeneous(), False)
+ table = Gtk.Table(2, 3, True)
+ self.assertEquals(table.get_size(), (2,3))
+ self.assertEquals(table.get_homogeneous(), True)
+
+ # Test PyGTK interface
+ table = Gtk.Table(rows=3, columns=2)
+ self.assertEquals(table.get_size(), (3,2))
+ # Test using the actual property names
+ table = Gtk.Table(n_rows=2, n_columns=3, homogeneous=True)
+ self.assertEquals(table.get_size(), (2,3))
+ self.assertEquals(table.get_homogeneous(), True)
+
+ label = Gtk.Label('Hello')
+ table.attach(label, 0, 1, 0, 1)
+ self.assertEquals(label, table.get_children()[0])
+
+ def test_scrolledwindow(self):
+ sw = Gtk.ScrolledWindow()
+ sb = sw.get_hscrollbar()
+ self.assertEquals(sw.get_hadjustment(), sb.get_adjustment())
+ sb = sw.get_vscrollbar()
+ self.assertEquals(sw.get_vadjustment(), sb.get_adjustment())
+
+ def test_widget_drag_methods(self):
+ widget = Gtk.Button()
+
+ # here we are not checking functionality, only that the methods exist
+ # and except the right number of arguments
+
+ widget.drag_check_threshold(0, 0, 0, 0)
+
+ # drag_dest_ methods
+ widget.drag_dest_set(Gtk.DestDefaults.DROP, None, Gdk.DragAction.COPY)
+ widget.drag_dest_add_image_targets()
+ widget.drag_dest_add_text_targets()
+ widget.drag_dest_add_uri_targets()
+ widget.drag_dest_get_track_motion()
+ widget.drag_dest_set_track_motion(True)
+ widget.drag_dest_get_target_list()
+ widget.drag_dest_set_target_list(Gtk.TargetList.new([Gtk.TargetEntry()]))
+ widget.drag_dest_unset()
+
+ widget.drag_highlight()
+ widget.drag_unhighlight()
+
+ # drag_source_ methods
+ widget.drag_source_set(Gdk.ModifierType.BUTTON1_MASK, None, Gdk.DragAction.MOVE)
+ widget.drag_source_add_image_targets()
+ widget.drag_source_add_text_targets()
+ widget.drag_source_add_uri_targets()
+ widget.drag_source_set_icon_name("")
+ widget.drag_source_set_icon_pixbuf(GdkPixbuf.Pixbuf())
+ widget.drag_source_set_icon_stock("")
+ widget.drag_source_get_target_list()
+ widget.drag_source_set_target_list(Gtk.TargetList.new([Gtk.TargetEntry()]))
+ widget.drag_source_unset()
+
+ # these methods cannot be called because they require a valid drag on
+ # a real GdkWindow. So we only check that they exist and are callable.
+ self.assertTrue(hasattr(widget.drag_dest_set_proxy, '__call__'))
+ self.assertTrue(hasattr(widget.drag_get_data, '__call__'))
+
+class TestGio(unittest.TestCase):
+ def setUp(self):
+ os.environ['GSETTINGS_BACKEND'] = 'memory'
+ # support a separate build tree, so look in build dir first
+ os.environ['GSETTINGS_SCHEMA_DIR'] = os.environ.get('TESTS_BUILDDIR',
+ os.path.dirname(__file__))
+ self.settings = Gio.Settings('org.gnome.test')
+ # we change the values in the tests, so set them to predictable start
+ # value
+ self.settings.reset('test-string')
+ self.settings.reset('test-array')
+
+ def test_file_enumerator(self):
+ self.assertEquals(Gio.FileEnumerator, overrides.Gio.FileEnumerator)
+ f = Gio.file_new_for_path("./")
+
+ iter_info = []
+ for info in f.enumerate_children("standard::*", 0, None):
+ iter_info.append(info.get_name())
+
+ next_info = []
+ enumerator = f.enumerate_children("standard::*", 0, None)
+ while True:
+ info = enumerator.next_file(None)
+ if info is None:
+ break
+ next_info.append(info.get_name())
+
+ self.assertEquals(iter_info, next_info)
+
+ def test_gsettings_native(self):
+ self.assertTrue('test-array' in self.settings.list_keys())
+
+ # get various types
+ v = self.settings.get_value('test-boolean')
+ self.assertEqual(v.get_boolean(), True)
+ self.assertEqual(self.settings.get_boolean('test-boolean'), True)
+
+ v = self.settings.get_value('test-string')
+ self.assertEqual(v.get_string(), 'Hello')
+ self.assertEqual(self.settings.get_string('test-string'), 'Hello')
+
+ v = self.settings.get_value('test-array')
+ self.assertEqual(v.unpack(), [1, 2])
+
+ v = self.settings.get_value('test-tuple')
+ self.assertEqual(v.unpack(), (1, 2))
+
+ # set a value
+ self.settings.set_string('test-string', 'World')
+ self.assertEqual(self.settings.get_string('test-string'), 'World')
+
+ self.settings.set_value('test-string', GLib.Variant('s', 'Goodbye'))
+ self.assertEqual(self.settings.get_string('test-string'), 'Goodbye')
+
+ def test_gsettings_constructor(self):
+ # default constructor uses path from schema
+ self.assertEqual(self.settings.get_property('path'), '/tests/')
+
+ # optional constructor arguments
+ with_path = Gio.Settings('org.gnome.nopathtest', path='/mypath/')
+ self.assertEqual(with_path.get_property('path'), '/mypath/')
+ self.assertEqual(with_path['np-int'], 42)
+
+ def test_gsettings_override(self):
+ # dictionary interface
+ self.assertEqual(len(self.settings), 4)
+ self.assertTrue('test-array' in self.settings)
+ self.assertTrue('test-array' in self.settings.keys())
+ self.failIf('nonexisting' in self.settings)
+ self.failIf(4 in self.settings)
+ self.assertEqual(bool(self.settings), True)
+
+ # get various types
+ self.assertEqual(self.settings['test-boolean'], True)
+ self.assertEqual(self.settings['test-string'], 'Hello')
+ self.assertEqual(self.settings['test-array'], [1, 2])
+ self.assertEqual(self.settings['test-tuple'], (1, 2))
+
+ self.assertRaises(KeyError, self.settings.__getitem__, 'unknown')
+ self.assertRaises(KeyError, self.settings.__getitem__, 2)
+
+ # set a value
+ self.settings['test-string'] = 'Goodbye'
+ self.assertEqual(self.settings['test-string'], 'Goodbye')
+ self.settings['test-array'] = [3, 4, 5]
+ self.assertEqual(self.settings['test-array'], [3, 4, 5])
+
+ self.assertRaises(TypeError, self.settings.__setitem__, 'test-string', 1)
+ self.assertRaises(KeyError, self.settings.__setitem__, 'unknown', 'moo')
+
+ def test_gsettings_empty(self):
+ empty = Gio.Settings('org.gnome.empty', path='/tests/')
+ self.assertEqual(len(empty), 0)
+ self.assertEqual(bool(empty), True)
+ self.assertEqual(empty.keys(), [])
+
+ def test_closures(self):
+ # make sure this doesn't crash
+ def fake_cb(*args):
+ pass
+
+ ag = Gtk.AccelGroup()
+ ag.connect(Gdk.KEY_l, Gdk.ModifierType.CONTROL_MASK, 0, fake_cb)
diff --git a/tests/test_properties.py b/tests/test_properties.py
index 19e1136d..54afd119 100644
--- a/tests/test_properties.py
+++ b/tests/test_properties.py
@@ -1,3 +1,4 @@
+# coding=utf-8
import sys
import struct
@@ -13,6 +14,13 @@ from gobject.constants import \
G_MININT, G_MAXINT, G_MAXUINT, G_MINLONG, G_MAXLONG, \
G_MAXULONG
+if sys.version_info < (3, 0):
+ TEST_UTF8 = "\xe2\x99\xa5"
+ UNICODE_UTF8 = unicode(TEST_UTF8, 'UTF-8')
+else:
+ TEST_UTF8 = "♥"
+ UNICODE_UTF8 = TEST_UTF8
+
from compathelper import _long
class PropertyObject(GObject):
@@ -71,6 +79,22 @@ class TestProperties(unittest.TestCase):
self.assertEqual(obj.props.construct, "456")
obj.props.construct = '789'
self.assertEqual(obj.props.construct, "789")
+
+ def testUTF8(self):
+ obj = new(PropertyObject, construct_only=UNICODE_UTF8)
+ self.assertEqual(obj.props.construct_only, TEST_UTF8)
+ obj.set_property('construct', UNICODE_UTF8)
+ self.assertEqual(obj.props.construct, TEST_UTF8)
+ obj.props.normal = UNICODE_UTF8
+ self.assertEqual(obj.props.normal, TEST_UTF8)
+
+ def testIntToStr(self):
+ obj = new(PropertyObject, construct_only=1)
+ self.assertEqual(obj.props.construct_only, '1')
+ obj.set_property('construct', '2')
+ self.assertEqual(obj.props.construct, '2')
+ obj.props.normal = 3
+ self.assertEqual(obj.props.normal, '3')
def testConstructOnly(self):
obj = new(PropertyObject, construct_only="123")
@@ -320,6 +344,15 @@ class TestProperty(unittest.TestCase):
pobj1 = pobj2.obj
self.assertEqual(hash(pobj1), obj1_hash)
+ def testObjectSubclassProperty(self):
+ class ObjectSubclass(GObject):
+ __gtype_name__ = 'ObjectSubclass'
+
+ class PropertyObjectSubclass(GObject):
+ obj = gobject.property(type=ObjectSubclass)
+
+ obj1 = PropertyObjectSubclass(obj=ObjectSubclass())
+
def testPropertySubclass(self):
# test for #470718
class A(GObject):
@@ -376,5 +409,28 @@ class TestProperty(unittest.TestCase):
gobject.property(type=gobject.TYPE_FLOAT, minimum=-1)
gobject.property(type=gobject.TYPE_DOUBLE, minimum=-1)
+ # Bug 644039
+ def testReferenceCount(self):
+ # We can check directly if an object gets finalized, so we will
+ # observe it indirectly through the refcount of a member object.
+
+ # We create our dummy object and store its current refcount
+ o = object()
+ rc = sys.getrefcount(o)
+
+ # We add our object as a member to our newly created object we
+ # want to observe. Its refcount is increased by one.
+ t = PropertyObject(normal="test")
+ t.o = o
+ self.assertEquals(sys.getrefcount(o), rc + 1)
+
+ # Now we want to ensure we do not leak any references to our
+ # object with properties. If no ref is leaked, then when deleting
+ # the local reference to this object, its reference count shoud
+ # drop to zero, and our dummy object should loose one reference.
+ del t
+ self.assertEquals(sys.getrefcount(o), rc)
+
+
if __name__ == '__main__':
unittest.main()
diff --git a/tests/testhelpermodule.c b/tests/testhelpermodule.c
index 5610a5d2..4d1b44d0 100644
--- a/tests/testhelpermodule.c
+++ b/tests/testhelpermodule.c
@@ -230,6 +230,37 @@ PYGLIB_DEFINE_TYPE("testhelper.FloatingWithSinkFunc", PyTestFloatingWithSinkFunc
/* TestFloatingWithoutSinkFunc */
PYGLIB_DEFINE_TYPE("testhelper.FloatingWithoutSinkFunc", PyTestFloatingWithoutSinkFunc_Type, PyGObject);
+/* TestOwnedByLibrary */
+PYGLIB_DEFINE_TYPE("testhelper.OwnedByLibrary", PyTestOwnedByLibrary_Type, PyGObject);
+
+static PyObject *
+_wrap_test_owned_by_library_release (PyGObject *self)
+{
+ test_owned_by_library_release (TEST_OWNED_BY_LIBRARY (self->obj));
+ return Py_None;
+}
+
+static const PyMethodDef _PyTestOwnedByLibrary_methods[] = {
+ { "release", (PyCFunction)_wrap_test_owned_by_library_release, METH_NOARGS, NULL },
+ { NULL, NULL, 0, NULL }
+};
+
+/* TestFloatingAndSunk */
+PYGLIB_DEFINE_TYPE("testhelper.FloatingAndSunk", PyTestFloatingAndSunk_Type, PyGObject);
+
+static PyObject *
+_wrap_test_floating_and_sunk_release (PyGObject *self)
+{
+ test_floating_and_sunk_release (TEST_FLOATING_AND_SUNK (self->obj));
+ return Py_None;
+}
+
+static const PyMethodDef _PyTestFloatingAndSunk_methods[] = {
+ { "release", (PyCFunction)_wrap_test_floating_and_sunk_release, METH_NOARGS, NULL },
+ { NULL, NULL, 0, NULL }
+};
+
+
#include <string.h>
#include <glib-object.h>
@@ -431,6 +462,52 @@ _wrap_test_gerror_exception(PyObject *self, PyObject *args)
return Py_None;
}
+static PyObject *
+_wrap_test_owned_by_library_get_instance_list (PyObject *self)
+{
+ PyObject *py_list, *py_obj;
+ GSList *list, *tmp;
+
+ list = test_owned_by_library_get_instance_list ();
+
+ if ((py_list = PyList_New (0)) == NULL) {
+ return NULL;
+ }
+ for (tmp = list; tmp != NULL; tmp = tmp->next) {
+ py_obj = pygobject_new (G_OBJECT (tmp->data));
+ if (py_obj == NULL) {
+ Py_DECREF (py_list);
+ return NULL;
+ }
+ PyList_Append (py_list, py_obj);
+ Py_DECREF (py_obj);
+ }
+ return py_list;
+}
+
+static PyObject *
+_wrap_test_floating_and_sunk_get_instance_list (PyObject *self)
+{
+ PyObject *py_list, *py_obj;
+ GSList *list, *tmp;
+
+ list = test_floating_and_sunk_get_instance_list ();
+
+ if ((py_list = PyList_New (0)) == NULL) {
+ return NULL;
+ }
+ for (tmp = list; tmp != NULL; tmp = tmp->next) {
+ py_obj = pygobject_new (G_OBJECT (tmp->data));
+ if (py_obj == NULL) {
+ Py_DECREF (py_list);
+ return NULL;
+ }
+ PyList_Append (py_list, py_obj);
+ Py_DECREF (py_obj);
+ }
+ return py_list;
+}
+
static PyMethodDef testhelper_functions[] = {
{ "get_test_thread", (PyCFunction)_wrap_get_test_thread, METH_NOARGS },
{ "get_unknown", (PyCFunction)_wrap_get_unknown, METH_NOARGS },
@@ -440,6 +517,8 @@ static PyMethodDef testhelper_functions[] = {
{ "test_value", (PyCFunction)_wrap_test_value, METH_VARARGS },
{ "test_value_array", (PyCFunction)_wrap_test_value_array, METH_VARARGS },
{ "test_gerror_exception", (PyCFunction)_wrap_test_gerror_exception, METH_VARARGS },
+ { "owned_by_library_get_instance_list", (PyCFunction)_wrap_test_owned_by_library_get_instance_list, METH_NOARGS },
+ { "floating_and_sunk_get_instance_list", (PyCFunction)_wrap_test_floating_and_sunk_get_instance_list, METH_NOARGS },
{ NULL, NULL }
};
@@ -448,7 +527,7 @@ PYGLIB_MODULE_START(testhelper, "testhelper")
PyObject *m, *d;
g_thread_init(NULL);
- init_pygobject();
+ pygobject_init(-1, -1, -1);
d = PyModule_GetDict(module);
@@ -459,12 +538,12 @@ PYGLIB_MODULE_START(testhelper, "testhelper")
if (_PyGObject_Type == NULL) {
PyErr_SetString(PyExc_ImportError,
"cannot import name GObject from gobject");
- return ;
+ return PYGLIB_MODULE_ERROR_RETURN;
}
} else {
PyErr_SetString(PyExc_ImportError,
"could not import gobject");
- return ;
+ return PYGLIB_MODULE_ERROR_RETURN;
}
/* TestInterface */
@@ -507,6 +586,28 @@ PYGLIB_MODULE_START(testhelper, "testhelper")
Py_BuildValue("(O)",
&PyGObject_Type));
pyg_set_object_has_new_constructor(TEST_TYPE_FLOATING_WITHOUT_SINK_FUNC);
+
+ /* TestOwnedByLibrary */
+ PyTestOwnedByLibrary_Type.tp_flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE);
+ PyTestOwnedByLibrary_Type.tp_methods = (struct PyMethodDef*)_PyTestOwnedByLibrary_methods;
+ PyTestOwnedByLibrary_Type.tp_weaklistoffset = offsetof(PyGObject, weakreflist);
+ PyTestOwnedByLibrary_Type.tp_dictoffset = offsetof(PyGObject, inst_dict);
+ pygobject_register_class(d, "OwnedByLibrary", TEST_TYPE_OWNED_BY_LIBRARY,
+ &PyTestOwnedByLibrary_Type,
+ Py_BuildValue("(O)",
+ &PyGObject_Type));
+ pyg_set_object_has_new_constructor(TEST_TYPE_OWNED_BY_LIBRARY);
+
+ /* TestFloatingAndSunk */
+ PyTestFloatingAndSunk_Type.tp_flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE);
+ PyTestFloatingAndSunk_Type.tp_methods = (struct PyMethodDef*)_PyTestFloatingAndSunk_methods;
+ PyTestFloatingAndSunk_Type.tp_weaklistoffset = offsetof(PyGObject, weakreflist);
+ PyTestFloatingAndSunk_Type.tp_dictoffset = offsetof(PyGObject, inst_dict);
+ pygobject_register_class(d, "FloatingAndSunk", TEST_TYPE_FLOATING_AND_SUNK,
+ &PyTestFloatingAndSunk_Type,
+ Py_BuildValue("(O)",
+ &PyGObject_Type));
+ pyg_set_object_has_new_constructor(TEST_TYPE_FLOATING_AND_SUNK);
}
PYGLIB_MODULE_END