Skip to content

Wrapping GObjects for pygtk

22/07/2009

Introduction

The documentation for this argument is really really old, and really^3 incomplete. So I will collect all infos I will gather in future…

This tutorial explains how to wrap a simple widget in pygtk, It’s the result of a 3-days headache on this two references:

  1. http://www.ibm.com/developerworks/linux/library/l-wrap/
  2. http://faq.pygtk.org/index.py?req=show&file=faq01.011.htp

Sorry, really sorry for the poor english.

I’ve done another tutorial where  I explain the most common problem of linking, take a look if you encounter problems:

https://pygabriel.wordpress.com/2009/07/31/wrapping-pygtk-widgets-what-you-should-know/

Why you should wrap a GObject?

There are a lot of third-party widgets for C/GTK+ or gtkmm, and you may want to integrate this widgets in your pygtk application.

Personally I found the scintilla editor not wrapped for pygtk  or the gtkmathview, that are really useful widgets.

In a situation like this you have two possibilites:

  1. Learn to wrap a gtk widget
  2. Ask to the pygtk developers to do this for you

However in the first case the best tutorial is fairly old.

In the second case  I wasn’t so lucky :).

Let’s start

The basics

To study the Iliad and the Odissey, you must know ancient Greek, to study music you must know notes, to wrap a GtkWidget you have to know the GObject system :D.

There is a really good documentation about this, you may want to deeper the argument, let’s look at the official tutorial there is also the gtk tutorial that is simpler.

When you want to use objects in C, you have to use GObject: an object consist in an header file like hello.h, and a source file hello.c.

In hello.h you need to declare the classes, and their respective methods in the GObject way.

In hello.c you have to Implement the methods.

There is a typical structure and a lot of boilerplate code to use. If you want to read the code copy this in your editor (it’s painful to read the code from the web page):

/* hello.h */

/* This assures you the header isn't included more
 than one time */
#ifndef _HELLO_WIDGET_H_
#define _HELLO_WIDGET_H_

#include <glib-object.h>

/* This macros registers the type and other stuff, really boilerplate */
#define HELLO_TYPE_WIDGET             (hello_widget_get_type ())
#define HELLO_WIDGET(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), HELLO_TYPE_WIDGET, HelloWidget))
#define HELLO_WIDGET_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), HELLO_TYPE_WIDGET, HelloWidgetClass))
#define HELLO_IS_WIDGET(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), HELLO_TYPE_WIDGET))
#define HELLO_IS_WIDGET_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), HELLO_TYPE_WIDGET))
#define HELLO_WIDGET_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), HELLO_TYPE_WIDGET, HelloWidgetClass))

/* These are the declarations of the class, and of the instance */
typedef struct _HelloWidgetClass HelloWidgetClass;
typedef struct _HelloWidget HelloWidget;

struct _HelloWidgetClass
{
	GObjectClass parent_class; /* hello widget inherit from gobject */
};

struct _HelloWidget
{
	GObject parent_instance; /* hello widget inherit from gobject */
};

GType hello_widget_get_type (void);

#endif /* _HELLO_WIDGET_H_ */

I know: this widget do nothing, really nothing.  In hello.c there are the G_DEFINE_TYPE, that really I don’t know what is, and the more interesting method definitions, the init function(constructor) and the finalize function (destructor).

I give you the code so you can try and play.

/* hello.c : in this file there is the implementation of
methods of the hello widget class*/
#include "hello.h"

G_DEFINE_TYPE (HelloWidget, hello_widget, G_TYPE_OBJECT);

static void
hello_widget_init (HelloWidget *object)
{
	/* TODO: Add initialization code here */
}

static void
hello_widget_finalize (GObject *object)
{
	/* TODO: Add deinitalization code here */

	G_OBJECT_CLASS (hello_widget_parent_class)->finalize (object);
}

static void
hello_widget_class_init (HelloWidgetClass *klass)
{
	GObjectClass* object_class = G_OBJECT_CLASS (klass);
	GObjectClass* parent_class = G_OBJECT_CLASS (klass);

	object_class->finalize = hello_widget_finalize;
}

Generating the wrapper

It’s time to write generate the wrapper (hello-widget.c): to do this you have to use the scripts from the pygtk/pygobject distribution.

To generate the wrapper you need:

  1. A definition file: hello-widget.defs
  2. An override file: hello-widget.override

There is no documentation about what is a definition file and an override file,  however let’s take them.

The definition file is generate by an utility called h2def that takes an header file (hello.h) and gives a def file:

python /usr/share/pygobject/2.0/codegen/h2def.py hello.h > hello-widget.defs

