lunes, 30 de septiembre de 2013

Migrating Anjuta Projects from version 1.x to 2.x

(versión en español)

Anjuta is a tool which for years has been very useful for me. While the essential tool for design Gtk+ interfaces is Glade, Anjuta aditionally provides an easily integration between different elements necessary for development (glade, the scintilla editor, etc).

That said, we must have clear that nothing prevents us to develop the various components of our program separately: we can maintain a glade session to modify our interface windows, and a separated text editor so we can modify our code. With an additional terminal, we can run and debug partial compilations of our program. Anjuta provides all these operations in an integrated manner.

To further facilitate this integration, Anjuta 1.x provided a set of functions that facilitated some tasks (lookup_widget (), pe). In newer versions of Anjuta this support ceased to exist, because it prioritizes the utilization of libglade library. This library allows to link the program and the screen developed with glade at run time. In Anjuta 1.x, the relationship between the interface and the code is determined at compilation time, using a set of additional functions provided by Anjuta itself (defined in the file support.c).

Getting Familiar with Anjuta 2.x interface

Once you install the new version of Anjuta, some obvious differences with the previous version become evident. The most significant may be that the key combination "Alt + G" does not start Glade as before. In exchange, the way you interact with Glade is much more natural now: we just simply double click on the file that defines the interface of our program.

Another difference is that now Anjuta has several predefined profiles for build our project. Every profile is just a combination of compiler flags needed to build a project optimized for execution (-O3), optimized for debug (-g), etc.. When you build your project, choose the type of profile you want.

Opening an "old" Anjuta 2.x project

For each option available to open an old project, I personally prefer: "File -> New -> 5. Project from existing sources."

After selecting this option, look for the directory containing the "old" project:

The suggested name for the new project is the same as the selected directory. You can change it if you wish. Then we choose the type of backend we want for the project we are importing. In my case, I choosed "autotools backend".

Finally Anjuta creates the new project:

may notice that in the left region of the screen are all inherited files from the previous old project.

If we try "Shift + F7", we must choose the type of profile we are going to use in compilation:

Once choose the type of profile, anjuta tries to build the binary. It does not work initially. We are going to try to overcome this situation.

Adapting the newly imported project

Como comenté anteriormente, muchas funciones de soporte que proveía Anjuta 1.x, ya no son necesarias. Básicamente se puede prescindir de los archivos: "suport.c", "suport.h", "interface.c" e "interface.h". Se pueden eliminar de varias formas. En mi caso, el directorio importado forma parte de un directorio de trabajo de un proyecto almacenado mediante "subversion", así que utilizo la siguiente opción:
As I previously mentioned, many support functions provided by Anjuta 1.x are no longer necessary. Basically, the files "suport.c", "suport.h", "interface.c" and "interface.h" can be deleted. This can be done in several ways. My personal favorite, as the imported directory is part of a working directory of a project stored using "subversion" versioning system, I use the following:

this way, we eliminate all files indicated previously. In addition, you must remove references to these files that no longer exists, in particular we need to edit the file "src/" and eliminate any references to the deleted files. Then we must go to the project directory (using a shell console), and execute the command "./".

This last command regenerates the file "Makefile", avoiding references to any deleted files. It is likely that many development tools used by our old project had changed since the last time we modified it, besides Anjuta and Glade, but also "automake", "autotools", etc. "./" just fix up all references to the current versions of these tools existent in our system.

With a little luck, the next attempt to generate the executable will start to fail for the content of the source files, and not for the assembly of the project itself. For starters, the source programs will still refer to the header files removed ("suport.h" and "interface.h"). We can remove these lines from our code.

Of course, eliminate these lines doesn't solve all our compilation problems. There are still many references to the functions included in those deleted files, and we need to replace them. The following shows some tips about it.

Modifying the source code for a successful compilation

The first attempt to compile your code shows a screen like the following:

we see a few alarm messages that indicate that certain functions are no longer existing in our project source files, namely: "add_pixmap_directory", "lookup_widget", "create_window" etc.. We must replace them

I personally have abused enough from some functions such as "lookup_widget". Basically, this function returns a pointer to a widget using the widget's name as the function's argument, and it traverses the entire widget tree until he found it, a very inefficient mechanism. But now we will use the "libglade" library, which analyzes the content of the file ".glade" at runtime and performs the necessary links between the names of the widgets, the associated callback functions, etc.. "libglade" uses a GtkBuilder constructor as a main element, parses the file "xml" at runtime and creates all the widgets, and also sets properties and associated callback functions that are defined in our code for those widgets.

For an efficient use of this library, we must create an structure with pointers to all the widgets that need to be accessed in every callback function (other than those widgets whose action is associated with the function itself as an argument). This structure is initialized when creating the builder. By doing this, we can use it in every callback function.

The first replacement that I will do, is the reformulation of the function generically known as "create_window ()" ("create_ventana_principal ()" in my example), which isn't available anymore after removal of the file "interface.c":

The line:

ventana_principal = create_window ();


item = g_slice_new(ItemStruct);

