What's New in PyGTK 2.8

Tighter GObject/Python Integration

Now GObject's are better integrated into python. For one thing, it is no longer needed to call gobject.type_register(MyClass) on subclasses, ever; this is now done automatically, through the use of metaclasses. Programs still using gobject.type_register will continue to work, but receive a deprecation warning.

Now you can also specify an alternate GType name for the class by defining the __gtype_name__ attribute on the class.

An important bug was solved that prevented gobject.new for working correctly in the presence of Python GObject subclasses. Now you can create Python GObjects through gobject.new, passing construction properties as you wish. However, please note that to make a Python class usable through gobject.new, its constructor must not have any mandatory arguments. You have to make all your arguments optional, or simply take no arguments at all and declare GObject construction properties. Example:

import gobject

class MyObject(gobject.GObject):
    __gtype_name__ = 'MyObject'
    __gproperties__ = {
        'myprop': (str, 'blurb', 'description', 'default',
                   gobject.PARAM_WRITABLE|gobject.PARAM_CONSTRUCT_ONLY),
    }

    def __init__(self):
        gobject.GObject.__init__(self)
        self._value = None

    def do_set_property(self, pspec, value):
        if pspec.name == 'myprop':
            self._value = value
            print "I have received myprop = '%s'" % value
        else:
            raise AssertionError

print gobject.new(MyObject, myprop="hello")
print gobject.new(MyObject)

This should print something like:

I have received myprop = 'hello'
<MyObject object (MyObject) at 0x80f04dc>
I have received myprop = 'default'
<MyObject object (MyObject) at 0x80f06ac>

Your type should also be easily usable from C code, or even other language bindings. Example:

   GObject *myobj = g_object_new(g_type_from_name("MyObject"), "myprop", "hello", NULL);

This should also allow better integration of custom widgets with libglade or gazpacho.

Better GType and Paramspec support

GType is an object that encapsulates all data types, from integers to GObjects, as seen by the GLib dynamic type system. The GType python object has been much improved since PyGTK 2.6.x. Most importantly, you can get the Python type for any given GType now, as well as find out all sorts of interesting information about GTypes, such as what GTypes derive from this one, what are the parent types, is it abstract, etc.

GParamSpec wrappers (objects that describe GObject properties) received a similar treatment, with more information made available to python.

  >>> gtk.SpinButton.props.value.maximum
  1.7976931348623157e+308

More Pythonic APIs

Properties as Object Attributes

From now on, all GObjects have a new props attribute giving the programmer full access to GObject properties as simple attributes. Now you can write this:

   label.props.angle = 0

instead of this:

   label.set_property("angle", 0)

Neat, huh? Moreover, you can iterate over the props object, yielding ParamSpecs for all properties that are valid for the class.

>>> [param.name for param in gtk.Label.props]
['user-data', 'name', 'parent', 'width-request', 'height-request', 'visible', 'sensitive',
 'app-paintable', 'can-focus', 'has-focus', 'is-focus', 'can-default', 'has-default', 'receives-default',
 'composite-child', 'style', 'events', 'extension-events', 'no-show-all', 'xalign', 'yalign', 'xpad',
 'ypad', 'label', 'attributes', 'use-markup', 'use-underline', 'justify', 'pattern', 'wrap', 'selectable',
 'mnemonic-keyval', 'mnemonic-widget', 'cursor-position', 'selection-bound', 'ellipsize', 'width-chars',
 'single-line-mode', 'angle', 'max-width-chars']

gtk.Container Iteration

Now gtk.Container and subclasses support the iterator protocol, yielding the container's children:

>>> box = gtk.VBox()
>>> box.add(gtk.Label())
>>> box.add(gtk.Entry())
>>> [child for child in box]
[<gtk.Label object (GtkLabel) at 0x80e9414>, <gtk.Entry object (GtkEntry) at 0x8297f74>]

New GLib wrappers

A couple of new GLib wrappers, GIOChannel and GSource were added to the gobject module to facilitate development of robust cross-platform applications.

IO Channels

IO Channels provide portable support for using files, pipes and sockets. Some of the features it provides include:

  • Portable read/write APIs common to files, pipes, and sockets;
  • Automatic encoding translation support;
  • Automatic line termination detection;
  • Finally, and most importantly, close integration with the glib main loop, including support for adding IO callbacks, which should allow portable GUI programs using network features without blocking and without using threads.

GSource (event sources)

Also new is the gobject.Source type. You can subclass it to provide your own custom event sources and attach them to the main loop.

Cairo

There is a new module, pangocairo, which wraps the pango-cairo library. Pango-cairo lets use pango to render text into Cairo surfaces. It defines a new pangocairo.CairoContext type, which inherits from the base cairo.Context object (thanks to the wonderful pycairo) and adds a bunch of pango rendering methods. This module is useful for anyone who wants to do any serious text rendering, since the Cairo only provides a "toy" text API, which cannot handle advanced scripts and unicode like pango does.

