gtk_hl: A high level interface for Gtk Fortran

jtappin edited this page Aug 31, 2012 · 17 revisions


This document describes a higher-level interface to the Gtk-Fortran package. The aim of this module to to make the development of widget interfaces somewhat less intimidating to the typical scientific programmer. The two main drivers of this have been: the widget routines in IDL (Interactive Data Language which allow the development of complex GUIs with a much simpler interface than that in plain GTK; and the Pilib project (web site now defunct) which was an earlier attempt to build a widget toolkit for Fortran but which is now essentially defunct.

The intent is not to replace the lower-level Gtk calls, but to supplement them by bundled routines that allow widget elements to be constructed in fewer lines of code. The majority of the routines provided are constructors, however when setting or getting values involves multiple calls, then such routines are also provided.

Not all the possible widgets are covered, nor are all the possible options. However most things likely to be needed for setting up and monitoring progress in a scientific program are covered.

The following simple example shows the comparison between the standard interface and the high-level interface in setting up an ``Apply'' button for a program:

```fortran abut = gtk_button_new_with_label("Apply"//cnull) call g_signal_connect(abut, "clicked", c_funloc(run_it), & & NULL) call gtk_widget_set_tooltip_text(abut, "Run the analysis"//cnull) call gtk_widget_set_sensitive(abut, FALSE) ```
High Level:
```fortran abut = hl_gtk_button_new("Apply"//cnull, sensitive=FALSE, & & clicked=c_funloc(run_it), tooltip="Run the analysis"//cnull) ```
While this example only reduces four calls to one, in other cases (particularly where the GtkTreeView is involved) many tens of lines may be saved. It will be seen that this interface makes heavy use of Fortran's optional argument system to allow the user to set those properties he needs while letting others take their default values without having to pass large numbers of unnecessary dummy values.



The `gtk_hl` interface depends on the low-level Gtk-Fortran interfaces and on Gtk+ (currently it's being developed against version 2.24 and 3.4). While there may be differences between the low-level calls between Gtk+ versions 2 and 3, these should be papered-over by the high level interface.

Including gtk_hl in your program

To use the high level interface in your program, it is necessary to include the `gtk_hl` module in your code. Note that because this already includes `gtk_sup` and `iso_c_binding` you do not need to include them explicitly, doing so will only slow down compilation. The example below shows the essential elements to make `gtk_hl` available. Because it only includes those items from `gtk` that it needs internally, you will still need to include the relevant items from `gtk` in your code. The `gtk-2-fortran-modscan` or `gtk-3-fortran-modscan` command can be used to scan the code, or it may be easier just to try compiling and linking and add the missing definitions as the compiler finds them (depends on the size of the code). ```fortran module handlers use gtk_hl use gtk, only: ... ! Any other modules, e.g. gdk g ... implicit none contains . . . end module handlers program my_gui_app use handlers implicit none . . . end program my_gui_app ``` If you need to add drawing area(s) to your application, then you also need a ```fortran use gtk_draw_hl ``` line in your program.

Argument conventions

Arguments are passed in the same forms as for the low-level interface. While it would in principle be possible to simplify the calls, there are two problems: 1. This could not be done in the signal handlers as these must be called directly from the underlying Gtk libraries. 1. It would then make the mixing of high and low level calls look different, and thus be likely to introduce more errors than leaving the C-compatibility constructs exposed. The argument conventions described below also apply to the low-level interface.
Widgets, lists and other Gtk objects
These are declared as being `type(c_ptr)`. Normally you do not need to do anything with these apart from passing them to and from Gtk.
When an argument is of type `gpointer` in raw Gtk, then the value may be of any type but it must be declared with the `target` attribute, and then passed to the routine as `c_loc(variable)`.
Integer variables are declared as `integer(kind=c_int)` for `gint`, `integer(kind=c_long)` for `glong` or `integer(kind=c_short)` for `gshort`. Passing by value or reference is handled by the low-level interface and Fortran's C interoperability system. There are a small number of cases where an explicit 8, 16 or 64-bit integer is needed, in these cases the `c_int8_t`, `c_int16_t` or `c_int64_t` kinds should be used.
Real variables are declared as either `real(kind=c_double)` for `gdouble` arguments or `real(kind=c_float)` for `gfloat`. Other issues are as for integers.
The boolean type used by Gtk (`gboolean`) is **NOT** the same thing as a C `bool` type, therefore any boolean variable should be declared as `integer(kind=c_int)` but only be given the pre-defined values `TRUE` or `FALSE`. Fortran logical types should not be used.
Character strings are handled in very different ways in Fortran and C. In C, they are just an array of characters terminated by a null character, in Fortran a length value is passed as well and there is no terminator. If a character constant is passed to a routine it should be written as `"I am a compatible string"//CNULL`, and a character variable should be passed as `trim(string)//CNULL`. String arguments to handler routines should be declared as `character(kind=c_char), dimension(*)`. A normal character variable passed to a dummy argument with the C-compatible form is accepted by the interface-matching rules, but it will confuse the selection of overloaded names (at least in `gfortran`), for this reason character arguments to overloaded routines are declared in the "Fortran" way. Character arrays, are declared internally in the _Fortran_ way, and the high-level interface routines handle the conversions.
Character (`gchar` or `guchar`) arguments are not seen by C in at all the same way as strings. And although a scalar character constant of variable of length 1 is passed in a way that C understands, a substring expression of length 1 is not. For this reason and also because of some changes in the handling of character `gvalue` types in glib 2.32, all character arguments are treated as 8 bit integers so to pass the character `'a'` to a gtk+ routine you should pass `ichar('a',c_int8_t)`.
If a function or subroutine is to be passed as an argument (e.g. a signal callback routine) then it must be defined with the `bind(c)` attribute, and it is passed as `c_funloc(procedure)`.

When a high-level routine provides an argument to set a signal handler, the argument name is just the signal name with hyphens (-) replaced with underscores (_). The user data argument is simply data if only a single signal is handled, and data_<signal name> when several are present.


Callback routines (also known as signal handlers or event handlers) are called when an action is performed on a widget (e.g. clicking a button). They are usually (though not always) subroutines (details of all signals and their requirements can be found in the Gtk documentation page at Generally callbacks fall into 2 categories:

Gtk Signals
Here there are usually just 2 arguments to the subroutine, the widget emitting the signal and a user data value, so a suitable declaration would be. ```fortran subroutine my_handler(widget, gdata) bind(c) type(c_ptr), value :: widget, gdata ``` The `gdata` argument is the data that was passed when the signal was attached, it may be of any type and can be used in any way the user thinks fit, values may be passed both into and out of the handler by this means, and the value does not even need to be C-interoperable. To access its contents you must declare a fortran pointer to the appropriate type and use the `c_f_pointer` subroutine to convert the pointer. In the example below, the data is just an integer. ```fortran integer, pointer :: fdata if (c_associated(gdata)) then call c_f_pointer(gdata, fdata) . . ! Do stuff with the data . endif ``` Note that you don't need to re-attach the Fortran values in any way after manipulating them.
Gdk Events
Signals passed from the Gdk drawing system (including GdkPixbuf and Cairo) have associated event structures which are passed as a second argument to the callback, so the declaration becomes: ```fortran subroutine my_event_handler(widget, event, gdata) bind(c) type(c_ptr), value :: widget, event, gdata ``` The event structure definitions are provided in the `gdk_events` module.


There is a separate high level drawing interface gtk_draw_hl that provides a bundled drawable widget with automatic expose event repair. To use this you need to have:

    use gtk_draw_hl

in your program. If you want to do more than very basic event processing you will also need gdk_events.

If the plplot libraries are installed with Fortran95 support, then an integration module is provided to allow them to draw to a gtk drawing surface. Plplot integration.


The interfaces to the individual routines are listed on the Highlevel-API page.

The supplemental routines and definitions are described in the Supplementary routines page.

The high level drawing interface is described in the High level drawing api page. NOTE There was a major change to the drawing API on 1 July 2011. This was to change the backing store from using a GdkPixbuf to a Cairo Image Surface, which facilitates updating plots rather than redrawing them.