    <rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:admin="http://webns.net/mvcb/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:content="http://purl.org/rss/1.0/modules/content/">
     <channel>
        <title>ACCU  :: An Introduction to Programming with GTK+ and Glade in ISO C
and ISO C++ - Part 3</title>
        <link>https://members.accu.org/index.php/articles/705</link>
        <description>Professionalism in Programming</description>
        <dc:language>en-us</dc:language> 
        <dc:creator>Administrator</dc:creator> 
        <admin:generatorAgent rdf:resource="http://www.xaraya.org" /> 
        <admin:errorReportsTo rdf:resource="mailto:webeditor@accu.org" />
       <sy:updatePeriod>hourly</sy:updatePeriod>
       <sy:updateFrequency>1</sy:updateFrequency>
       <docs>http://backend.userland.com/rss</docs>




<div class="xar-mod-head"><span class="xar-mod-title">Programming Topics + CVu Journal Vol 16, #6 - Dec 2004</span></div>

<table border="0" cellpadding="1" cellspacing="0">
    <tbody>
    <tr>
        <td valign="top">
            Browse in :
       </td>
       <td valign="top">

                                            <a href="https://members.accu.org/index.php/articles/">All</a>

                     &gt;                         <a href="https://members.accu.org/index.php/articles/c13/">Topics</a>

                     &gt;                         <a href="https://members.accu.org/index.php/articles/c65/">Programming</a>
<br />

                                            <a href="https://members.accu.org/index.php/articles/">All</a>

                     &gt;                         <a href="https://members.accu.org/index.php/articles/c76/">Journals</a>

                     &gt;                         <a href="https://members.accu.org/index.php/articles/c77/">CVu</a>

                     &gt;                         <a href="https://members.accu.org/index.php/articles/c99/">166</a>
<br />

                                            <a href="https://members.accu.org/index.php/articles/c65-99/">Any of these categories</a>

                    -                        <a href="https://members.accu.org/index.php/articles/c65+99/">All of these categories</a>
<br />
</td>
   </tr>
   </tbody>
</table>




<div class="xar-error">
   <p>
 <strong>Note:</strong> when you create a new publication type,
the articles module will automatically use the templates
<em>user-display-[publicationtype].xt</em>
and <em>user-summary-[publicationtype].xt</em>.
If those templates do not exist when you try to preview or display a new article,
you'll get this warning :-)  Please place your own templates in themes/<em>yourtheme</em>/modules/articles . The templates will get the extension .xt there. </p>
</div>
<div class="xar-norm xar-standard-box-padding">
   <h1><strong>Title:</strong>&nbsp;An Introduction to Programming with GTK+ and Glade in ISO C
