Writing GTK+ Programs with the Free Pascal Compiler
Being a fan of Delphi/Pascal for Windows programming, I have been curious about the Free Pascal project for quite some time. Free Pascal is an object-oriented compiled language that has been in development since 1993. I've also been interested in writing GUI applications for Linux, which recently prompted me to give Free Pascal a try. This article will briefly explain, based on my (limited) experience, how to write graphical applications using the GIMP Took Kit (GTK+) and the Free Pascal compiler on Linux.
The Free Pascal compiler (FPC) is a 32-bit compiler released under the GPL that is compatible with Turbo Pascal and implements Borland Delphi extensions, such as exceptions and overloading. It has been under development since 1993 and is available for a large number of operating systems including Linux, OS/2, Windows and DOS.
To install Free Pascal download the appropriate binary from the Free Pascal web site and follow the installation instructions. Don't forget to download the documentation, which is separate from the binary files. The Free Pascal compiler is written in Pascal, so don't download the source unless you already have a Pascal compiler on your machine. The standard installation provides the compiler, several Pascal tools, a complete set of system units, documentation and example programs.
The program in this article uses the GTK package included with the Free Pascal installation. It was written based on information from a tutorial called "Using GTK+ with FPC" (see Resources), examples included in the FPC doc directory and a series of articles written by Free Pascal's authors, which are available from the FPC site.
I wanted to do a bit more than the usual "Hello, World" example program, so the application presented here allows the user to enter temperatures using slider bars or text boxes and convert them between Fahrenheit and Celsius.
This article will focus more on the GTK+ aspects of the program and less on general Pascal programming. Please note that I use "controls" and "widgets" interchangeably throughout the text.
To use the GTK+ libraries, you need to include Free Pascal's GTK units in the program's uses clause, as shown below:
uses glib, gdk, gtk
Initializing and running a GTK+ program is handled in three basic steps, listed the bottom of the program.
First, call gtk_init with any command-line arguments that were supplied:
This initializes everything needed to use the toolkit and terminates the application if the toolkit can't be initialized. The gtk_init routine strips any command line options it recognizes (e.g., which display to use) and returns the rest to your code.
Next, create the application's window and show it. This is handled by the two lines shown below. Instead of using a routine such as create_main_window, you could also put all the creation code inline.
Finally, call gtk_main(), which starts the GTK+ event handling loop.
In code, it is common for one control to affect another control's settings. In a small application it might be possible to handle this by defining all the controls globally, but this could be difficult to maintain in large programs. A common practice is to wrap a form and its controls in their own record type, then manipulate the controls through the record. In this program the TMainWindow type declaration wraps the applications main window and controls.
The main window and controls are created in the create_main_window routine. First the application's main window is created, and a caption is assigned to it with the following lines:
Window := pGtkWindow(gtk_window_new(GTK_WINDOW_TOPLEVEL)); gtk_window_set_title(Window, 'Temperature Conversion');
Next a horizontal packing box is created, hBox, to hold the various controls. Boxes are containers that hold widgets and provide rudimentary positioning and sizing control. A horizontal box positions controls on a line from left to right. A vertical box is also available that positions controls in a column. A layout of some complexity can be obtained by putting boxes inside of other boxes, something done in this program.
The individual controls are then created and packed into the horizontal box. The user interface consists of two vertical sliders and two text entry boxes, with labels above them and buttons below.
Creating a control won't automatically make it visible when the program is run; it also has to be shown, which can be accomplished in a couple of ways. One way would be to call the gtk_widget_show routine for each widget created. Another way is to call the gtk_widget_show_all routine with the container holding all the controls. That is the method used in this application, with the top-level container being the application's main window itself.
In order for controls to be able to respond to user input, signal-handling routines have to be assigned to them. Assigning a signal handler involves specifying the control to bind to, the signal to watch for, the address of the routine to call when the signal occurs and a piece of user-defined data.
The first signal handler assigned is the one for the main window's destroy event. This is called when the application is closed; it should free any used resources and then call GTK+'s gtk_main_quit routine.
This is accomplished by a call to gtk_signal_connect, binding the main window's destroy event to the program's quit_app procedure. gtk_signal_connect accepts the widget to monitor, the signal to watch for, the routine to handle the signal and a user-defined piece of data. In this program the user-defined data is the PMainWindow record created for this application. If you didn't want to pass any data to the signal handler, then you would just use Nil as the last parameter.
gtk_signal_connect(pGtkObject(Window), 'destroy', GTK_SIGNAL_FUNC(@quit_app), Result);
Result holds an instance of PMainWindow, which was created at the beginning of the function. The quit_app routine is declared as shown with:
procedure quit_app (widget: pGtkWidget; Window : PMainWindow); cdecl;
Notice the cdecl flag at the end of all signal handlers in the program. If you leave this off of your signal handler procedure declarations, the application will crash when they are called. Here is the explanation of cdecl from the FPC documentation:
A function that has a cdecl modifier, will be used with C calling conventions, that is, the caller clears the stack. Also the mangled name will be the name exactly as it is declared. cdecl is part of the function declaration, and hence must be present both in the interface and implementation section of a unit.
Vertical sliders for setting the temperatures are created (fahSlider, celSlider) with calls to the gtk_vscale_new function. The slider's display properties are set with calls to scale_set_default_values. The slider's initial, minimum, maximum and paging values are determined by GtkAdjustment widgets, which are created for each slider and supplied to the gtk_vscale_new calls.
When a slider is moved it emits a value_changed signal. Signal handlers (CsliderChanged and FsliderChanged) for this event are assigned to each slider.
A vertical packing box is created to hold the Fahrenheit label, text box and F to C button. The widgets are placed in the box, and the box is then added to the application's main horizontal box.
The same thing is done for the Celsius label and text box. Finally, the Celsius slider is placed in the application's window as the right-most control.
The FtoC and CtoF functions convert temperatures between Fahrenheit and Celsius. SetSliderValue moves a slider to a specific value, while DisplayTemp displays a temperature in the specified text box.
The buttonCtoF routine is called when the C to F button is clicked. It reads the value from the Celsius text box, attempts to convert it to Fahrenheit and then displays the new value. Since the program doesn't prevent the user from entering letters into the text box, the try-except-end block traps the error generated if a non-numeric string is sent to the StrToFloat function. The buttonFtoC routine does the same thing but converts the Fahrenheit values to Celsius.
FsliderChanged and CsliderChanged are the signal handlers that get called when the sliders are moved. They read the slider's current value and display it in the appropriate text box.
The Linux version of Free Pascal doesn't come with an integrated development environment (IDE). The Free Pascal site provides a link to RHIDE, which is similar to Borland's old IDEs; however, it doesn't work well in an xterm. Another Borland-like IDE is xwpe-alpha, but its web site acknowledges that it has problems with Free Pascal.
I ended up configuring Vim's quick-fix mode to use Free Pascal by adding the following two lines to my ~/.vimrc file:
set makeprg=fpc\ -v0\ -l- % set errorformat=%f(%l\\,%c)\ %m
In the .vimrc file, spaces are escaped with the \ character; % is the name of the file being edited. FPC's -v0 option tells the compiler not to show anything but compile errors, while -l- prevents the FPC logo from printing. These make it easier for Vim to sort through any errors returned by the compiler.
Vim 6.1 (and possibly earlier versions) has a switch for turning on Free Pascal syntax highlighting. Set pascal_fpc in your ~/.vimrc like this:
This article was meant to provide a brief look at Free Pascal and using it to write GTK+ applications. If using the GTK units included with Free Pascal seems a bit daunting, projects are out there that aim to make it easier to use GTK+. The GTKPas project provides an OOP wrapper for GTK+, while the Lazarus project is trying to implement a Delphi work-alike using GTK+ on Linux.
With all of the programming languages available for Linux, you might not ever think of Pascal, even though it is the language used for Delphi and Kylix. Pascal is a mature language, and it is still possible to find Pascal books in your local bookstore.