-
Notifications
You must be signed in to change notification settings - Fork 43
gtk_hl: a high level interface for gtk fortran
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.
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.
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.
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:
- This could not be done in the signal handlers as these must be called directly from the underlying GTK libraries.
- 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.
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"//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.
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).
Generally callbacks fall into 2 categories:
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.
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.
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.
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.
- Installation
- My first gtk-fortran application
- Drawing an image in a PNG file (without GUI)
- A program also usable without GUI
- Using Glade3 and gtkf-sketcher (GTK 3)
- Using gtk-fortran as a fpm dependency
- Debugging with GtkInspector
- Learning from examples
- Video tutorials
- How to start my own project from a gtk-fortran example
- git basics
- CMake basics
- Alternatives to CMake
- How to migrate to GTK 4
- How to contribute to gtk-fortran
- How to hack the cfwrapper