and ISO C++ - Part 3</h1>
<p><strong>Author:</strong>&nbsp;</p>
<p>
<strong>Date:</strong> 03 December 2004 13:16:08 +00:00 or Fri, 03 December 2004 13:16:08 +00:00</p>
<p><strong>Summary:</strong>&nbsp;</p>
<p><strong>Body:</strong>&nbsp;<div class="sect1" lang="en">
<div class="titlepage">
<h2><a name="d0e20" id="d0e20"></a>GTK+ and
GObject</h2>
</div>
<p>In the previous sections, the user interface was constructed
entirely by hand, or automatically using <span class=
"application">libglade</span>. The callback functions called in
response to signals were simple C functions. While this mechanism
is simple, understandable and works well, as a project gets larger
the source will become more difficult to understand and manage. A
better way of organising the source is required.</p>
<p>One very common way of reducing this complexity is <span class=
"emphasis"><em>object-orientation</em></span>. The GTK+ library is
already made up of many different objects. By using the same object
mechanism (<tt class="classname">Gobject</tt>), the ogcalc code can
be made more understandable and maintainable.</p>
<p>The ogcalc program consists of a <tt class=
"classname">GtkWindow</tt> which contains a number of other
<tt class="classname">GtkWidget</tt>s and some signal handler
functions. If our program was a class (<tt class=
"classname">Ogcalc</tt>) which derived from <tt class=
"classname">GtkWindow</tt>, the widgets the window contains would
be member variables and the signal handlers would be member
functions (methods). The user of the class wouldn't be required to
have knowledge of these details, they just create a new <tt class=
"classname">Ogcalc</tt> object and show it. By using objects one
also gains <span class="emphasis"><em>reusability</em></span>.
Previously only one instance of the object at a time was possible,
and <tt class="function">main()</tt> had explicit knowledge of the
creation and workings of the interface.</p>
<p>This example bears many similarities with the C++ Glade example
(next edition). Some of the features offered by C++ may be taken
advantage of using plain C and <tt class=
"classname">GObject</tt>.</p>
<div class="figure"><a name="d0e64" id="d0e64"></a>
<div class="mediaobject c2"><img src="/var/uploads/journals/resources/leigh-fig1.png"
align="middle" alt="C/gobject/ogcalc in action."></div>
<p class="title c3">Figure 1. <tt class=
"literal">C/gobject/ogcalc</tt> in action.</p>
</div>
<p>The listings for the code are given at the end of the article
(next two pages).</p>
<p>To build the source, do the following:</p>
<pre class="screen">
cd C/gobject
cc 'pkg-config -cflags libglade-2.0' -c ogcalc.c
cc 'pkg-config -cflags libglade-2.0' -c ogcalc-main.c
cc 'pkg-config -libs libglade-2.0' -o ogcalc ogcalc.o ogcalc-main.o
</pre></div>
<div class="sect1" lang="en">
<div class="titlepage">
<h2><a name="d0e78" id="d0e78"></a>Analysis</h2>
</div>
<p>The bulk of the code is the same as in previous sections, and so
describing what the code does will not be repeated here. The
<tt class="classname">Ogcalc</tt> class is defined in <tt class=
"filename">C/gobject/ogcalc.h</tt>. This header declares the object
and class structures and some macros common to all <tt class=
"classname">GObject</tt>-based objects and classes. The macros and
internals of <tt class="classname">GObject</tt> are out of the
scope of this document, but suffice it to say that this boilerplate
is required, and is identical for all <tt class=
"classname">GObject</tt> classes bar the class and object
names.</p>
<p>The object structure (<span class="structname">_Ogcalc</span>)
has the object it derives from as the first member. This is very
important, since it allows casting between types in the inheritance
hierarchy, since all of the object structures start at an offset of
0 from the start address of the object. The other members may be in
any order. In this case it contains the Glade XML interface object
and the widgets required to be manipulated after object and
interface construction. The class structure (<span class=
"structname">_OgcalcClass</span>) is identical to that of the
derived class (<tt class="classname">GtkWindowClass</tt>). For more
complex classes, this might contain virtual function pointers. It
has many similarities to a C++ vtable. Finally, the header defines
the public member functions of the class.</p>
<p>The implementation of this class is found in <tt class=
"filename">C/gobject/ogcalc.c</tt>. The major difference to
previous examples is the class registration and the extra functions
for object construction, initialisation and notification of
destruction. The body of the methods to reset and calculate are
identical to previous examples.</p>
<p><tt class="methodname">ogcalc_get_type()</tt> is used to get the
the typeid (<tt class="type">GType</tt>) of the class. As a side
effect, it also triggers registration of the class with the
<tt class="type">GType</tt> type system. Remember, Gtype is a
<span class="emphasis"><em>dynamic</em></span> type system. Unlike
languages like C++, where the types of all classes are known at
compile-time, the majority of all the types used with GTK+ are
registered on demand, except for the primitive data types and the
base class <tt class="classname">GObject</tt> which are registered
as <span class="emphasis"><em>fundamental</em></span> types. As a
result, in addition to being able to specify constructors and
destructors for the object (or initialisers and finalisers in Gtype
parlance), it is also possible to have initialisation and
finalisation functions for both the class and base. For example,
the class initialiser could be used to fix up the vtable for
overriding virtual functions in derived classes. In addition, there
is also an <tt class="methodname">instance_init</tt> function,
which is used in this example to initialise the class. It's similar
to the constructor, but is called after object construction.</p>
<p>All these functions are specified in a <span class=
"structname">GTypeInfo</span> structure which is passed to
<tt class="function">g_type_register_static(</tt>) to register the
new type.</p>
<p><tt class="function">ogcalc_class_init()</tt> is the class
initialisation function. This has no C++ equivalent, since this is
taken care of by the compiler. In this case it is used to override
the <tt class="methodname">finalize()</tt> virtual function in the
<tt class="classname">GObjectClass</tt> base class. This is used to
specify a virtual destructor (it's not specified in the
<span class="structname">GTypeInfo</span> because the destructor
cannot be run until after an instance is created, and so has no
place in object construction). With C++, the vtable would be fixed
up automatically; here, it must be done manually. Pure virtual
functions and default implementations are also possible, as with
C++.</p>
<p><tt class="methodname">ogcalc_init()</tt> is the object
initialisation function (C++ constructor). This does a similar job
to the <tt class="function">main()</tt> function in previous
examples, namely contructing the interface (using Glade) and
setting up the few object properties and signal handlers that could
not be done automatically with Glade. In this example, a second
argument is passed to <tt class="methodname">glade_xml_new()</tt>;
in this case, there is no need to create the window, since our
<tt class="classname">Ogcalc</tt> object is a window, and so only
the interface rooted from <tt class=
"classname">ogcalc_main_vbox</tt> is loaded.</p>
<p><tt class="methodname">ogcalc_finalize()</tt> is the object
finalisation function (C++ destructor). It's used to free resources
allocated by the object, in this case the GladeXML interface
description.</p>
<p><tt class="methodname">g_object_unref()</tt> is used to decrease
the reference count on a <tt class="classname">GObject</tt>. When
the reference count reaches zero, the destructor is run and then
the object is destroyed. There is also a <tt class=
"methodname">dispose()</tt> function called prior to <tt class=
"methodname">finalize()</tt>, which may be called multiple times.
Its purpose is to safely free resources when there are cyclic
references between objects, but this is not required in this simple
case.</p>
<p>An important difference with earlier examples is that instead of
connecting the window destroy signal to <tt class=
"methodname">gtk_main_quit()</tt> to end the application by ending
the GTK+ main loop, the delete signal is connected to <tt class=
"methodname">ogcalc_on_delete_event()</tt> instead. This is because
the default action of the delete event is to trigger a destroy
event. The object should not be destroyed, so by handling the
delete signal and returning <tt class="literal">TRUE</tt>,
destruction is prevented. Both the &quot;Quit&quot; button and the delete
event end up calling <tt class="methodname">gtk_widget_hide()</tt>
to hide the widget rather than <tt class=
"methodname">gtk_main_quit()</tt> as before.</p>
<p>Lastly, <tt class="filename">C/gobject/ogcalc-main.c</tt>
defines a minimal <tt class="function">main()</tt>. The sole
purpose of this function is to create an instance of <tt class=
"classname">Ogcalc</tt>, show it, and then destroy it. Notice how
simple and understandable this has become now that building the UI
is where it belongs - in the object construction process. The users
of <tt class="classname">Ogcalc</tt> need no knowledge of its
internal workings, which is the advantage of encapsulating
complexity in classes.</p>
<p>By connecting the hide signal of the <tt class=
"classname">Ogcalc</tt> object to <tt class=
"methodname">gtk_main_quit()</tt> the GTK+ event loop is ended when
the user presses &quot;Quit&quot; or closes the window. By not doing this
directly in the class it is possible to have as many instances of
it as one likes in the same program, and control over termination
is entirely in the hands of the user of the class - where it should
be.</p>
<p><span class="bold"><b>Listing 1:</b></span> <tt class=
"filename">C/gobject/ogcalc.h</tt></p>
<pre class="programlisting">
#include &lt;gtk/gtk.h&gt;
#include &lt;glade/glade.h&gt;
/* The following macros are GObject boilerplate. */

