Lichtung im GTK-Wald
GUI-Programme erstellen mit Glade
Ein Blick hinter den Kulissen
Alle Signale sind angebunden und alle Widget-Eigenschaften festgelegt – nun gilt es, das Projekt etwa mit Datei / Speichern zu speichern und den Interface-Quelltext zu generieren (z. B. via Datei / Quelltext speichern). Werfen wir jetzt einen Blick auf den von Glade erstellten Code.
Diesen legt das Programm per Default in vier Dateien unterhalb des src-Directories im Projektverzeichnis (im Beispiel hello) ab: main.c ist dafür verantwortlich, dass beim Applikationsstart Fenster auf dem Bildschirm erscheinen. interface.c enthält den von Glade generierten Code für die Bedieneroberfläche – allerdings ohne Funktionalität. callbacks.c beinhaltet die Definitionen aller über das Eigenschaftsfenster angelegten Handler-Funktionen. Hier wird also die eigentliche Programmfunktionalität realisiert. support.c hingegen sammelt Sonderfunktionen zur Vereinfachung der Anbindung von Programminterface und -funktionalität.
Über den Menüeintrag Datei / Projekt Optionen... erhält man einen Dialog zum Ändern dieser Dateinamen. Wie sehr man sie in dessen Karteikarte C Optionen auch manipuliert – ihre Funktionalität wird dabei nicht beeinflusst. Weitere Quelltext-Dateien bzw. Bibliotheken müssen eigenhändig in die Datei Makefile.am im Projektverzeichnis unterhalb von src eingefügt werden. Das ist aber einfach, weil Makefile.am die entsprechenden Einträge bereits parat hält, von denen die wichtigsten Projektname_SOURCES für weitere Quelltext- und Projektname_LDADD für Bibliotheksdateien sind. Die Namen der ersteren fügt man einfach mit Leerzeichen getrennt an; bei der Angabe zusätzlicher Bibliotheken gilt es, sich an die entsprechende gcc-Konvention [4] zu halten (für eine Bibliothek namens libzvt wäre zum Beispiel -lzvt einzufügen).
In hello/src/main.c ist nicht viel zu sehen, eigentlich nur die Funktion main(). Diese verbirgt aber bereits interessante Geheimnisse (Listing 1): Zuallererst wird eine Variable – in Wahrheit ein Objekt – vom Typ GtkWidget * namens window1 deklariert. window1 wird in einem der nächsten Schritte zur Referenz auf das Hauptfenster.
Die anschließend aufgerufene Funktion gtk_set_locale() hat die Aufgabe, das Programm mit lokalen Einstellungen bezüglich Sprache, Datums- bzw. Währungsdarstellung etc. zu versorgen. Da wir uns bei unserer einfachen "Hello World"-Anwendung nicht um die Lokalisierung kümmern, wird daraus aber auch auf "deutschsprachigen" Rechnern nicht auf magische Weise ein "Hallo Welt!".
Die zweite Funktion, gtk_init(), muss nun vor allen weiteren Methoden aufgerufen werden. Sie sorgt dafür, dass die notwendigen Initialisierungsvorgänge für das Programm stattfinden und bereitet eventuelle Kommandozeilen-Argumente für die weitere Bearbeitung durch GTK+-Funktionen vor. Als nächstes kommt zweimal eine Funktion aus der Datei support.c zum Zuge. Diese hat die Aufgabe, das Programm darüber zu informieren, in welchen Verzeichnissen nach Pixmaps (Icons bzw. Bildern) gesucht werden soll.
Der nun folgende Code-Block ist das Herz der main()-Funktion: Hier bekommt window1 von der in der Datei interface.c enthaltenen Funktion create_hello_window() die Referenz zum Hauptfenster unserer Applikation. gtk_widget_show(window1) ist für das Anzeigen des Fensters zuständig. Als letztes muss man gtk_main() aufrufen, um das Programm in einen Ereignis-Wartezustand zu versetzen. return 0 sorgt dafür, dass main() wie erwartet einen ganzzahligen (Integer-)Wert an die aufrufende Shell zurückgibt.
Listing 1
Die
main()-Funktion
int
main (int argc, char *argv[])
{
GtkWidget *window1;
gtk_set_locale ();
gtk_init (&argc, &argv);
add_pixmap_directory (PACKAGE_DATA_DIR "/pixmaps");
add_pixmap_directory (PACKAGE_SOURCE_DIR "/pixmaps");
/* [Kommentare gelöscht]
*/
window1 = create_hello_window ();
gtk_widget_show (window1);
gtk_main ();
return 0;
}
Die Datei interface.c enthält ebenfalls nur eine einzige Funktion: create_hello_window(). Diese ist für den Aufbau des Hauptfensters samt dessen Inhalt zuständig. Als Grundregel gilt: Für jedes neue Fenster einer Applikation, sei es ein "normales" Fenster, ein Dialog oder ein Popup-Menü, wird immer eine neue create-Funktion kreiert. Sie sorgt dafür, dass die anderen Widgets dieses Fensters das Licht der Welt erblicken. Für uns sind nur zwei Funktionsaufrufe wirklich von Bedeutung:
gtk_signal_connect (GTK_OBJECT (hello_window), "delete_event", GTK_SIGNAL_FUNC (on_hello_window_delete_event, NULL); gtk_signal_connect (GTK_OBJECT (hello_button), "clicked", GTK_SIGNAL_FUNC (on_hello_button_clicked), NULL);
Sie verbinden die Signale delete_event und clicked mit den Callback-Funktionen on_hello_window_delete_event() bzw. on_hello_button_clicked(). Als jeweils erstes Argument ist angegeben, auf welche Objekte wir uns beziehen: hello_window bzw. hello_button.
Doch woher kommen diese beiden Funktionen? Ihre Definitionen enthält die Datei callbacks.c, und hier müssen wir ein wenig programmieren.
Den Inhalt von on_hello_window_delete_event() bildet ein Aufruf der Funktion gtk_exit(0), mit der man das Programm verlässt. printf("Hello World!"); sorgt in on_hello_button_clicked() dafür, dass die Zeichenkette Hello World! in die Konsole geschrieben wird. Listing 2 zeigt den kompletten Inhalt von callbacks.c.
Listing 2
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdio.h>
#include <gtk/gtk.h>
#include "callbacks.h"
#include "interface.h"
#include "support.h"
/*
* Diese Funktion sorgt dafür, dass das Programm beim Drücken
* des Symbols zum Fensterschließen richtig beendet und die
* Eingabeaufforderung für weitere Kommandos freigegeben wird.
*/
gboolean
on_hello_window_delete_event (GtkWidget *widget,
GdkEvent *event,
gpointer user_data)
{
/* Aufruf der Funktion zum Verlassen des Programms */
gtk_exit(0);
/* FALSE wird zurückgegeben, weil die Funktion die Rückgabe
* einer booleschen Variable erwartet. Wenn man das nicht
* macht, meckert der Compiler mit einer Warnung, dass die
* Funktion nicht richtig geschrieben wurde…
*/
return FALSE;
}
/*
* Funktion zur Ausgabe der Zeichenkette "Hello World!" in der
* Konsole, falls der Knopf mit der Beschriftung "Hello World"
* gedrückt wird
*/
void
on_hello_button_clicked (GtkButton *button,
gpointer user_data)
{
printf("Hello World!\n");
}
Letzter Akt
Nun ist das Programm fertig. Um es zu übersetzen, gibt man im Projektverzeichnis folgenden Befehl ein:
./autogen.sh [--prefix=Appverzeichnis]
Damit werden alle für Glade benötigten Systemressourcen überprüft und ein passendes Makefile erzeugt. Mit make starten Sie dann den Kompilierungsvorgang. Aus dem Projektverzeichnis heraus rufen Sie das neuerzeugte Binärprogramm mit relativer Pfadangabe als
./src/hello
auf. make install kopiert das erzeugte Kompilat ins ggf. hinter --prefix= angegebene Applikationsverzeichnis. Bei fehlender Präfixangabe ist dies normalerweise das Verzeichnis /usr/local/bin.
Wer Appetit auf mehr hat, findet in [5] eine etwas anspruchvollere Beispiel-Applikation, deren Quellcode unter [6] bereitsteht.
Glossar
OO-Entwicklung
Eine Programmierungsmethodik, die daraus besteht, Daten (Zustände) und Funktionen bzw. Methoden (Verhalten) zu Objekten zusammenzukapseln, so wie es bei den Objekten des alltäglichen Lebens üblich ist. (Das Objekt "Zeitschrift" etwa kann den Zustand "gelesen" oder "ungelesen" haben und selbst beispielsweise herunterfallen.) "OO" steht kurz für "Objekt-Orientierung".
Signal-Handler
Eine Funktion oder ein Programmstück, die bzw. das automatisch beim Auslösen eines Signals (beispielweise durch das Anklicken eines Knopfes) aufgerufen wird. Auch bekannt als Callback-Funktion.
--prefix
Bei der Syntaxangabe (nicht nur in Manpages) werden mögliche, aber nicht notwendige Angaben (hier die Option "--prefix=Wert") laut Konvention von eckigen Klammern umgeschlossen. Mit dem configure-Präfix beschäftigte sich übrigens der "Answer Girl"-Artikel in Heft 01/2002.
\n
Symbol für einen Zeilenumbruch ("newline").
Infos
[1] http:///http://glade.gnome.org/
[2] http://libre.act-europe.fr/GtkAda/
[3] https://www.linux-magazin.de/pub/listings/user/2002/02/glade/
[4] http://gcc.gnu.org/onlinedocs/gcc-3.0.2/gcc_3.html#SEC16
[5] Rafael Peregrino da Silva: "Klein-Delphi", Linux-Magazin 12/01, S. 111 ff.
[6] https://www.linux-magazin.de/pub/listings/magazin/2001/12/glade/telefonbuch.tgz



