5.2 Using the Interface


5.2 Using the Interface

When you're happy with an interface (or are paranoid about losing data), save it with Project > Save . The first time you save a project, the Project Options dialog appears; you must supply a new directory and name here. Make sure that you deselect all of the check buttons under File Output Options on the C Options tab except Set Widget Names .

After the save, your project directory contains a .glade file with widget definitions and a .gladep file with internal information. Do not remove the .gladep file; it contains the Glade settings for that project. Your program does not need a .gladep file to run, though, and you should not include it when you distribute your program.

As you can see from the extensive listing that follows , a .glade file is an XML document. To use this file in your program, you can use the small, frequently overlooked library called libglade , which you will see in the next section.

 <?xml version="1.0" standalone="no"?> <!--*- mode: xml -*--> <!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd"> <glade-interface> <requires lib="gnome"/> <widget class="GtkDialog" id="window">   <property name="border_width">3</property>   <property name="visible">True</property>   <property name="title" translatable="yes">Temperature Converter</property>   <property name="type">GTK_WINDOW_TOPLEVEL</property>   <property name="window_position">GTK_WIN_POS_NONE</property>   <property name="modal">False</property>   <property name="resizable">True</property>   <property name="destroy_with_parent">False</property>   <property name="has_separator">True</property>   <child internal-child="vbox">     <widget class="GtkVBox" id="dialog-vbox1">       <property name="visible">True</property>       <property name="homogeneous">False</property>       <property name="spacing">0</property>       <child internal-child="action_area">         <widget class="GtkHButtonBox" id="dialog-action_area1">           <property name="visible">True</property>           <property name="layout_style">GTK_BUTTONBOX_END</property>           <child>             <widget class="GtkButton" id="close-button">               <property name="visible">True</property>               <property name="can_default">True</property>               <property name="can_focus">True</property>               <property name="label">gtk-close</property>               <property name="use_stock">True</property>               <property name="relief">GTK_RELIEF_NORMAL</property>               <property name="response_id">-7</property>             </widget>           </child>         </widget>         <packing>           <property name="padding">0</property>           <property name="expand">False</property>           <property name="fill">True</property>           <property name="pack_type">GTK_PACK_END</property>         </packing>       </child>       <child>         <widget class="GtkLabel" id="label6">           <property name="visible">True</property>           <property name="label" translatable="yes">&lt;b&gt;When you change a \      temperature box below, every other box automatically converts the \      temperature to its scale.&lt;/b&gt;</property>           <property name="use_underline">False</property>           <property name="use_markup">True</property>           <property name="justify">GTK_JUSTIFY_LEFT</property>           <property name="wrap">True</property>           <property name="selectable">False</property>           <property name="xalign">0</property>           <property name="yalign">0.5</property>           <property name="xpad">0</property>           <property name="ypad">0</property>         </widget>         <packing>           <property name="padding">0</property>           <property name="expand">False</property>           <property name="fill">False</property>         </packing>       </child>       <child>         <widget class="GtkTable" id="table1">           <property name="visible">True</property>           <property name="n_rows">5</property>           <property name="n_columns">2</property>           <property name="homogeneous">False</property>           <property name="row_spacing">6</property>           <property name="column_spacing">6</property>           <child>             <widget class="GtkLabel" id="label1">           <property name="visible">True</property>           <property name="label" translatable="yes">_Kelvin (K):</property>           <property name="use_underline">True</property>           <property name="use_markup">False</property>           <property name="justify">GTK_JUSTIFY_RIGHT</property>           <property name="wrap">False</property>           <property name="selectable">False</property>           <property name="xalign">1</property>           <property name="yalign">0.5</property>           <property name="xpad">0</property>           <property name="ypad">0</property>           <property name="mnemonic_widget">kelvin-spin</property>         </widget>         <packing>           <property name="left_attach">0</property>           <property name="right_attach">1</property>           <property name="top_attach">0</property>           <property name="bottom_attach">1</property>           <property name="x_options">fill</property>           <property name="y_options"></property>         </packing>       </child>       << more GtkLabel widget definitions >>       <child>         <widget class="GtkSpinButton" id="kelvin-spin">           <property name="visible">True</property>           <property name="can_focus">True</property>           <property name="climb_rate">1</property>           <property name="digits">2</property>           <property name="numeric">True</property>           <property name="update_policy">GTK_UPDATE_ALWAYS</property>           <property name="snap_to_ticks">False</property>           <property name="wrap">False</property>           <property name="adjustment">1 0 10000 1 10 10</property>         </widget>         <packing>           <property name="left_attach">1</property>           <property name="right_attach">2</property>           <property name="top_attach">0</property>           <property name="bottom_attach">1</property>           <property name="y_options"></property>         </packing>       </child>       << more GtkSpinButton widget definitions >>     </widget>         <packing>           <property name="padding">0</property>           <property name="expand">True</property>           <property name="fill">True</property>         </packing>       </child>     </widget>   </child> </widget> </glade-interface> 