/* Return the GType of the Ogcalc class. */
#define OGCALC_TYPE (ogcalc_get_type())

/* Cast an object to type Ogcalc.  The object must
   be of type Ogcalc, or derived from Ogcalc for
   this to work.
   This is similar to a C++ dynamic_cast&lt;&gt;. */
#define OGCALC(obj) \
  (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
                      OGCALC_TYPE, Ogcalc))

/* Cast a derived class to an OgcalcClass. */
#define OGCALC_CLASS(klass) \
  (G_TYPE_CHECK_CLASS_CAST ((klass), \
                      OGCALC_TYPE, OgcalcClass))

/* Check if an object is an Ogcalc. */
#define IS_OGCALC(obj) \
  (G_TYPE_CHECK_TYPE ((obj), OGCALC_TYPE))

/* Check if a class is an OgcalcClass. */
#define IS_OGCALC_CLASS(klass) \
  (G_TYPE_CHECK_CLASS_TYPE ((klass), \
                            OGCALC_TYPE))

/* Get the OgcalcClass class. */
#define OGCALC_GET_CLASS(obj) \
  (G_TYPE_INSTANCE_GET_CLASS ((obj), \
                      OGCALC_TYPE, OgcalcClass))

/* The Ogcalc object instance type. */
typedef struct _Ogcalc Ogcalc;
/* The Ogcalc class type. */
typedef struct _OgcalcClass OgcalcClass;

