The Problem

GTK is a popular library to build portable GUI (Graphical User Interface) written in C and an Ada binding (GTKAda) is available on Adacore Libre site.

As of 2012, there is the support of the interactive GTK builder Glade3. We describe a basic example of the different steps needed to build a “hello world” with Glade3.

Prerequisite

You must have GTKAda installed and the environnement variable GPR_PROJECT_PATH set to find the project file gtkada.gpr For Windows, GPR_PROJECT_PATH should look like :

C:\GNAT\2012\lib\gnat;C:\GtkAda\lib\gnat (if you used the default locations)

Creating the XML description file

Glade3 generates interactively XML files that describes the UI. Our basic UI will contain a Main_Window with a Label. We must take care of the termination by declaring a signal “destroy” in Main_Window to quit the app, otherwise the program will not end when we close the window.

Defining the signal for termination

glade3 output : simple.glade

<?xml version="1.0" encoding="UTF-8"?>
<interface>
  <requires lib="gtk+" version="2.18"/>
<!-- interface-naming-policy project-wide -->
  <object class="GtkWindow" id="main_window">
    <property name="width_request">400</property>
    <property name="height_request">100</property>
    <property name="visible">True</property>
    <property name="can_focus">False</property>
    <property name="title" translatable="yes">Glade 3 simple demo with Ada</property>
    <property name="resizable">False</property>
    <property name="window_position">center</property>
    <signal name="destroy" handler="Main_Quit" swapped="no"/>
    <child>
      <object class="GtkLabel" id="some_label">
        <property name="width_request">200</property>
        <property name="height_request">50</property>
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <property name="label" translatable="yes">Hello from Glade 3 ;-)</property>
      </object>
    </child>
  </object>
</interface>

Ada code

First, we declare the handler/callback that will be attached to the “destroy” signal. The callbacks must be in a package, otherwise you run into accessibility errors.

The Gobject event “destroy” is handled by “Main_Quit” in XML, and “Main_Quit” is implemented by the Ada procedure Simple_Callbacks.Quit.

The logical connection is :

signal : GTKObject.destroy => Handler name : "Main_Quit" => Ada callback : Simple_Callbacks.Quit

Callback spec : simple_callbacks.ads

with Gtkada.Builder; use Gtkada.Builder;
 
package Simple_Callbacks is
 
   procedure Quit (Object : access Gtkada_Builder_Record'Class);
 
end Simple_Callbacks;

Callback body : simple_callbacks.adb

with Gtk.Main;
 
package body Simple_Callbacks is
 
   procedure Quit (Object : access Gtkada_Builder_Record'Class) is
      pragma Unreferenced (Object);
   begin
      Gtk.Main.Main_Quit;
   end Quit;
 
end Simple_Callbacks;

Main program : simple_glade3.adb

Read the XML description, register the termination handler, connect, start the Gtk.Main.Main loop, and that's it.

with Gtk;            use Gtk;
with Gtk.Main;       use Gtk.Main;
with Glib.Error;     use Glib.Error;
with Gtk.Widget;     use Gtk.Widget;
with Ada.Text_IO;
with Gtkada.Builder; use Gtkada.Builder;
 
-- the following package is user defined.
with Simple_Callbacks; use Simple_Callbacks;
 
procedure Simple_Glade3 is
 
   Builder : Gtkada_Builder;
   Error   : Glib.Error.GError;
 
begin
   Gtk.Main.Init;
   --     Step 1: create a Builder and add the XML data,
   Gtk_New (Builder);
   Error := Add_From_File (Builder, "simple.glade");
   if Error /= null then
      Ada.Text_IO.Put_Line ("Error : " & Get_Message (Error));
      Error_Free (Error);
      return;
   end if;
   --     Step 2: add calls to "Register_Handler" to associate your
   --     handlers with your callbacks.
   Register_Handler
     (Builder      => Builder,
      Handler_Name => "Main_Quit", -- from XML file <signal handler=..>
      Handler      => Simple_Callbacks.Quit'Access);
 
   -- Step 3: call Do_Connect. Once to connect all registered handlers
   Do_Connect (Builder);
 
   --  Find our main window, then display it and all of its children.
   Gtk.Widget.Show_All (Get_Widget (Builder, "main_window"));
   Gtk.Main.Main;
 
   -- Step 4: when the application terminates or all Windows described through
   --         your builder should be closed, call Unref to free memory
   --         associated with the Builder.
   Ada.Text_IO.Put_Line ("The demo is over");
   Unref (Builder);
 
end Simple_Glade3;

Project file : simple.gpr

with "gtkada";
 
project Simple is
 
   type Gtkada_Kind_Type is
      ("static", "relocatable");
   Library_Type : Gtkada_Kind_Type := external ("LIBRARY_TYPE", "static");
   for Source_Dirs use ("src");
   for Object_Dir use "obj";
   for Exec_Dir use ".";
 
   for Main use ("simple_glade3.adb");
 
   package Builder is
      for Default_Switches ("ada") use ("-s");
   end Builder;
 
   package Compiler is
            for Default_Switches ("ada") use ("-O2", "-gnat05");
   end Compiler;
 
   package Linker is
      -- for Windows production only ;; remove for Linux / Mac / Win debug
      for Default_Switches ("ada") use ("-mwindows");
   end Linker;
 
end Simple;

The final result

What you shall get !

Credits

This page was adapted from codes of Pascal Pignard in Utilisation de la bibliothèque graphique GTKAda (mai 2012) and a post of Brian Drummond from GTKAda archives. GtkAda builder example

More

Complete demo programs available from Sourceforge

  • Lorenz chaotic attractor : Drawing demo with GTK timer loop.
  • Julia set : GTK interface mixed with Ada tasking.
  • For RAD, gate3 is an Ada code sketcher : you build the User Interface with glade3.8, and gate3 generates an Ada prototype that is a valid Ada code.

Navigation