Aus Linux-Magazin 12/2001

Rapid Application Development mit Glade

Die Zeiten sind vorbei, in denen Linux-Benutzer sämtlichen Code für das grafische Bediener-Interface eines Programms per Hand schreiben mussten. Zu Hilfe kommt Glade, eine RAD-Umgebung, die diesen Job erheblich vereinfacht.

Grafische Benutzeroberflächen mit dem Gimp-Toolkit GTK+ erstellen verlangt dank des GUI-Builders Glade keine pixelgenauen Rechen- und Programmierorgien mehr. Greift man noch dazu auf »libglade«, eine Bibliothek, die das Interface aus einer XML-Datei zur Laufzeit aufbaut, zurück, wird es sogar möglich, das Aussehen einer Applikation ohne Neuübersetzung des Quellcodes zu modifizieren.

Die erste Release dieses Wunders, dessen Hauptautoren Damon Chaplin und Martijn van Beers sind, erschien mit Version 0.1 im April 1998. Mittlerweile ist es bei Version 0.5.11 (stabil) und 0.6.2 (Entwicklungsversion) angelangt, die im Internet unter[1] zum Download bereitstehen.

Wysiwyg fürs Interface

Seit der Entstehung von GTK+ wird nach Wegen gesucht, um das Interface einer Applikation nach dem Wysiwyg-Paradigma zu gestalten. Mehrere Open-Source-Projekte befassen sich mit diesem Thema, etwa Epingle[2], GLE[3], GRAD[4] und VDKBuilder[5]. Jedes hat besondere Eigenschaften und ist mehr oder minder ausgereift.

Glades besondere Attraktivität besteht darin, Code in verschiedenen Programmiersprachen zu erstellen. Standardmäßig generiert dieser GUI-Builder C-Code; er kann jedoch auch C++ (bei Verwendung von Glade–), Ada 95, Eiffel, Perl und Python erzeugen, wenn man zusätzliche Software (GtkAda, eGlade, Glade-Perl und PyGtk) installiert.

Diese Software liest die XML-Datei, in der Glade die User-Interface-Beschreibung ablegt, und erzeugt daraus den Code in der entsprechenden Programmiersprache. Sie wird aber dank perfekter Integration direkt von Glade aufgerufen, so dass der Benutzer gar nicht bemerkt, dass es sich um externe Programme handelt.

Signale und Container

Da Glade auf GTK+ basiert, muss man sich in erster Linie mit zwei GTK-Konzepten vertraut machen, um den GUI-Builder effizient zu benutzen. Dabei handelt es sich um die beiden Themen “Signale und Ereignisbehandlung” und “Container”.

Um ein Programm zu befähigen, auf vom Benutzer hervorgerufene Ereignisse (etwa Bewegen des Mauszeigers, Drücken eines Mausknopfs, Tippen von Text, Schließen eines Fensters) zu reagieren, verwendet man in der GUI-Programmierung Signale. Je nach Typ des Widgets und dessen übergeordneter Klasse wird ein unverwechselbares Signal gesendet, das sich von einem Signal-Handler (bei GTK einer Callback-Funktion) regelrecht aufnehmen lässt, so dass eine bestimmte Aufgabe erfüllt wird. Es gibt Signale, die alle Widgets erben (zum Beispiel das Signal »destroy«), und andere, die Widget-spezifisch sind (etwa das Signal »toggled« von den Ein-/Aus-Schaltern). Die Idee der Signale stammt übrigens aus dem Hause des Qt-GUI-Toolkits[9].

Anders als die meisten GUI-Toolkits benutzt GTK+ eine Methode zur Positionierung der Widgets, die anfangs verwirrend zu sein scheint, aber sehr flexibel ist: das Verpacken einzelner Widgets in Containern. Bei diesem Verfahren informiert man GTK+, an welcher Position ein Widget erscheinen soll. Die wichtigsten Container sind Boxen und Tabellen. Mit ihnen bereitet man Zellen vor, in denen andere Widgets platziert werden. Dadurch lassen sich Widgets visuell ganz einfach verschachteln, was die Gestaltung komplexer Layouts mit Glade ermöglicht.