if (create_window(item) == FALSE) {
      printf("Error building screen");

The argument of the function "create_window ()" is a pointer to the structure representing the widgets that need to be accessed in the callback functions. Maybe we don't know the content of the structure at first, but we are going to build it step by step, by subsequently attemps to succesfully recompiling the program. For now, this structure includes at least one element: the pointer to the widget "ventana_principal". A good place to define this structure is the file "callbacks.h":

typedef struct {
   GtkWidget       *ventana_principal;
} ItemStruct;

ItemStruct *item;

We can define the function "create_window()" in the file "main.c":

gboolean create_window (ItemStruct *item)
   GtkBuilder *builder;
   GError* error = NULL;

   builder = gtk_builder_new ();
   if (!gtk_builder_add_from_file (builder, UI_FILE, &error)) {
      g_warning ("Can not create builder from UI_FILE: %s", error->message);
      g_error_free (error);
      return FALSE;

   /* link the widget named "ventana_principal", to the correspondent element of the structure */
   item->ventana_principal = GTK_WIDGET (gtk_builder_get_object (builder, "ventana_principal"));

   /* link the signal associated to the widgets */
   gtk_builder_connect_signals (builder, item);

   g_object_unref (builder);

   return TRUE;

This function is also the perfect place to define any matter relating to the "look and feel" of our interface, that is not already defined by the file ".xml", or in the ".rc" file we use. UI_FILE is a macro that defines the path to the glade file.

We must delete or comment  those lines that use the "lookup_widget ()" function, that corresponds to the lines indicated by compilation attempts similar to this:

main.c:XXX: error: 'my_variable' undeclared (first use in this function)

"my_variable" is a pointer whose value was previously completed using "lookup_widget()" function. Now this variable is a part of the structure mentioned above, and must be initialized during the invocation of "create_window()". Subsequently, the former structure would be formed as follows:

typedef struct {
   GtkWidget       *ventana_principal;
   GtkWidget       *my_variable;
} ItemStruct;

ItemStruct *item;

And now, "create_window ()" must also include the definition of this pointer to the new widget:


   /* Associate every widget with the element of the structure that will represent it */
   item->ventana_principal = GTK_WIDGET (gtk_builder_get_object (builder, "ventana_principal"));

   item->my_variable = GTK_WIDGET (gtk_builder_get_object (builder, "my_variable"));


In every line the compiler shows an error message about "my_variable" unavailability, you should replace this variable by the element of the structure. Example:

gtk_widget_modify_font(GTK_WIDGET(my_variable), pfd);

should be replaced by:

gtk_widget_modify_font(GTK_WIDGET(item->my_variable), pfd);

We must gradually replace all widgets that must be accessed, and that therefore should be included in the structure, until there are no more of this error messages from the compiler. The files affected are "main.c" and "callbacks.c".

In relation to "add_pixmap_directory()", we can avoid using this function, because relationship to image files are defined in the file ".glade". Either way, if you need to refer to any particular image, we can use "glade_xml_get_widget()" function.

With a little luck, our compiler will not show any more compilation errors on missing variables or functions. We will probably see some time execution errors still or maybe the aesthetic of our program will look awful. This is because we didn't fix the file ".glade" yet.

Adapting the interface file to the new version of Glade

There are some differences between versions of glade 2 and 3, in relation to xml marks that define the different widgets. Here are some of them:

  • mark <glade-interface> becomes <interface>, and </glade-interface> becomes </interface>
  • mark <widget class=... becomes <object class=..., and </widget> becomes </object>
  • In addition, widgets like "GtkVBox" and "GtkVPane" must be modified. We must add a property: <property name="orientation">vertical</property> that way those widgets don't look in a landscaped form.
  • Widgets of type "GtkComboBox" that shows a simple list of text are different in newer versions of GTK +. There is a widget of type "GtkComboBoxText" that behaves similar to the previous one, but it is not recognized by glade 3.12.1. If we still want to use glade, we must use a list approach and integrate to the GtkComboBox. It can be done like this:

  1. From glade, you must associate the "GtkComboBox" widget to a ComboBox "model". This "model" is defined separately: it defines how many columns that the model will have, and what kind of data the model will include in each column . Each column is identified by a distinctive name. Also, some static values ​​can be preloaded in the model. For a simple text list, the number of columns has to be 1, and the type must be "gchararray". Once the model list is created, must click right mouse button on the ComboBox widget, access "Edit ..." option -> tab "hierarchy" -> "add" a new hierarchy, defining name,, type, and a related attribute (this attribute correspond to the only column defined in the model).
  • As for the source code of the program, you must do the following replacements:
          // Loop that completes ComboBox widget
          while ( ... ) {
              gtk_combo_box_append_text(GTK_COMBO_BOX(comboBox), item);

new version:

          GtkListStore *listaModelo;
          GtkTreeIter iter;

          // Loop that completes ComboBox widget
          while ( ... ) {
              gtk_list_store_append (listaModelo, &iter);
              gtk_list_store_set (listaModelo, &iter, 0, item, -1);
          // links the "list" to the ComboBox widget
          gtk_combo_box_set_model(comboBox, GTK_TREE_MODEL(listaModelo));
          // "elementoLista" is the widget created when ComboBox hierarchy was
          // defined
          gtk_cell_layout_add_attribute (comboBox, elementoLista, "text", 0);

You can still see some error messages at runtime, such as "could not find signal handler ...". This message indicates that the associated functions to a widget signal (typically defined in the file "callbacks.c") were not found. To fix this error, we must compile the project with the additional flag "-export-dynamic". This can be defined in the menu option "Build -> Configure the project ... -> Configuration Options", adding 'CFLAGS=-export-dynamic' to "./configure" options.


"Creating GtkComboBox using Glade":

"gtk_combo_box_append_text() problem":

No hay comentarios:

Publicar un comentario