How to encapsulate the GObject module in Python for graphical programming

Source: Internet
Author: User
Tags glob
This article describes how to encapsulate the GObject module in Python for graphical programming. This article is from the IBM official website technical documentation, for more information, see Python, which is an excellent language used to encode the graphic interface. Since you can quickly write work code without a time-consuming compilation cycle, you can immediately start and run the interface and use it soon. By combining this with Python's ability to easily link the local library, you can build an outstanding environment.

Gnome-python is a software package that encapsulates GNOME and its related libraries for Python. This allows you to write an application that looks exactly the same as the core GNOME Application in Python, and the time spent is only part of the time spent writing the application in C.

However, programming without C has a disadvantage. Most GNOME is written in C, and the widgets to be used in Python must be encapsulated. This is a fast task for people who know how the encapsulation process works, but it is not automatic unless the widget is a core GNOME Library or at least useful, otherwise, they will not be encapsulated. C programmers may have to write more complex code, but they did this step first!

But not necessarily! Although the traditional technology of encapsulating the process of window widgets is only known to a very small number of people, it is not really so difficult. If you can encapsulate new widgets when they are found, you can immediately use them in Python programs.

This article describes how to encapsulate a C-encoded GObject (all GTK + widgets and the final base classes of many related objects) so that it can be used from Python code. Assume that gnome-python V1.99.x is installed on your machine (if not, see references for links ). If you are using a software package, make sure that the software package is installed. In addition, Python 2.2 and its header files must be installed. Assume that you have knowledge of Make, Python, GTK + 2, and C.

To demonstrate this process, I will encapsulate the EggTrayIcon, which is a GTK + widget used to abstract the icon in the notification area. This library is located in the libegg module in gnome cvs. At the end of this article, we will have a local Python module named trayicon, which contains a TrayIcon object.

At the beginning, get eggtrayicon. c and eggtrayicon. h (its link is in the reference section at the end of this article), and then put them in a new directory. This source file should be built in the automake environment (but we will not be in this environment), so or remove the # include Or create an empty file named config. h, and then create an empty makefile. Next, we will fill it in.
Create Interface Definition

The first step in the object encapsulation process is to create trayicon. defs, which specifies the API for the object. Definition files are written in a Scheme language. although they are easy to generate for small interfaces, it is difficult for large interfaces or beginners to compile them.

Gnome-python is provided together with the tool named h2def. This tool parses the header file and generates a rough definition file. Note: Because it does not parse the C code, but only uses regular expressions, it does require the traditional format of GObject, and cannot correctly parse the C code that is specially formatted.

To generate an initial definition file, call h2def: python/usr/share/pygtk/2.0/codegen/h2def. py eggtrayicon. h> trayicon. defs

Note: If h2def. py is not installed in/usr, you must change the path to the location where it is located.

If we view the generated definition file now, it should have some meaning. This file contains the definition, constructor, and methods send_message and cancel_message of the EggTrayIcon class. This file has no obvious errors. we don't want to remove any methods or fields, so we don't need to edit it. Note: This file is not specific to Python and can be used for binding in other languages.

Generate wrapper

Now that we have the interface definition, we can generate the Python package code block. This includes generating an overwrite File. The overwrite File tells the code generator which header files should be included and what module names will be.

Use % to divide the overwrite File into multiple sections (in the lex/yacc style ). These sections define the header files, module names, Python modules to be included, functions to be ignored, and finally all manually encapsulated functions. The following is the initial overwrite File of the trayicon module.
Listing 1. trayicon. override

%%headers#include 
 
          #include "pygobject.h"#include "eggtrayicon.h"%%modulename trayicon           %%import gtk.Plug as PyGtkPlug_Type    %%ignore-glob *_get_type              %%
 

Let's check the code in more detail again:

  headers  #include 
 
    #include "pygobject.h"  #include "eggtrayicon.h"
 

These are the header files to be included when building the wrapper. Python. h and pygobject. h must always be included. when we encapsulate eggtrayicon. h, we must also include them.

modulename trayicon

The modulename specification declares the module in which the package will be located.

import gtk.Plug as PyGtkPlug_Type

These are Python imports for the wrapper. Pay attention to the naming conventions. for the module to be compiled, it must be followed. Generally, the superclass of the imported object is enough. For example, if the object is inherited directly from GObject, use:

  import gobject.GObject as PyGObject_Type  ignore-glob  *_get_type

This is the glob mode of the function name to be ignored (shell-style regular expression ). Python processes the type code for us, so we ignore the * _ get_type function; otherwise, they will be encapsulated.

Since we have constructed an overwrite File, we can use it to generate the package. Gnome-python binding provides a magic tool for generating the package, which we can use at will. Add the following content to makefile:
Listing 2. initial makefile

Further details:

  DEFS='pkg-config --variable=defsdir pygtk-2.0'

DEFS is the path containing the Python GTK + bound definition file.

  trayicon.c: trayicon.defs trayicon.override