5.2.1 Reading Glade Files

To read the file.glade file into your program, include the Glade header files and call glade_xml_new() .

 #include <glade/glade.h> GladeXML *  ui_defs  ;  ui_defs  = glade_xml_new("  file  .glade",  root  ,  domain  ); 

Here, root is a widget root (use NULL if the file contains only one top-level widget), and domain is a translation environment (it can be NULL ). After the function call, the ui_defs GladeXML object contains the interface definitions. The GladeXML object is not a widget ” it's just an internal representation.

You should copy your application's .glade files along with any other program data to $(PREFIX)/share/app_id ( app_id is your program's application identifier). Use a glade subdirectory if you feel that this is necessary. Use gnome_program_locate_file() with the GNOME_FILE_DOMAIN_APP_DATADIR category to return a path for the .glade file.

Here is how you might include a .glade file in a real program:

 GnomeProgram *program; gchar *filename; GladeXML *all_defs, *window1_defs;   << ... >> filename = gnome_program_locate_file(program, GNOME_FILE_DOMAIN_APP_DATADIR,                                      "interface.glade", TRUE, NULL); /* read all definitions from the file */ all_defs = glade_xml_new(filename, NULL, NULL); /* read only the widget tree starting at window1 */ window1_defs = glade_xml_new(filename, "window1", NULL); g_free(filename); 

5.2.2 Accessing Widgets

After reading one or more windows into a GladeXML object, you can get a GtkWidget * pointer to access the widgets with these functions:

  • GtkWidget *glade_xml_get_widget(GladeXml * ui_defs , const char * name )

    Returns a widget matching name in ui_defs , or NULL if there is no such widget. In many cases, you probably want to cast the return value to a more convenient form.

  • GList *glade_xml_get_widget_prefix(GladeXml * ui_defs , const char * prefix )

    Returns a list of all widgets in ui_defs that begin with prefix . This function can be useful for iterating through several widgets with g_list_foreach() .

5.2.3 Automatically Attaching Signals

You could call glade_xml_get_widget() to get a widget pointer and bind the widget's signals by hand, but there's an even easier way: Add a handler identifier with Glade.

In the Glade property editor for a widget, click the Signals tab. To add a handler identifier, follow these steps:

  1. Pick a signal from the Signal list.

  2. Enter a handler identifier in the Handler box.

  3. Click Add .

Figure 5.13 shows the signal display for the temperature converter's Close button; the clicked signal carries the close-button-clicked identifier.


Figure 5.13: Glade signal editor display.

To connect the signal handler function in your program, use

 glade_xml_signal_connect(  ui_defs  ,  handler_id  ,  func  ) 

Here, ui_defs is a GladeXML object, handler_id is the new handler identifier, and func is the handler callback function. Widgets may share handler identifiers; glade_xml_signal_connect() binds the signal to all applicable widgets. If you want to include a data pointer with the signal, use

 glade_xml_signal_connect_data(  ui_defs  ,  handler_id  ,  func  ,  data  ) 

You can make libglade attempt to automatically connect signal handlers with

 glade_xml_signal_autoconnect(  ui_defs  ) 

Here, libglade attaches a handler to the function's widget if the handler identifier matches a function name. However, a couple of things can go wrong:

  • You need to compile with a special option like -export-dynamic .

  • GModule must work properly; that is, it must understand how to grovel around in a binary's symbol table.

  • You can't pass much for a data pointer.

  • You can't use g_signal_connect_swapped() .

  • You can't use dashes and other restricted characters in your handler identifiers.

The temperature conversion example uses glade_xml_signal_connect_data() ; see Section 5.2.5.

5.2.4 Associative Functions

There are two functions that can help you go back and forth between Glade identifiers and widgets:

  • const char *glade_get_widget_name(GtkWidget * widget )

    Returns the Glade widget name corresponding to widget . Do not change or deallocate this string.

  • GladeXML *glade_get_widget_tree(GtkWidget * widget )

    Returns the GladeXML object that defines widget .

5.2.5 The Complete Temperature Converter

This section contains the entire temperature conversion program that goes with temperature.glade . Notice that there are no big pieces dealing with widget creation and packing ” one glade_xml_new() call does the work. Not only do a dozen lines or so replace what would otherwise be a huge portion of a conventional program, but you should also remember that you can tweak small parts of the interface at any time with Glade, eliminating the need to recompile the code.

Notice that the global definitions reflect only some of the widgets:

 /* -*-coding: utf-8;-*- */ /* temperature.c -- temperature converter using libglade */ #include <gnome.h> #include <glade/glade.h> typedef enum {   KELVIN,   CELSIUS,   FAHRENHEIT,   REAUMUR,   RANKINE,   NUM_TEMPERATURE_SCALES } TemperatureScale; const gchar *widget_name[] = {   "kelvin-spin",   "celsius-spin",   "fahrenheit-spin",   "reaumur-spin",   "rankine-spin" }; GtkSpinButton *temp_spin[NUM_TEMPERATURE_SCALES]; gulong handler[NUM_TEMPERATURE_SCALES]; gdouble temp_value[NUM_TEMPERATURE_SCALES]; 