Mit diesem Wissen versehen startet man den GUI-Builder mit dem Kommando »glade &«. Daraufhin erscheinen drei neue Fenster: das Projekt-, das Paletten- und das Eigen-schaftsfenster (siehe Abbildung 1).

Abbildung 1: Glade - eine RAD-Umgebung stellt sich vor.

Abbildung 1: Glade – eine RAD-Umgebung stellt sich vor.

Fenster und Files

Das erste fungiert als Steuerkonsole. Hier kann man das aktuelle Projekt sichern oder ein vor-handenes laden, aber auch die beiden anderen Werk- zeug-Fenster öffnen oder Projektoptionen ändern. Vom GUI-Designer erstellte Hauptcon- tainer, etwa Fenster oder Dialoge für die Farb-, Datei- oder Schriftauswahl, sind in Listenform unter der Knopfleiste anzeigbar. Klickt man deren Elemente zweimal an, öffnet sich das entsprechende Dialogfenster und lässt sich bearbeiten.

Aus dem Reiter »C Optionen« im Menüpunkt »Datei | Projekt Optionen« (Abbildung 2) ist ersichtlich, dass Glade vier Code-Dateien ablegt:

  • In »main.c« steht der Aufruf des Hauptfensters der Applikation.
  • »interface.c« enthält die Implementation des GUIs.
  • »callbacks.c« ist ein Skelett mit Definitionen aller Funktionen, die von Signalen gestartet und beim Anlegen des User-Interface unter Verwendung des Eigenschaftsfensters angelegt wurden. Den Inhalt dieser Funktionen muss man selbst schreiben.
  • »support.c« enthält ein paar Funktionen, die beispielsweise die Verbindung zwischen den vom Anwender geschriebenen Callback-Funktionen und den Objekten und Variablen der Interface-Datei vereinfachen.

Die Namen dieser vier Dateien können nach Gusto geändert werden, ihre Funktionalität bleibt dabei unberührt. Andere Quell- und Header-Dateien oder Bibliotheken werden (allerdings per Hand) in die Datei »Makefile.am« an den entsprechenden Stellen eingefügt.

Das Palettenfenster zeigt die Widgets, aus denen das Benutzer-Interface aufgebaut wird, aufgeteilt in zwei Kategorien: »GTK+ Basic« und »GTK+ Additional«. Falls Gnome-Unterstützung verfügbar ist, gibt es noch eine dritte: »Gnome«. Klickt man das Window-Icon in der oberen linken Ecke des Palettenfensters an, zeichnet Glade ein neues Fenster, in das man andere Widgets von der Palette hinzufügen kann.

Das Eigenschaftsfenster (Abbildung 3) ist ein kontextsensitiver Dialog, der jeweils den Namen und die aktuellen Eigenschaften des angeklickten Widgets anzeigt. Unter der Rubrik »Signale« können Handler-Funktionen an die verschiedenen Ereignissen des aktuellen Widgets gebunden werden.

Es gibt zwei Ansätze, mit Glade erzeugte GUIs in Programme einzubauen: Beim klassischen konstruiert man mit Glade das Interface, schreibt die Callback-Funktionen und kompiliert alles zusammen. Alternativ lässt sich das Interface mit »libglade« auch erst zur Laufzeit aus der GUI-Beschreibung bauen.

Beide Varianten haben Vor- und Nachteile: Verwendet man die »libglade«-Methode, braucht man das ausführbare Programm nicht neu übersetzen zu lassen, wenn sich das GUI ändert. Das Programm baut es on the fly, indem es über »libglade« die von Glade gespeicherte XML-Datei liest und die entsprechenden Widgets lokalisiert.

Dreimal “muss” zeigt die Nachteile des Verfahrens: Das neu generierte Interface muss alle Variablen enthalten, auf die das Programm zugreift, die XML-Datei mit der Interface-Beschreibung muss immer erreichbar sein, zudem muss die »libglade« auf dem ausführenden Rechner vorhanden sein, was die Portabilität des Programms (zurzeit) reduziert.

Bei der klassischen Methode dagegen gilt es, das Binary bei jeder Interface-Änderung neu zu kompilieren. Andererseits kommt es ohne zusätzliche Ressourcendateien (mit Ausnahme der üblichen Bibliotheken) aus.

 