/* The definition of Ogcalc. */
struct _Ogcalc {
  GtkWindow parent;
    /* The object derives from GtkWindow. */
  GladeXML *xml;    /* The XML interface. */
  /* Widgets contained within the window. */
  GtkSpinButton *pg_val;
  GtkSpinButton *ri_val;
  GtkSpinButton *cf_val;
  GtkLabel *og_result;
  GtkLabel *abv_result;
  GtkButton* quit_button;
  GtkButton* reset_button;
  GtkButton* calculate_button;
};

struct _OgcalcClass {
  /* The class derives from GtkWindowClass. */
  GtkWindowClass parent;
  /* No other class properties are required (e.g.
     virtual functions). */
};

/* The following functions are described in ogcalc.c */
GType ogcalc_get_type(void);
Ogcalc * ogcalc_new(void);
gboolean ogcalc_on_delete_event(Ogcalc *ogcalc,
                                GdkEvent *event,
                                gpointer data);
void ogcalc_reset(Ogcalc *ogcalc, gpointer data);
void ogcalc_calculate(Ogcalc *ogcalc,
                      gpointer data);
</pre>
<p><span class="bold"><b>Listing 2:</b></span> <tt class=
"filename">C/gobject/ogcalc.c</tt></p>
<pre class="programlisting">
#include &quot;ogcalc.h&quot;

static void ogcalc_class_init(OgcalcClass *klass);
static void ogcalc_init(GTypeInstance *instance,
                        gpointer g_class);
static void ogcalc_finalize(Ogcalc *self);

/* Get the GType of Ogcalc.  This has the side
   effect of registering Ogcalc as a new GType if it
   has not already been registered. */
GType ogcalc_get_type(void) {
  static GType type = 0;
  if(type == 0) {
    /* GTypeInfo describes a GType.  In this case,
       we only specify the size of the class and
       object instance types, along with an
       initialisation function.  We could have also
       specified both class and object
       constructors and destructors here as well. */
    static const GTypeInfo info = {
      sizeof (OgcalcClass),
      NULL,
      NULL,
      (GClassInitFunc) ogcalc_class_init,
      NULL,
      NULL,
      sizeof(Ogcalc),
      0,
      (GInstanceInitFunc) ogcalc_init
    };
    /* Actually register the type using the above
       type information.  We specify the type we are
       deriving from, the class name and type
       information. */
    type = g_type_register_static(GTK_TYPE_WINDOW,
                                  &quot;Ogcalc&quot;, &amp;info,
                                  (GTypeFlags) 0);
  }
  return type;
}

/* This is the class initialisation function.  It
   has no comparable C++ equivalent, since this is
   done by the compiler. */
