Skip to content

gtk_hl: a high level interface for gtk fortran

vmagnin edited this page Apr 28, 2022 · 5 revisions

Introduction

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 which was an earlier attempt to build a widget toolkit for Fortran but is now defunct since 2008.

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:

Standard:

abut = gtk_button_new_with_label("Apply"//c_null_char)
call g_signal_connect(abut, "clicked", c_funloc(run_it), c_null_ptr)
call gtk_widget_set_tooltip_text(abut, "Run the analysis"//c_null_char)
call gtk_widget_set_sensitive(abut, FALSE)

High Level:

abut = hl_gtk_button_new("Apply"//c_null_char, sensitive=FALSE, &
   & clicked=c_funloc(run_it), tooltip="Run the analysis"//c_null_char)

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.

Requirements

The gtk_hl interface depends on the low-level gtk-fortran interfaces and on GTK. While there may be differences between the low-level calls between GTK major versions, 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. 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-?-fortran-modscan command (where ? is the GTK major version) 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).

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

   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.
  2. 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.

gpointer

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).

Integers

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.

Reals

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.

Booleans

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.

Strings

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"//c_null_char, and a character variable should be passed as trim(string)//c_null_char. 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.

Characters

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).

Routines

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.

Callbacks

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).

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.

  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.

    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:

  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.

Drawing

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 library is installed with Fortran support, then an integration module is provided to allow it to draw to a GTK drawing area. See Plplot integration.

Routines

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.

Clone this wiki locally