Glade für alle

Glade liegt sowohl als Quelltext als auch als Binärdatei in unterschiedlichen Formaten vor: als Red-Hat-RPM (Source oder Binary), Debian-DEB-, Slackware- oder NetBSD-Paket und auch als Quelltext-Tar-Datei, die übrigens über eine kleine FAQ und einiges an Dokumentation verfügt (nach dem Auspacken des Tar-Balls im Verzeichnis »glade-0.5.11/doc/« zu finden). Besonders gepflegt ist der Baum von Kompilaten für die NetBSD-Plattform.

Um Glade erfolgreich zu übersetzen, benötigt man GTK+ in der Version 1.2.0 (oder höher). Diese Bibliothek besteht aus einer Sammlung von Objekten (so genannten Widgets), etwa Fenster, Knöpfe, Menüs. GTK+ seinerseits braucht die Glib-Bibliothek, die Funktionalität wie verkettete Listen, Bäume, Routinen zur Fehlerbehandlung und dynamische Speicher-Allokation bereitstellt und aus diesem Grunde zuerst installiert werden muss. Beide Bibliotheken sind bereits in allen aktuellen Linux-Distributionen vorhanden und ansonsten im Internet unter[6] zu finden.

Die Installation von Glade aus einer Tar-Datei erfolgt mit den üblichen Befehlen: »./configure« (beziehungsweise »./configure –prefix= Applikationsverzeichnis«), »make« und »make install«.

Windows-Benutzer stehen auch nicht im Regen: Sowohl Glade als auch die notwendigen GTK+-Bibliotheken stehen für Redmonder Betriebssysteme zum Download unter[7] beziehungsweise[8] bereit.

Voraussetzung für die automatische Erstellung von C-Code ist die Präsenz eines lauffähigen »autoconf« in der Version 2.13 und »automake« in der Version 1.4. Falls dazu noch Gnome-Support erwünscht ist, ist eine aktuelle Version (neuer als 1.0.50) der Gnome-Bibliotheken ein Muss.

Damit hat Glade weitere Widgets zur Verfügung. Doch schafft diese Erweiterung zurzeit Hindernisse bei der Portabilität des Programms auf andere Plattformen, da nur GTK+-Portierungen für fast alle gängigen Betriebssysteme vorliegen, nicht aber Gnome selbst.

Abbildung 2: Von Glade geschriebene Quell-Dateien.

Abbildung 2: Von Glade geschriebene Quell-Dateien.

Experimentierarbeit

Der beste Weg Glade kennenzulernen ist das Experimentieren, beispielsweise anhand eines einfachen Telefon- und Adressbuchs, das unter[10] gut kommentiert zum Download bereitsteht (Abbildung 7). Es verwendet die klassische Methodik ohne »libglade«. Unter[11] ist eine ähnliche Anwendung zu finden, die sowohl »libglade« benutzt als auch mit Gnome-Support gebaut wurde. Nähere Informationen zu dieser Vorgehensweise bietet auch[12].

Abbildung 7: Ein mit Glade erstelltes Telefonbuch

Abbildung 7: Ein mit Glade erstelltes Telefonbuch

Man beginnt mit der Erstellung eines Hauptfensters, auf dem andere Widgets angebracht werden, in unserem Fall eine Menü- und eine Knopfleiste »menubar« (Klasse »GtkMenuBar«) respektive »toolbar« (Klasse »GtkToolbar«), eine Spaltenliste »clist« mit drei Spalten (Klasse »GtkClist«), eine Statuszeile »statusbar« (Klasse »GtkStatusbar«) sowie ein Eingabefeld »clist_cell_entry« (Klasse »GtkEntry«). Dazu braucht man zuerst eine vertikale Box »vbox1« (Klasse »GtkVBox«) mit vier Unterteilungen: zwei für die verschiedenen Leisten, die dritte für die Spaltenliste und die vierte für die Statuszeile und das Eingabefeld.

Um die beiden letztgenannten Widgets zusammen in der vierten vertikalen Box-Unterteilung unterzubringen, richtet man zuerst eine horizontale Box »hbox1« (Klasse »GtkHbox«) mit zwei Unterteilungen ein, in denen je ein Widget platziert wird. Die Attribute der neuen Widgets können im Eigenschaftsfenster im Detail bearbeitet werden. Diese Prozedur wiederholt sich im Laufe des gesamten Interface-Entwurfs, wie aus Abbildung 4 zu ersehen ist.