static void ogcalc_class_init(OgcalcClass *klass) {
  GObjectClass *gobject_class
                        = G_OBJECT_CLASS (klass);
  /* Override the virtual finalize method in the
     GObject class vtable (which is contained in
     OgcalcClass). */
  gobject_class-&gt;finalize
            = (GObjectFinalizeFunc) ogcalc_finalize;
}

/* This is the object initialisation function.  It
   is comparable to a C++ constructor.  Note the
   similarity between &quot;self&quot; and the C++ &quot;this&quot;
   pointer. */
static void ogcalc_init(GTypeInstance *instance,
                        gpointer g_class) {
  Ogcalc *self = (Ogcalc *) instance;
  /* Set the window title */
  gtk_window_set_title(GTK_WINDOW (self),
                       &quot;OG &amp; ABV Calculator&quot;);
  /* Don't permit resizing */
  gtk_window_set_resizable(GTK_WINDOW (self), FALSE);
  /* Connect the window close button (&quot;destroy-
     event&quot;) to a callback. */
  g_signal_connect(G_OBJECT (self), &quot;delete-event&quot;,
                G_CALLBACK (ogcalc_on_delete_event),
                NULL);

  /* Load the interface description. */
  self-&gt;xml = glade_xml_new(&quot;ogcalc.glade&quot;,
                         &quot;ogcalc_main_vbox&quot;, NULL);

  /* Get the widgets. */
  self-&gt;pg_val = GTK_SPIN_BUTTON
    (glade_xml_get_widget (self-&gt;xml, &quot;pg_entry&quot;));
  self-&gt;ri_val = GTK_SPIN_BUTTON
    (glade_xml_get_widget (self-&gt;xml, &quot;ri_entry&quot;));
  self-&gt;cf_val = GTK_SPIN_BUTTON
    (glade_xml_get_widget (self-&gt;xml, &quot;cf_entry&quot;));
  self-&gt;og_result = GTK_LABEL
    (glade_xml_get_widget (self-&gt;xml, &quot;og_result&quot;));
  self-&gt;abv_result = GTK_LABEL
    (glade_xml_get_widget (self-&gt;xml, &quot;abv_result&quot;));
  self-&gt;quit_button = GTK_BUTTON
    (glade_xml_get_widget (self-&gt;xml, &quot;quit_button&quot;));
  self-&gt;reset_button = GTK_BUTTON
    (glade_xml_get_widget (self-&gt;xml, &quot;reset_button&quot;));
  self-&gt;calculate_button = GTK_BUTTON
    (glade_xml_get_widget (self-&gt;xml,
                               &quot;calculate_button&quot;));

  /* Set up the signal handlers. */
  glade_xml_signal_autoconnect(self-&gt;xml);

  g_signal_connect_swapped
    (G_OBJECT (self-&gt;cf_val), &quot;activate&quot;,
     G_CALLBACK (gtk_window_activate_default),
     (gpointer) self);
  g_signal_connect_swapped
    (G_OBJECT (self-&gt;calculate_button), &quot;clicked&quot;,
     G_CALLBACK (ogcalc_calculate),
     (gpointer) self);
  g_signal_connect_swapped
    (G_OBJECT (self-&gt;reset_button), &quot;clicked&quot;,
     G_CALLBACK (ogcalc_reset),
     (gpointer) self);
  g_signal_connect_swapped
    (G_OBJECT (self-&gt;quit_button), &quot;clicked&quot;,
     G_CALLBACK (gtk_widget_hide),
     (gpointer) self);

  /* Get the interface root and pack it into our
     window. */
  gtk_container_add 
    (GTK_CONTAINER (self), glade_xml_get_widget(
               self-&gt;xml, &quot;ogcalc_main_vbox&quot;));

  /* Ensure calculate is the default.  The Glade
     default was lost since it wasn't in a window
     when the default was set. */
  gtk_widget_grab_default
    (GTK_WIDGET (self-&gt;calculate_button));
}

/* This is the object initialisation function.  It is
  comparable to a C++ destructor.  Note the similarity
  between &quot;self&quot; and the C++ &quot;this&quot; pointer. */