Also, there is a new gtk.gdk.CairoContext type, inheritting from pangocairo.CairoContext and adding a few more gdk-related utility functions, like setting a gtk.gdk.Pixbuf as source. Most importantly, the new method gtk.Drawable.cairo_create provides the necessary glue between gtk and cairo worlds, so you can draw your widgets using Cairo instead of GDK.

There's a small example in PyGTK.

Pango

The pango module is a bit more complete now. All attributes of pango.LayoutLine are now accessibile in Python, including pango.LayoutLine.runs, allowing you to get to the individual fonts used in any particular pango layout, for example.

For 3rd Party Extensions Developers

Some behind-the-scenes changes only affect developers using pygtk/pygobject for 3rd party extensions.

Hand-written constructors need to be updated

First a bit of past history, since I don't think this bit ever got documented anywhere:P

Before PyGTK 2.6, this code wouldn't work:

class MyLabel(gtk.Label):
    __gsignals__ = { 'my-signal': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
                                   (gobject.TYPE_INT,)) }
    def __init__(self):
        gtk.Label.__init__(self)
        self.foobar = 123

    def do_my_signal(self, arg):
        self.arg = arg

gobject.type_register(MyLabel)

Why didn't it work? Well, the new type MyLabel got registered correctly, no problem there. The problem was that the wrapper for gtk.Label.__init__ contained something like this:

static int
_warp_gtk_label_new(PyGObject *self, PyObject *args, PyObject *kwargs)
{
   char *text;
   /* ... parse the function arguments ... */
   self->obj = gtk_label_new(text);
   return 0;
}

As you can see, when the subclass constructor chains to the parent constructor, a GtkLabel C GObject is created instead of a MyLabel one. Even though you have a MyLabel python wrapper, you still have a GtkLabel underneath, which means the signal that we declared is not valid for this instance. To workaround the problem, prior to PyGTK 2.6 one had to call self.__gobject_init__() instead of calling the parent constructor, which is kind of ugly and error prone.

In PyGTK, almost all constructors were fixed to solve this problem and allow subclasses to naturally chain to the parent constructor. The constructors were updated to use g_object_new and pyg_type_from_object, like this:

static int
_warp_gtk_label_new(PyGObject *self, PyObject *args, PyObject *kwargs)
{
   char *text;
   /* ... parse the function arguments ... */
   self->obj = g_object_new(pyg_type_from_object(self), "label", text, NULL);
   return 0;
}

In order for the code generator to be able to produce this new kind of constructor, the .defs have to slightly updated to define the parameters in terms of GObject properties. For example, while before the gtk.Label constructor was defined like this:

(define-function gtk_label_new
  (c-name "gtk_label_new")
  (is-constructor-of "GtkLabel")
  (return-type "GtkWidget*")
  (parameters
    '("const-char*" "str" (null-ok) (default "NULL"))))

It had to be changed to this:

(define-function gtk_label_new
  (c-name "gtk_label_new")
  (is-constructor-of "GtkLabel")
  (return-type "GtkWidget*")
  (properties
    '("label" (argname "str") (optional))))

When solving gnomebug:161177, the constructors were updated once again. Fortunately nothing so radical this time. If the constructor is already using (properties ...) instead of (parameters ...) in the constructor, then nothing needs to be updated. However, if a constructor is manually coded in a .override file, you need to update it like this:

  static int
  _warp_gtk_label_new(PyGObject *self, PyObject *args, PyObject *kwargs)
  {
     char *text;
     /* ... parse the function arguments ... */
-    self->obj = g_object_new(pyg_type_from_object(self), "label", text, NULL);
+    if (pygobject_construct(self, "label", text, NULL))
+        return -1;
     return 0;
  }

Moreover, you need to tell PyGTK that the new constructor obeys the new "constructor protocol", by adding something like this to the .override file:

%%
new-constructor GTK_TYPE_LABEL

New code generator features

body sections

Now the code generator understands body sections, for those utility functions you want to code that don't actually belong to any particular wrapper. Typically developers have been using the headers section for this. The problem is when you want to write code that depends on the types defined in that same module, then the compiler sees your code using a type/variable that wasn't defined yet. The body section always puts the code after all python types are declared, so you never have this problem.

Class and static methods, one-argument functions

There are a new options that you can give to override and define sections in .override files:

onearg

may appear after the function/method name, and is mutually exclusive with noargs and kwargs; it means that the function receives a single argument as the object in second argument of the wrapper(PyObject *args becomes a direct python argument instead of a tuple). This maps to the METH_O python method flag;

classmethod

This maps to the METH_CLASS python method flag, and is used to indicate the method receives the class instead of the instance as first argument; Example:

%%
define GtkWidget.set_activate_signal kwargs classmethod
static PyObject *
_wrap_gtk_widget_set_activate_signal(PyObject *cls,
                                     PyObject *args,
                                     PyObject *kwargs)
{
   /* do something with the class 'cls' */
}
staticmethod

This maps to the METH_STATIC python method flag, and is used to indicate the method receives NULL instead of the instance as first argument, and can be invoked from the class, without an instance.

Projects/PyGTK/WhatsNew28 (last edited 2013-11-22 23:49:32 by WilliamJonMcCann)