Um die Übersicht über die gesamte Widget-Struktur zu behalten, greift man auf den Widget-Baum (Abbildung 5) zurück, über den alle dargestellten Widgets auch erreichbar sind: Klickt man auf eines der GUI-Elemente, zeigt das Eigenschaftsfenster sofort seine Attribute. Das ist besonders hilfreich bei Projekten mit Hunderten Fenstern und Dialogen.

Abbildung 3: Ereignisse und deren Handler-Funktionen werden über den Eigenschaften-Dialog verknüpft.

Abbildung 3: Ereignisse und deren Handler-Funktionen werden über den Eigenschaften-Dialog verknüpft.

Abbildung 4: Stein auf Stein - wie ein Lego-Spiel nimmt die Applikation Gestalt an.

Abbildung 4: Stein auf Stein – wie ein Lego-Spiel nimmt die Applikation Gestalt an.

Heiratsantrag

Der Name und die Attribute der Widgets lassen sich im Eigenschaftsfenster während der gesamten Designarbeit im Detail bearbeiten und modifizieren. Insbesondere dem Inhalt der Rubrik »Signale« schenke man an dieser Stelle Beachtung: Das Anlegen eines passenden Signals und dessen Handlers erfolgt hier. Für jedes verbundene Signal wird automatisch beim Drücken des Knopfes zum Code-Generieren der entsprechende Aufruf einer Handler-Funktion erstellt und in die Datei »callbacks.c« geschrieben.

Der Bearbeitung einer Menüleiste ist ein spezieller Menü-Editor gewidmet. Er besitzt mehr oder weniger die Funktionen des Eigenschaftsfensters, ist aber gesondert für Menüs ausgelegt, wie in Abbildung 6 zu sehen ist.