This handler function is the meat of the program. Notice how this callback blocks signals at the very end, where the values go back to the spin buttons.

 void changed(GtkSpinButton *spin_button, gpointer id_ptr) {   TemperatureScale scale_id = (TemperatureScale)id_ptr;   TemperatureScale i;   g_assert(scale_id >= KELVIN && scale_id < NUM_TEMPERATURE_SCALES);   /* read the temperature from the spin button */   g_object_get(temp_spin[scale_id], "value", &temp_value[scale_id], NULL);   /* determine the Kelvin equivalent of that temperature */   switch (scale_id)   {      case CELSIUS:         temp_value[KELVIN] = temp_value[CELSIUS] + 273.15;         break;      case FAHRENHEIT:         temp_value[KELVIN] = (5.0/9.0)*(temp_value[FAHRENHEIT]-32.0) + 273.15;         break;      case REAUMUR:         temp_value[KELVIN] = (5.0/4.0)*temp_value[REAUMUR] + 273.15;         break;      case RANKINE:         temp_value[KELVIN] = (5.0/9.0)*temp_value[RANKINE];         break;      default:         break;   }   /* determine the rest of the values based on the Kelvin temperature */   if (scale_id != CELSIUS)      temp_value[CELSIUS] = temp_value[KELVIN] - 273.15;   if (scale_id != FAHRENHEIT)      temp_value[FAHRENHEIT] = (9.0/5.0)*(temp_value[KELVIN]-273.15) + 32.0;   if (scale_id != REAUMUR)      temp_value[REAUMUR] = (4.0/5.0)*(temp_value[KELVIN]-273.15);   if (scale_id != RANKINE)      temp_value[RANKINE] = (9.0/5.0)*temp_value[KELVIN];   /* write the new values back into the spin buttons;      disable signal handlers when doing this so that this      handler function doesn't get called again and again and.. */   for (i=KELVIN; i < NUM_TEMPERATURE_SCALES; i++)   {      if (scale_id != i)      {        g_signal_handler_block(temp_spin[i], handler[i]);        g_object_set(temp_spin[i], "value", temp_value[i], NULL);        g_signal_handler_unblock(temp_spin[i], handler[i]);      }   } } /* standard event handlers */ gint delete_event(GtkWidget *widget, GdkEvent event, gpointer data) {   return FALSE; } void end_program(GtkWidget *widget, gpointer data) {   gtk_main_quit(); } 

This event handler is for the Close button on the main dialog window.

 void close_clicked(GtkButton *button, gpointer window_ptr) {   GtkDialog *window = GTK_DIALOG(window_ptr);   gtk_widget_destroy(GTK_WIDGET(window)); } 

Here is the main program ” just about 50 lines of code. The libglade library assists in attaching the handler near the end of the program.

 int main(int argc, char **argv) {   GladeXML *ui;   GnomeProgram *program;   GtkDialog *window;   TemperatureScale i;   /* initialize GNOME */   program = gnome_program_init("temperature", "0.1",                                LIBGNOMEUI_MODULE,                                argc, argv,                                GNOME_PROGRAM_STANDARD_PROPERTIES,                                GNOME_PARAM_HUMAN_READABLE_NAME, "Temperature",                                NULL);   /* read the definitions with libglade */   ui = glade_xml_new("temperature.glade", NULL, NULL);   /* get a handle on each of the spin button widgets */   for (i=KELVIN; i < NUM_TEMPERATURE_SCALES; i++)   {      temp_spin[i] = GTK_SPIN_BUTTON(glade_xml_get_widget(ui, widget_name[i]));   }   window = GTK_DIALOG(glade_xml_get_widget(ui, "window"));   /* bind "value-changed" signal handler for each spin button,      use an identifier as the the user data pointer */   for (i = KELVIN; i < NUM_TEMPERATURE_SCALES; i++)   {      handler[i] = g_signal_connect(temp_spin[i],                                    "value-changed", G_CALLBACK(changed),                                    GUINT_TO_POINTER(i));   }   /* set the starting value to the boiling point of water */   g_object_set(temp_spin[CELSIUS], "value", 100.0, NULL);   /* attach standard handlers */   g_signal_connect(window, "delete_event", G_CALLBACK(delete_event), NULL);   g_signal_connect(window, "destroy", G_CALLBACK(end_program), NULL);   /* attach the close button handler with the help of libglade */   glade_xml_signal_connect_data(ui,                                 "close-button-clicked",                                 G_CALLBACK(close_clicked),                                 GUINT_TO_POINTER(window));   gtk_main();        /* start GTK+ main event loop */   return 0; } 



The Official GNOME 2 Developers Guide
The Official GNOME 2 Developers Guide
ISBN: 1593270305
EAN: 2147483647
Year: 2004
Pages: 108

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net