static void ogcalc_finalize(Ogcalc *self) {
  /* Free the Glade XML interface description. */
  g_object_unref(G_OBJECT(self-&gt;xml));
}

/* Create a new instance of the Ogcalc class (i.e.
   an object) and pass it back by reference. */
Ogcalc * ogcalc_new(void) {
  return (Ogcalc *) g_object_new(OGCALC_TYPE, NULL);
}

/* This function is called when the window is about
   to be destroyed (e.g. if the close button on the
   window was clicked).  It is not a destructor. */

gboolean ogcalc_on_delete_event(Ogcalc *ogcalc,
          GdkEvent *event, gpointer user_data) {
  gtk_widget_hide(GTK_WIDGET (ogcalc));
  /* We return true because the object should not be
     automatically destroyed. */
  return TRUE;
}

/* Reset the interface. */
void ogcalc_reset(Ogcalc *ogcalc, gpointer data) {
  gtk_spin_button_set_value(ogcalc-&gt;pg_val, 0.0);
  gtk_spin_button_set_value(ogcalc-&gt;ri_val, 0.0);
  gtk_spin_button_set_value(ogcalc-&gt;cf_val, 0.0);
  gtk_label_set_text(ogcalc-&gt;og_result, &quot;&quot;);
  gtk_label_set_text(ogcalc-&gt;abv_result, &quot;&quot;);
}

/* Perform the calculation. */
void ogcalc_calculate(Ogcalc *ogcalc, gpointer data) {
  gdouble pg, ri, cf, og, abv;
  gchar *og_string;
  gchar *abv_string;
  pg = gtk_spin_button_get_value (ogcalc-&gt;pg_val);
  ri = gtk_spin_button_get_value (ogcalc-&gt;ri_val);
  cf = gtk_spin_button_get_value (ogcalc-&gt;cf_val);
  og = (ri * 2.597) - (pg * 1.644) - 34.4165 + cf;

  /* Do the sums. */
  if (og &lt; 60)
    abv = (og - pg) * 0.130;
  else
    abv = (og - pg) * 0.134;

  /* Display the results.  Note the &lt;b&gt;&lt;/b&gt; GMarkup
     tags to make it display in Bold. */
  og_string = g_strdup_printf(&quot;&lt;b&gt;%0.2f&lt;/b&gt;&quot;, og);
  abv_string = g_strdup_printf(&quot;&lt;b&gt;%0.2f&lt;/b&gt;&quot;, abv);
  gtk_label_set_markup(ogcalc-&gt;og_result, og_string);
  gtk_label_set_markup(ogcalc-&gt;abv_result, abv_string);
  g_free(og_string);
  g_free(abv_string);
}
</pre>
<p><span class="bold"><b>Listing 3:</b></span> <tt class=
"filename">C/gobject/ogcalc-main.c</tt></p>
<pre class="programlisting">
#include &lt;gtk/gtk.h&gt;
#include &lt;glade/glade.h&gt;
#include &quot;ogcalc.h&quot;

/* This main function merely instantiates the ogcalc
   class and displays its main window. */
int main(int argc, char *argv[]) {
  /* Initialise GTK+. */
  gtk_init(&amp;argc, &amp;argv);
  /* Create an Ogcalc object. */
  Ogcalc *ogcalc = ogcalc_new();
  /* When the widget is hidden, quit the GTK+ main
     loop. */
  g_signal_connect(G_OBJECT (ogcalc), &quot;hide&quot;,
                  G_CALLBACK (gtk_main_quit), NULL);

  /* Show the object. */
  gtk_widget_show(GTK_WIDGET (ogcalc));

  /* Enter the GTK Event Loop.  This is where all
     the events are caught and handled.  It is
     exited with gtk_main_quit(). */
  gtk_main();

  /* Clean up. */
  gtk_widget_destroy(GTK_WIDGET (ogcalc));
  return 0;
}
</pre></div>
</p>
<p><strong>Notes:</strong>&nbsp;</p>
<p><em>More fields may be available via dynamicdata ..</em></p>
</div>
</channel>
</rss>