Wie bei jedem GUI-Builder stellt sich irgendwann die Frage nach der Verknüpfung von automatisch generiertem und vom Programmierer geschriebenem Code. Bei Glade lässt sich diese Problematik folgendermaßen lösen:

  • Verbindung der Signale mit den Callback-Funktionen in der Datei »callbacks.c«. Ein Blick auf den Code verrät, wie’s gemacht wird: Außer dem reinen Interface-Code enthalten die in der Datei »interface.c« angelegten Funktionen auch die Verknüpfungen zwischen den Handler-Funktionen aus der Datei »callbacks.c« und den Signalen, die von einem Ereignis im entsprechenden Fenster ausgelöst werden. Das alles erfolgt automatisch, man muss nur darauf achten, dass die Signale bei der Erstellung des Interface richtig (im Eigenschaftsfenster unter der Rubrik »Signale« (Abbildung 3) eingetragen werden.
  • Aufrufe von Funktionen der Interface-Datei (normalerweise Funktionen zum Öffnen eines Fensters beziehungsweise Dialogs): Die Prozedur in diesem Fall ist immer jene, die Listing 1 zeigt. Es demonstriert, wie die von Glade automatisch in »interface.c« angelegte Funktion »create_about_dialog()« innerhalb von »on_about_activate()« in der Datei »callbacks.c« aufgerufen wird.
  • Zugriff auf Widgets in der Datei »interface.c«. Hier gibt es mehrere Möglichkeiten: Eine Handler-Funktion bekommt als erstes Argument immer einen Zeiger auf jenes Widget geliefert, das das Signal für deren Aufruf auslöst. Im Falle eines Handlers zum Selektieren der Zeile einer Spaltenliste (Listing 2) sieht man, dass der Zeiger »*clist« die Adresse der im Programm angeklickten Spaltenliste erhält, um den Text der ausgewählten Zelle in der Variablen »text« zu speichern.

Listing 3 zeigt eine andere Variante: Falls das Widget der oberste Container des Interface ist, wird es mit der Funktion »gtk_widget_get_toplevel()« gewonnen. So übergibt die Funktion »on_delete_data_button_clicked()« einen Zeiger zum Hauptfenster, in dem sich das Widget »button« befindet, an die Funktion »delete_data()«.

Will man aus einer Callback-Funktion hingegen auf ein beliebiges Widget aus dem entsprechenden Interface-Code zugreifen, gibt es zwei Varianten: Listing 4 zeigt den Weg über die in »support.c« definierte Funktion »lookup_widget()«, die jedoch in Zukunft nicht mehr zur Verfügung stehen wird. Ihr übergibt man als erstes Argument den Zeiger auf ein bereits vorhandenes Widget. Als zweites Argument dient der Name des zu gewinnenden Widgets.

Bei der zweiten – und künftig bevorzugten – Möglichkeit gibt man das gewünschte Widget als »user_data« weiter. Dabei wird der Name des Ziel-Widgets explizit im Feld »Daten« des Eigenschaftsfensters (Abbildung 3) eingegeben. Die eine Zeile innerhalb der Funktion »on_add_new_data_clicked()« in Listing 5 erfüllt die Aufgabe der Funktion »lookup_widget()« aus Listing 4 und übergibt zugleich die in Abbildung 3 festgelegte Variable »telefonbuch« (Hauptfenster,Klasse »GtkWidget«) an die Funktion »add_data()«. Kurz: Ein Zeiger zum Hauptfenster in »user_data« wird an die Funktion »add_data()« gegeben.

Es verbleibt noch die Möglichkeit, von einer Callback-Funktion aus auf lokale Widgets einer Interface-Funktion zuzugreifen, aber das erfordert GTK-Kenntnisse, die den Rahmen dieses Artikels sprengen würden. Allerdings enthält die Beispiel-Applikation entsprechenden, mit Kommentaren versehenen Code.

Sobald das Interface erzeugt und der Körper der Callback-Funktionen gefüllt wurde, steht die Kompilierung des Codes an. Dafür ruft man

./autogen.sh  [--prefix=Appverzeichnis]

auf, ein Shell-Skript, das die für Glade benötigten Systemressourcen überprüft und das passende »Makefile« erstellt. Wie üblich stößt »make« nun den Übersetzungsvorgang an. Das Binary wird auf der Karteikarte »Allgemein« im Menü »Datei | Projekt Optionen« festgelegten Verzeichnis (im Beispiel »~/Telefonbuch/src/«) hinterlegt.

»make install« kopiert es ins Verzeichnis, das mit der Option »–prefix« angegeben wurde. Dieser Schritt ist während der Entwicklungsphase des Programms jedoch nicht zu empfehlen.

Wer Blut geleckt hat, kann nun anhand der Beispielapplikation erste Erfahrungen mit Glade sammeln: Neben dem Hauptfenster besteht sie aus fünf weiteren Dialogen, die den Eintrag von Daten und das Laden oder Speichern eines Telefonbuchs ermöglichen, aber auch mit Fehlerbehandlungsmechanismen ausgestattet sind. Das Speicherformat des Telefonbuchs ist übrigens identisch mit dem des »pine«-Adressbuchs.

Abbildung 5: Alles im Überblick und greifbar - der Widget-Baum.

Abbildung 5: Alles im Überblick und greifbar – der Widget-Baum.

Abbildung 6: Mit dem Menü-Editor erstellt man schnell eine Menüleiste mit unterschiedlichen Eintragsvarianten und Tastaturkürzeln.

Abbildung 6: Mit dem Menü-Editor erstellt man schnell eine Menüleiste mit unterschiedlichen Eintragsvarianten und Tastaturkürzeln.

 

Listing 1: Aufrufe von Funktionen der Interface-Datei

Ein Auszug aus »callbacks.c«:

...
void on_about_activate (GtkMenuItem *menuitem,
                        gpointer user_data)
{
  GtkWidget *about_dialog;
  ...
  about_dialog = create_about_dialog ();
  gtk_widget_show (about_dialog);
}
...

Ein Auszug aus »interface.c«:

...
GtkWidget* create_about_dialog (void)
{
  /* Code von Glade automatisch generiert.*/
}
...

 

Listing 2: Zugriff auf Widgets aus dem GUI, 1. Variante

void on_clist_select_row (GtkCList *clist,
                          gint row,
                          gint column,
                          GdkEvent *event,
                          gpointer user_data)
{
  gchar *text;
  ...
  gtk_clist_get_text(GTK_CLIST(clist),
                     row,column,&text);
  ...
}

 

Listing 3: Zugriff auf Widgets aus dem GUI, 2. Variante

void on_delete_data_button_clicked (GtkButton *button,
                                    gpointer user_data)
{
  delete_data(gtk_widget_get_toplevel(GTK_WIDGET(button)));
}

 

Listing 4: Zugriff auf Widgets aus dem GUI, 3. Variante

void on_new_file_button_clicked (GtkButton *button,
                                 gpointer  user_data)
{
  GtkWidget *main_window;
  main_window = lookup_widget (GTK_WIDGET (button),
                               "telefonbuch");
  new_file(main_window);
}

 

Listing 5: Zugriff auf Widgets aus dem GUI, 4. Variante

void on_add_new_data_clicked (GtkButton *button,
                              gpointer user_data)
{
  add_data((GtkWidget *) user_data);
}

Fazit

Die Open-Source-Bewegung hat mit Glade (und anderen GUI-Buildern) ein wichtiges Ziel erreicht: den Entwurf von User-Interfaces durch visuelle Programmierung einfacher zu machen. Das spart dem Programmierer einige Mühsal beim Aufbau einer adäquaten User-Oberfläche, so dass er sich vornehmlich auf die Entwicklung des Codes seiner Applikation konzentrieren kann.

Er muss dabei auch nicht auf die Flexibilität der eigenen Interface-Programmierung verzichten: Wer mit dem Ergebnis von Glade unzufrieden ist, richtet visuell eine Box für das entsprechende Widget ein und verknüpft den selbst geschriebenen Interface-Code damit. Das ist besonders willkommen im Falle von nicht im Palettenfenster aufgeführten Widgets: Da GTK+ erweiterbar ist, kann jeder mit etwas Erfahrung eigene Widgets bauen. Beispiele sind Bibliotheken wie GtkEditor[13], GtkExtra[14] und ZVT Terminal Widget[15].

Wo viel Licht ist, ist aber auch wenigstens etwas Schatten: So bringt Glade zur Zeit keinen eigenen Quelltext-Editor mit. Das wird sich aber wahrscheinlich ändern: Die Integration in gIDE – das Integrated Development Environment von GTK+/Gnome[16] – ist bereits geplant.

Ein anderes Manko ist, dass beim Löschen von oder bei Änderungen an einem Signal-Handler die entsprechende Callback-Funktion in der Datei »callbacks.c« nicht gelöscht beziehungsweise neu geschrieben wird, was zu Compiler-Fehlern führen kann. Bis zur Version 1.0 darf man also noch auf einiges gespannt sein. (pju)

 

Infos

[1] [http://glade.gnome.org/]

[2] [http://www.epita.fr/~theber_s/epingle/epingle.html]

[3] [http://www.gtk.org/~timj/]

[4] [http://www.penguin.cz/~grad/]

[5] [http://www.guest.net/homepages/mmotta/VDKHome/vdkbuilder.html]

[6] [ftp://ftp.gtk.org/pub/gtk/v1.2/] und [http://www.gtk.org/]

[7] [http://members.aol.com/suboner/code/Gtk/glade.html]

[8] [http://members.aol.com/suboner/code/Gtk/index.html]

[9] [http://www.troll.no/]

[10] [https://www.linux-magazin.de/pub/listings/magazin/2001/12/glade/]

[11] [http://www-4.ibm.com/software/developer/library/gnome-glade/]

[12] Thorsten Fischer: “Schnell und oberflächlich”, Linux-Magazin 9/00, [https://www.linux-magazin.de/ausgabe/2000/09/Gnome/gnome.html]

[13] [http://gtkeditor.sourceforge.net/]

[14] [http://gtkextra.sourceforge.net/ ]

[15] [http://developer.gnome.org/doc/API/zvtterm/book1.html]

[16] [http://gide.pn.org/]

LINUX-MAGAZIN KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS Readly Logo
E-Mail Benachrichtigung
Benachrichtige mich zu:
0 Kommentare
Älteste
Neuste Beste Bewertung
Inline Feedbacks
Alle Kommentare anzeigen
Nach oben