Well, now you need an override file, and you have to handwrite this:  hello-widget.override

%%
headers
#include <Python.h>
#include "pygobject.h"
#include "hello.h"
%%
modulename hello_widget
%%
import gobject.GObject as PyGObject_Type
%%
ignore-glob
 *_get_type
%%

All I know is that we have to set the modulename with the name of the module (really?). And to import the superclass. The ignore-glob simply avoid wrapping the get_type functions. The rest is self-explaining.

Ok, now we have to generate the wrapper with the codegen utility, do this command:


python /usr/share/pygobject/2.0/codegen/codegen.py \
--override hello-widget.override \
--prefix hello_widget hello-widget.defs > gen-hello-widget.c \
&& cp gen-hello-widget.c hello-widget.c \
&& rm -f gen-hello-widget.c

Simply don’t ask me why ;).

Seriously, don’t underestimate the –prefix, it has to be coherent with the module initializator.. You will see the module inizializator afterwards.

We have generated the wrapper! Now we have to make the python module and compile all the things.

Compiling all

We need:

  1. A module initializator hello-widget-module.c
  2. A Makefile to compile all the things (not fundamental,you can type commands in the shell)

The module initializator is a boilerplate code:

#include
<pygobject.h>

void hello_widget_register_classes (PyObject *d);
extern PyMethodDef hello_widget_functions[];

DL_EXPORT(void) inithello_widget(void)
{
  PyObject *m, *d;

  init_pygobject ();

  m = Py_InitModule ("hello_widget", hello_widget_functions);
  d = PyModule_GetDict (m);

  hello_widget_register_classes (d);

  if (PyErr_Occurred ()) {
    Py_FatalError ("can't initialise module hello_widget");
  }
}

This simply initializates the module, all I have done was substituting hello_widget (the –prefix) in the occurrences.

Now we are ready to write the Makefile, I will include the command to generate the wrapper so, in case of error we haven’t to redo the command.

DEFS=`pkg-config --variable=defsdir pygtk-2.0`
CFLAGS=`pkg-config --cflags gtk+-2.0 pygtk-2.0 pygobject-2.0` -I/usr/include/python2.6 -I. -fPIC
LDFLAGS=`pkg-config --libs gtk+-2.0 pygtk-2.0 pygobject-2.0`

all: hello-widget.c hello.o hello-widget.so

hello-widget.c: hello-widget.defs hello-widget.override
        python /usr/share/pygobject/2.0/codegen/codegen.py \
        --override hello-widget.override \
        --prefix hello_widget hello-widget.defs > gen-hello-widget.c \
        && cp gen-hello-widget.c hello-widget.c   \
        && rm -f gen-hello-widget.c

hello-widget.so: hello-widget.o hello.o hello-widgetmodule.o
        $(CC) $(LDFLAGS) $(CFLAGS) -shared $^ -o $@
        cp -f hello-widget.so hello_widget.so

hello.o: hello.c
        $(CC) $(CFLAGS) $(LDFLAGS) -fPIC -c hello.c -o hello.o

clean:
        rm -f hello-widget.so hello-widget.c hello.o

Please note: HTML don’t support tabs, I’ve used spaces instead. Please copy the makefile and substitute the indentation used with tabs, otherwise it wouldn’t work. The Makefile is very bad-written, it will be updated later…

Make a prayer and… make.  If all was ok, you should obtain the file hello_widget.so , open an interactive shell and:

>>> import hello_widget
>>> wid = hello_widget.HelloWidget()

Now you can play with the new toy, you can do about nothing, but with a:

>>> dir(wid)
['__class__', '__cmp__', '__copy__', '__deepcopy__', '__delattr__', '__dict__', '__doc__', '__format__', '__gdoc__', '__getattribute__', '__gobject_init__', '__grefcount__', '__gtype__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'chain', 'connect', 'connect_after', 'connect_object', 'connect_object_after', 'disconnect', 'disconnect_by_func', 'emit', 'emit_stop_by_name', 'freeze_notify', 'get_data', 'get_properties', 'get_property', 'handler_block', 'handler_block_by_func', 'handler_disconnect', 'handler_is_connected', 'handler_unblock', 'handler_unblock_by_func', 'notify', 'props', 'set_data', 'set_properties', 'set_property', 'stop_emission', 'thaw_notify', 'weak_ref']

There are all the methods inherithed by GObject, fantastic!

In the next weeks I will explore other wrapping frontiers!!!
EDIT:
New Frontiers:

I pray you to tell me if you have some problem with the tutorial: don’t be lazy!

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: