Previous Next

Coding the Backend: Gtemp Redux

Compiling for the First Time

If you haven't already done so, save your application in the main glade window. Then click the build button, to build the code for the application. You can check the gtemp subdirectory to see what has been built, if you wish.

The gtemp program will start. You'll notice that both the windows we created, the main application window, and the about dialog window, open up. You can close the about window, as it's already got its signals set up with callbacks, as discussed before. However, we don't want both windows opening at once, so this is one of the first things we'll have to fix.

Close the application by clicking on the X in the upper righthand corner of the window. Notice that the command line doesn't return in the terminal window from which you started gtemp. This is because the automatic built in callbacks for the window icons(minimize, maximize and close) do not close the program completely. It would be silly if it did so, and you were using more than one window in an application. The user would close the popup and the program would terminate. We'll have to set up the callback to terminate the program when the X icon is clicked. This is the second thing we'll fix.

We need to make the program quit when the quit button is clicked or when quit is selected from the menubar under the file heading. This are the third things we'll add. We'll also make the about window appear when the about menuitem is selected from under the help menu on the menubar. This will leave us with only the 'guts' of the application to work on, the actual C to F or F to C conversion.

Making Only One Window Open when the Program Starts

Open an editor window. The tutorial writer uses Gvim, but Emacs, even gedit or a wordprocessor will do. Gvim and Emacs are nice because of all the color coding they do. Open the gtemp main.c file. It's in the /src subdirectory.

The headers in the main.c file say that glade will not overwrite the file, and it can be editted as required. Remember if you open a glade generated file and it doesn't say this, don't edit it. Move into the main() function, and find the declaration GtkWidget *gtemp_about. Remove it.

Move down the function and find the lines that read
gtemp_about = create_gtemp_about ();
gtk_widget_show (gtemp_about);
Remove them.

Glade writes the create_xxx() functions, such as create_gtemp_about(), for every window you design. These can be used in your code to create the window when it's needed. Each window needs to be showed, with gtk_widget_show(), after it's created. We've removed these lines here, but we'll soon add them to the callback for the about menuitem.

Making the X Icon in the Upper Righthand Corner of the Gtemp Main Window Work

We need to add another callback first to add this. In Glade select the Gtemp application window, bringing it up into the properties window. On the signals page, select the delete_event signal under GtkWidget Signals. Add its signal handler to the list, then save, and build the project again.

Open the callbacks.c file in your editor of choice. You'll see all the callbacks or signal handlers we added previously, with the one we just added for delete event at the bottom of the file. Go to that one.

Add
gtk_main_quit();
above
return FALSE;
in the on_gtemp_app_delete_event() function. For more information on gtk_main_quit() you can consult the gtk+ API at the gtk-General section.

Making the Quit Menuitem and Quit Button Work

In the on_quit1_activate() function add the
gtk_main_quit()
function call. That's all there is to it. Do the same for the on_quit_button_clicked() function.

Making the About Window Load When the About Menuitem is Selected

Find the function called on_about1_activate(). Here we are going to add back in the create window things we took out of the main.c file. Add the following code to the function:

GtkWidget *about

about = create_gtemp_about();
gtk_widget_show(about);

And that's it.

Now is a good time to test what you've done. Recompile the application by typing make in the /src subdirectory. If you've no typos or other errors make should return cleanly with no error messages. Run gtemp to check on what you've done.

We'll code the guts of the backend now. It's very simple, but in a complex application there are likely to be at least several auxiliary files, and most of the backend will not be put into callbacks.c, as in gtemp.

Finally Making Gtemp Convert from One Temperature Scale to Another

There should be four empty callback functions in callbacks.c now. Two of them, on_properties1_activate() and on_preferences1_activate(), we'll not use right now. This leaves on_fahr_entry_activate() and on_cel_entry_activate() to be used.

We'll write a function called compute_temp(), which will be called from both the entry_activate callbacks. We'll define two flags, C_TO_F and F_TO_C to let us know which direction the conversion is going. Compute_temp() will have the calling widget as a parameter, as well as the temperature input by the user, and the type of conversion.

At the top the file, under the other #define calls, add these lines

#define C_TO_F 0
#define F_TO_C 1

void compute_temp(GtkWidget *this_widget, float temperature, int type);

These are the declarations for the function and the flags.

At the bottom of the file, below all the callback functions, add this function:

void compute_temp(GtkWidget *this_widget, float temperature, int type)
{
   GtkWidget *other_entry = NULL;
   float result = 0.0;
   gchar *result_string = NULL;
   switch(type) {
     case C_TO_F:
       result = ((9.0 / 5.0) * temperature) + 32.0;
       other_entry = lookup_widget(this_widget, "fahr_entry");
       break;
     case F_TO_C:
       result = (5.0 / 9.0) * (temperature - 32.0);
       other_entry = lookup_widget(this_widget, "cel_entry");
       break;
   }
   result_string = g_strdup_printf("%5.2f", result);
   gtk_entry_set_text(GTK_ENTRY(other_entry), result_string);
   g_free(result_string);
}

Stepping through the function we see that it declares the proper variables and then moves into a switch statement. Here we compute the conversion depending upon where the original entry was taken. These lines: other_entry = lookup_widget(this_widget, "cel_entry"); and other_entry = lookup_widget(this_widget, "fahr_entry"); find the widget that didn't take the user input using the glade support function, lookup_widget(). Gtk_entry only takes text so we have to convert our float number to text with g_strdup_printf(). This is a utility that is like a printf() function, but makes sure the proper amount of memory is allocated, no matter how long the string is. We then set the answer text into the gtk_entry with gtk_entry_set_text() and g_free() the result_string we created with g_strdup_printf().

All of the above functions can be found in API section at gnome.org. For more on GtkEntry see this page. You can find out about the glib string utilities here.

We still need to put code into the entry_activate callbacks to actually run the compute_temp function. Find the callback called on_fahr_entry_activate() and add the following code to it:

   gchar *fahr = NULL;

   fahr = gtk_editable_get_chars(GTK_EDITABLE(entry), 0, -1);

   compute_temp(GTK_WIDGET(entry), atof(fahr), F_TO_C);

We've done no real error checking here, just changed the character string fahr into a float with atof(). We'll add the error checking in a bit. Find the on_cel_entry_activate() function and add the following to it:

   gchar *celsius = NULL;

   celsius = gtk_editable_get_chars(GTK_EDITABLE(entry), 0, -1);

   compute_temp(GTK_WIDGET(entry), atof(celsius), C_TO_F);

Once again, we'll add the error checking in a bit. Notice that in both of these code snippets we are using GtkEditable instead of GtkEntry. This is because certain aspects of GtkEntry had been deprecated (as of Gtk+ version 1.0, I note, however that they seem to have rehabilitated gtk_entry_get_text with version 2.0), most notably the gtk_entry_get_text() function. Since GtkEntry is a subclass of GtkEditable, using a GtkEditable function with it is perfectly valid. We just have to remember to use the convenience macro GTK_EDITABLE() to cast it to the new class.

At this Point, we have a Useable Application

Compile it again, and see. Type make in the /src subdirectory, fix any typos or errors you might have made, and run the application. It works pretty well, if you only enter numeric data in the entry fields. Try entering 'abc' in one of the fields. It gives a result, because the atof() function is gamely converting 'abc' to a float, but we know the result is nonsense. If the program was only intended to be used by yourself, and you knew what you had to enter to get the proper result (as most coders do) then we could stop coding right now. But let's go a tiny bit further, let's add simple error detection and notification.

Adding Error Notification

We'll write another function, called check_temperature_value() which will check to make sure the value entered is numeric, and nothing else. This function will be called from the two entry_activate functions as well, and will print an error message to the gtemp application statusbar.

At the bottom of the callbacks.c file add the following function:

gint check_temperature_value(GtkWidget *this_widget, gchar *value)
{
   GtkWidget *appbar;
   gint num = 0;
   gchar this_char = '\0';

   this_char = *value;

   if (!this_char)
   return 0;

   while (this_char != '\0') {
     if (!isdigit(this_char)) {
       /* set up the error message */
       appbar = lookup_widget(GTK_WIDGET(this_widget), "appbar1");
       gnome_appbar_push(GNOME_APPBAR(appbar), "Please enter numbers only");
       return 0;
     } else {
       num++;
       this_char = value[num];
     }
   }
   return 1;
}

We need to call this function in the entry_activate callbacks. Add these lines to on_cel_entry_activate(), before the compute_temp() function call:

   if (!check_temperature_value(GTK_WIDGET(entry), celsius))
     return;
   clear_appbar(GTK_WIDGET(entry));

Add roughly the same lines to on_fahr_entry_activate(), before the compute_temp() function call:

   if (!check_temperature_value(GTK_WIDGET(entry), fahr))
     return;
   clear_appbar(GTK_WIDGET(entry));

Finally we need the clear_appbar() function. This is just a little utility that removes the error message from the appbar if they enter a number properly. Add it to the end of the callbacks.c file:

void clear_appbar(GtkWidget *this_widget)
{
   GtkWidget *appbar;

   appbar = lookup_widget(GTK_WIDGET(this_widget), "appbar1");
   gnome_appbar_clear_stack(GNOME_APPBAR(appbar));
}


Don't forget to add function headers near the top of the file for the two functions we have added here. Compile the program, and you have a working, error checking, message sending version of Gtemp!

Previous Next