GTK+

GTK+ is a multi-platform widget toolkit that is used for the implementation of graphical user interfaces on Linux, Unix, macOS, and Microsoft Windows. It is mainly developed by The GNOME Project. The toolkit is written in C, but bindings are available for many programming languages.

The gtk-fortran interface bindings provide access to GTK+ 2/3/4 from Fortran 2003. The source code is licenced under GNU GPLv3 with GCC Runtime Library Exception that permits the use in closed source software.

gtk-fortran

We first have to build the Fortran interface library gtk-fortran. Create a directory build/, run CMake to output a Makefile, and then compile the libraries:

$ git clone https://github.com/vmagnin/gtk-fortran
$ cd gtk-fortran/
$ mkdir build && cd build/
$ cmake ..
$ make

If you do not want to include bindings to PLplot, run CMake instead with:

$ cmake -DEXCLUDE_PLPLOT=1 ..

If multiple Fortran compilers are installed, a specific one can be forced with the -DCMAKE_Fortran_COMPILER option, for example:

$ cmake -DCMAKE_Fortran_COMPILER=gfortran10 -DCMAKE_INSTALL_RPATH=/usr/local/lib/gcc10/ ..

The static library libgtk-3-fortran.a and the shared library libgtk-3-fortran.so are then located in gtk-fortran/build/src/, the Fortran module files for linking in gtk-fortran/build/src/modules/.

Example

gtk-fortran
Fig. 1: Simple GTK+ 3.0 application in Fortran

The demo application (fig. 1) shows how to add widgets to a GTK+ window and connect these with signal handlers. Further examples can be found in gtk-fortran/examples/.

Due to the large coverage of the GTK+ Fortran interfaces, it is recommended to import only actually used parameters and procedures from the gtk module, in order to reduce the compilation time.

! demo.f90
module handlers
    use, intrinsic :: iso_c_binding, only: c_ptr, c_int
    use :: gtk, only: FALSE, gtk_main_quit
    implicit none
contains
    function hello(widget, gdata)
        type(c_ptr), value  :: widget
        type(c_ptr), value  :: gdata
        integer(kind=c_int) :: hello

        print '(a)', 'Hello, World!'
        hello = FALSE
    end function hello

    subroutine destroy(widget, gdata)
        type(c_ptr), value :: widget
        type(c_ptr), value :: gdata

        call gtk_main_quit()
    end subroutine destroy
end module handlers

program main
    use, intrinsic :: iso_c_binding, only: c_funloc, c_int, c_null_char, c_ptr
    use :: gtk, only: FALSE, GTK_WINDOW_TOPLEVEL, TRUE, g_signal_connect, &
                      gtk_box_pack_start, gtk_button_new_with_label, &
                      gtk_container_add, gtk_container_set_border_width, &
                      gtk_hbox_new, gtk_init, gtk_main, gtk_widget_show, &
                      gtk_window_new, gtk_window_set_default_size, &
                      gtk_window_set_title
    use :: handlers
    implicit none
    type(c_ptr) :: window
    type(c_ptr) :: box
    type(c_ptr) :: button

    call gtk_init()

    ! Create window.
    window = gtk_window_new(GTK_WINDOW_TOPLEVEL)
    call gtk_window_set_default_size(window, 200, 100)
    call gtk_window_set_title(window, 'GTK+ Fortran' // c_null_char)
    call gtk_container_set_border_width(window, 10)
    call g_signal_connect(window, 'destroy' // c_null_char, c_funloc(destroy))

    ! Create widget container.
    box = gtk_hbox_new(TRUE, 10);
    call gtk_container_add(window, box)

    ! Create widget.
    button = gtk_button_new_with_label('click me' // c_null_char)
    call gtk_box_pack_start(box, button, FALSE, FALSE, 10)
    call g_signal_connect(button, 'clicked' // c_null_char, c_funloc(hello))
    call gtk_widget_show(button)

    ! Show widget and window.
    call gtk_widget_show(box)
    call gtk_widget_show(window)

    call gtk_main()
end program main

The static library libgtk-3-fortran.a has to be copied to ./lib/, the Fortran module files to ./include/ of the example project directory:

Using GNU Fortran, compile and run the demo application with:

$ gfortran10 -I./include/ `pkg-config --cflags gtk+-3.0` -o demo demo.f90 \
  ./lib/libgtk-3-fortran.a `pkg-config --libs gtk+-3.0`
$ ./demo

Makefile

We can write a basic Makefile to simplify the build process:

.POSIX:

FC      = gfortran10
FFLAGS  = -I./include/ `pkg-config --cflags gtk+-3.0`
LDFLAGS = `pkg-config --libs gtk+-3.0`
LDLIBS  = ./lib/libgtk-3-fortran.a
SRC     = demo.f90
TARGET  = demo

.PHONY: all clean

all: $(TARGET)

$(TARGET):
	$(FC) $(FFLAGS) $(LDFLAGS) -o $(TARGET) $(SRC) $(LDLIBS)

clean:
	rm $(TARGET) *.mod

Execute the Makefile to compile the binary demo:

$ make
$ ./demo

References