The generated C code depends on the definition file and overwrite File.

 pygtk-codegen-2.0 --prefix trayicon \

Call the gnome-python code generator. The prefix parameter is used as the prefix of variable names in the generated code. You can name this parameter at will, but use the module name to make the symbol names consistent.

  --register $(DEFS)/gdk-types.defs \  --register $(DEFS)/gtk-types.defs \

The module uses the types in GLib and GTK +, so we must also tell the code generator to load these types.

  --override trayicon.override \

This parameter passes the overwrite File we created to the code generator.

  trayicon.defs > $@

Here, the final option of the code generator is to define the file itself. The code generator outputs standard output, so we redirect it to the target trayicon. c.

If we run make trayicon. c and view the generated file, we will see that C code wraps every function in EggTrayIcon. Don't worry about warning No ArgType for GdkScreen *-this is normal.

As you can see, the encapsulated code looks complicated, so we thank the code generator for writing every line for us. Later, we will learn how to manually encapsulate each method when we want to optimize the encapsulation, instead of writing all the wrappers ourselves.

Create a module

Since the code block of the package has been created, you need a method to start it. This involves creating trayiconmodule. c, which can be considered as the main () function of the Python module. This file is the sample file code (similar to the overwrite File) and we will slightly modify it. Below is the trayiconmodule. c we will use:
Listing 3. TrayIcon module code

#include 
 
   void trayicon_register_classes (PyObject *d); extern PyMethodDef trayicon_functions[]; DL_EXPORT(void)inittrayicon(void){  PyObject *m, *d;   init_pygobject ();   m = Py_InitModule ("trayicon", trayicon_functions);  d = PyModule_GetDict (m);   trayicon_register_classes (d);   if (PyErr_Occurred ()) {    Py_FatalError ("can't initialise module trayicon");  }}
 

Here we need to explain some minor differences, because there are multiple source codes using the word trayicon. The name of the inittrayicon function and the name of the initialization module are the real names of the Python module, so they are the names of the final shared objects. The arrays trayicon_functions and the trayicon_register_classes are named based on the -- prefix parameter of the code generator. As mentioned above, it is best to keep these names consistent so that encoding the file will not become messy.

Although the name source may be confused, the C code is very simple. It initializes the GObject and trayicon modules and registers these classes with Python.

Now we have all the code blocks to generate shared objects. Add the following content to makefile:
Listing 4. Additional code for makefile

CFLAGS = 'pkg-config --cflags gtk+-2.0 pygtk-2.0' -I/usr/include/python2.2/ -I.  LDFLAGS = 'pkg-config --libs gtk+-2.0 pygtk-2.0'                   trayicon.so: trayicon.o eggtrayicon.o trayiconmodule.o                 $(CC) $(LDFLAGS) -shared $^ -o $@

Let's perform a row-by-row check again:

  CFLAGS = 'pkg-config --cflags gtk+-2.0 pygtk-2.0' -I/usr/include/python2.2/ -I.

This row defines the C compilation flag. We use pkg-config to obtain the include path of GTK + and PyGTK.

 LDFLAGS = 'pkg-config --libs gtk+-2.0 pygtk-2.0'

This row defines the link program flag. Use pkg-config again to obtain the correct library path.

trayicon.so: trayicon.o eggtrayicon.o trayiconmodule.o

Shared objects are constructed based on the generated code, the module code we just compiled, and the implementation of EggTrayIcon. Implicit rules are used to construct the. o file based on the. c file we created.

  $(CC) $(LDFLAGS) -shared $^ -o $@

Here we build the final shared library.

Now running make trayicon. so should generate C code according to the definition, compile three C files, and finally link them all together. Well done-we have built the first native Python module. If there is no compilation or link, check these phases carefully and ensure that there is no warning that a later error may occur.

Now that we have trayicon. so, we can try and use it in the Python program. It is best to load it at the beginning and then list its members. Run python in shell to open the interactive interpreter, and then enter the following command.
Listing 5. TrayIcon interactive test

$ pythonPython 2.2.2 (#1, Jan 18 2003, 10:18:59)[GCC 3.2.2 20030109 (Debian prerelease)] on linux2Type "help", "copyright", "credits" or "license" for more information.>>> import pygtk>>> pygtk.require("2.0")>>> import trayicon>>> dir (trayicon)['TrayIcon', '__doc__', '__file__', '__name__']

You want to generate the same result from dir. Now we are going to start a bigger example!
Listing 6. Hello example

#! /usr/bin/pythonimport pygtkpygtk.require("2.0")import gtkimport trayicon                t = trayicon.TrayIcon("MyFirstTrayIcon")   t.add(gtk.Label("Hello"))           t.show_all()gtk.main()

Refine It row by row:

  #! /usr/bin/python  import pygtk  pygtk.require("2.0")  import gtk  import trayicon

Here, we first request to bind with the imported GTK + and then import the new module.

  t = trayicon.TrayIcon("MyFirstTrayIcon")

Create a trayicon. TrayIcon instance. Note: The constructor carries the string parameter-icon name.

  t.add(gtk.Label("Hello"))

The TrayIcon element is a GTK + container, so you can add anything to it. Here, I add a tag widget.

  t.show_all()  gtk.main()

Here, I set the widget to visible and then start the GTK + Main Event loop.

Now, if you have not done so, add the Notification Area applet to the GNOME Panel (right-click the panel, select "Add to Panel"-> Utility-> Notification Area ). Running the test program should display "Hello" in the bar ". It's cool, isn't it?

What else can we do in the notification area? Okay. The program can tell the notification area to display messages. The actual display method of the message is specific to the implementation. Currently, the GNOME notification area displays tool prompts. You can call the send_message () function to send messages. You can quickly view the API to learn that it wants to have a timeout and a message, so it should work as follows:

...t = trayicon.TrayIcon("test")...t.send_message(1000, "My First Message")

But not that. The C prototype is send_message (int timeout, char * message, int length). Therefore, Python APIs also require character pointers and lengths. This does work:

...t = trayicon.TrayIcon("test")...message = "My First Message"t.send_message(1000, message, len(message))

However, this is a bit ugly. This is Python; programming should be simple. If we stick to this path, it will end with C, but there is no semicolon. Fortunately, you can manually encapsulate each method when using the gnome-python code generator.

Optimization interface

So far, we have the send_message (int timeout, char * message, int length) function. It would be better if the Python API of EggTrayIcon allows us to call send_message (timeout, message. Fortunately, this is not too difficult.

This step involves editing trayicon. override again. This is exactly what the file name means: This file mainly contains manually overwritten wrapper functions. The working principle of these functions is much harder to explain than to demonstrate an example and gradually describe the content. Therefore, the send_message code is manually encapsulated below.
Listing 7. manual overwriting

override egg_tray_icon_send_message kwargs static PyObject*_wrap_egg_tray_icon_send_message(PyGObject *self,                 PyObject *args, PyObject *kwargs) {  static char *kwlist[] = {"timeout", "message", NULL};   int timeout, len, ret;  char *message;  if (!PyArg_ParseTupleAndKeywords(args, kwargs,                     "is#:TrayIcon.send_message", kwlist,                   &timeout, &message, &len))    return NULL;  ret = egg_tray_icon_send_message(EGG_TRAY_ICON(self->obj),                   timeout, message, len);  return PyInt_FromLong(ret); }

For clarity, we will refine the list line by line again:

  override egg_tray_icon_send_message kwargs

This row tells the code generator that we will provide the manual definition of egg_tray_icon_send_message, which should not generate a definition.

  static PyObject*  _wrap_egg_tray_icon_send_message(PyGObject *self,  PyObject *args, PyObject *kwargs)

This is the prototype of the Python-to-C bridge. It consists of GObject pointers, parameter arrays, and keyword parameter arrays that are calling methods. The returned value is always PyObject *, because all values in Python are objects (or even integers ).

  {  static char *kwlist[] = {"timeout", "message", NULL};  int timeout, len, ret;  char *message;

This array defines the name of the keyword parameter accepted by the function. Providing the ability to use keyword parameters is not necessary, but it can make code with many parameters much clearer and does not require a lot of extra work.

  if (!PyArg_ParseTupleAndKeywords(args, kwargs,  "is#:TrayIcon.send_message", kwlist,  &timeout, &message, &len))  return NULL;

This complex function calls the execution parameter parsing. We provide it with a list of known keyword parameters and all given parameters. it sets the final parameter to the value. The obscure string declares the required variable type, which will be described later.

  ret = egg_tray_icon_send_message(EGG_TRAY_ICON(self->obj),  timeout, message, len);  return PyInt_FromLong(ret);  }

Here, we actually call egg_tray_icon_send_message and convert the returned int to PyObject.

It seems a little scary at first, but it was originally copied from the generated code in trayicon. c. In most cases, if you only want to optimize the required parameters, this is completely possible. Just copy and paste the relevant function from the generated C, add a magic overwrite line and edit the code until it is as expected.

The most important change is to modify the required parameters. The obscure string in the PyArg_ParseTupleAndKeywords function defines the required parameters. Initially, it was the isi: TrayIcon. send_message; this means that the parameters are int, char * (s indicates the string), and int. if an exception is thrown, this function is called TrayIcon. send_message. We do not want to specify the string length in Python code, so we need to change the isi to is #. Using s # instead of s means PyArg_ParseTupleAndKeywords will automatically calculate the string length and set another variable for us-this is exactly what we want.

To use the new package, you only need to re-build the shared object and change the send_message call in the test program:

t.send_message(1000, message)

If everything is done as usual, the modified example should have the same behavior but have clearer code.

End the game

We use a small but useful C GObject to encapsulate it, so that it can be used in Python, or even customized to fit our needs. The technology here can be applied to different objects multiple times, allowing you to use any GObject found in Python.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.