14.13 Ein einfaches Fenster mit einem Button
Um die Xt-Funktionen zu verwenden, sind immer mindestens folgende zwei Headerdateien nötig:
/* Toolkit-Strukturen */
#include <X11/Intrinsic.h>
/* Toolkit-Konstanten, z. B. fuer Ressourcen */
#include <X11/StringDefs.h>
Des Weiteren sind bestimmte Header für die verwendete Widget-Klasse nötig. Im ersten Beispiel werden zwei Buttons verwendet, die in der folgenden Headerdatei definiert sind:
/* Command-Widget des Athena-Sets */
#include <X11/Xaw/Command. h.>
Im ersten Schritt, bevor Sie überhaupt ein Widget verwenden können, müssen Sie erst das Toolkit initialisieren. Initialisieren können Sie ein Toolkit mit der Funktion XtVaAppInitialize(), wodurch eine komplexe Datenstruktur mit dem Namen XtAppContext, die für weitere Aktionen später benötigt wird, initialisiert wird. Die Funktion XtVaAppInitialize() liefert eine Variable vom Typ Widget zurück und stellt gleichzeitig auch das ranghöchste Widget in der Widget-Hierarchie dar – das Root- bzw. Eltern-Widget aller noch im Programm folgenden Widgets. Es gibt noch andere Funktionen zum Initialisieren eines Toolkits, die sich nur geringfügig von der Anzahl der Parameter unterscheiden. So besteht z. B. zwischen XtAppInitialize() und XtVaAppInitialize() nur der Unterschied, dass die zweite Version (..Va..) eine variable Argumentenliste zulässt.
Hinweis Es gibt bei vielen Xt-Funktionen eine zweite Version, die eine variable Argumentenliste verwendet.
|
Eine Anwendung der Funktion XtVaAppInitialize() könnte wie folgt aussehen:
int main (int argc, char **argv) {
XtAppContext contex; /* Wird bei Init des Toolkits belegt */
Widget w_root; /* Variable fuer Root-Widget */
...
w_root = XtVaAppInitialize(
&app_context, /* Kontext der Applikation */
NULL , /* Applikationsklasse */
NULL, 0, /* Kommandozeilenoptionsliste */
&argc, argv, /* Kommandozeilenargumente */
NULL, /* fallback_ressources */
NULL); /* Terminierte varargs Liste */
In den folgenden Beispielen werden Sie diese Funktion auch nicht mit den anderen Parametern verwenden – daher werden die meisten Werte mit NULL bzw. 0 belegt. Bis auf die Bedeutung des zweiten Parameters wird darauf nicht näher eingegangen.
Wenn Sie das Toolkit initialisiert haben, können Sie ein Widget einer bestimmten Klasse erzeugen. Dazu verwenden Sie praktisch eine Instanz. Zum Erzeugen eines Widgets wird auch hierbei wieder auf eine Funktion mit der ..Va..-Version zurückgegriffen – XtVaAppInitialize(). Als Beispiel wird hierbei ein Button der Klasse commandWidgetClass erzeugt, die in der Headerdatei <X11/Xaw/Command. h.> definiert ist.
quit = XtVaCreateManagedWidget (
"quit", commandWidgetClass,
w_root,
/* Eigenschaften des Buttons ... */
XtNheight, (XtArgVal) 20, /* Höhe */
XtNwidth, (XtArgVal) 50, /* Breite */
/* Hier könnten noch mehr Eigenschaften stehen ... */
NULL );
Damit erzeugen Sie ein Widget mit dem selbst definierten Widget-Namen quit und der Widget-Klasse commandWidgetClass. Die Eltern-Klasse des Buttons ist hier w_root, die Sie zuvor mit der Funktion XtVaAppInitialize() erzeugt haben. Anschließend können Sie hier die Eigenschaften des Buttons wie Farbe, Größe, Position, Abstand usw. als variable Argumentenliste angeben, die mit NULL terminiert werden muss. Die Elemente der variablen Argumentenliste müssen immer paarweise auftreten. Der erste der beiden Parameter muss ein Bezeichner der Form sein, der festlegt, was das zweite Argument bedeutet. Bei der Verwendung der variablen Argumentenliste spricht man von Hardcoding.
14.13.1 Ressourcen
Außer der Verwendung von Hardcoding können nämlich auch Ressourcendateien verwendet werden. Ressourcen sind wiederum nichts anderes als modifizierbare Eigenschaften von Widgets wie z. B. Farbe, Schriftart und -größe, Größe des Widgets usw. Der Hersteller eines Widget-Sets legt die Ressourcen fest. D. h., er definiert für jede Widget-Klasse, welche Eigenschaften Sie als Anwendungsprogrammierer überhaupt modifizieren können – so entsteht auch das typischen Look & Feel der verschiedenen Desktops wie im Vergleich von KDE zu GNOME. Da Sie hierbei einen hierarchischen Aufbau des Widget-Sets haben, erben alle Abkommen einer Klasse die Ressourcen der Vorfahren – daher sehen auch alle Ihre Programme erst dem Window-Manager ähnlich. Wenn Sie jetzt wissen wollen, welche Ressourcen für welche Widget-Klasse zur Verfügung stehen, können Sie das Tool listres verwenden. Anwenden können Sie listres mit der Angabe der Widget-Klasse als zweitem Parameter. Im Beispiel des Buttons (command) wäre dies:
$ listres command
WidgetClass Instance Class Type
----------- -------- ----- ----
command: Core\Simple\Label\Command
Core accelerators Accelerators AcceleratorTable
Core ancestorSensitive Sensitive Boolean
Core background Background Pixel
Core backgroundPixmap Pixmap Pixmap
Label bitmap Pixmap Bitmap
Core borderColor BorderColor Pixel
Core borderPixmap Pixmap Pixmap
...
Zu Beginn erkennen Sie gleich die Hierarchie: Core\Simple\Label\Command. Bei der Ressourcendatei handelt es sich schlicht um eine einfache ASCII-Textdatei, die während der Ausführung der X-Anwendung verändert werden kann. Die Veränderungen werden umgehend aktiv, ohne die Anwendung neu zu übersetzen. Den Namen der Ressourcendatei müssen Sie bei der Initialisierung mit der Funktion XtVaAppInitialize() als zweiten Parameter mit angeben, z. B.:
w_root = XtVaAppInitialize(
&app_context, /* Kontext der Applikation */
"MEine_Ressource" /* Applikationsklasse */
NULL, 0, /* Kommandozeilenoptionsliste */
&argc, argv, /* Kommandozeilenargumente */
NULL, /* fallback_ressources */
NULL); /* Terminierte varargs Liste */
Um die anwendungsspezifische Ressourcendatei zu verwenden, müssen Sie den Namen der Datei als zweiten Parameter angeben. Üblicherweise schreibt man hierbei die ersten beiden Buchstaben groß.
Wollen Sie z. B., dass alle Instanzen des Command-Widgets die Breite 100 haben, müssen Sie Folgendes in die Ressourcendatei schreiben:
*Command*width: 100 !1. Buchstabe gross!
Links steht der Klassenname, dann kommt der Ressourcenname und schließlich der Wert. Wollen Sie hingegen nur, dass die Instanz mit dem Namen 'hallo' den Wert bekommt, dann müssen Sie dies wie folgt in die Ressourcendatei eintragen:
*hallo.width: 150
Links steht der Instanzenname, dann kommen wieder der Ressourcenname und der Wert.
Es gibt hierbei allerdings keinerlei Syntax-Checks! Wenn also etwas nicht wie gewünscht funktioniert, hat man hier meistens etwas falsch gemacht. Z. B. muss man auf * und : achten und darf am Zeilenende keine Blanks mehr haben. Kommentarzeilen werden in der Ressourcendatei mit einem ! eingeleitet. Ob vor dem Ressourcennamen ein * oder ein . steht, ist nur von sekundärer Bedeutung. Ich empfehle Ihnen, die Ressourcendateien im Verzeichnis /usr/lib/X11/app-defaults zu einigen X-Anwendungen anzusehen.
Wollen Sie die Ressourcen, die Ihnen das Tool listres zurückliefert, im Hardcoding-Stil verwenden, ist das auch kein Problem; den Schreibstil haben Sie ja bereits gesehen:
quit = XtVaCreateManagedWidget (
...
XtNheight, (XtArgVal) 20,
XtNwidth, (XtArgVal) 50,
NULL);
Folgende Bedeutung hat dabei die Zeile XtNheight, (XtArgVal) 20:
|
XtN: Abkürzung für XtressourceName |
|
height: Name der Ressource, die Sie auch von dem Tool listres angezeigt bekommen (Spalte: Instance) |
|
20: Wert der Ressource vom geeigneten Typ |
|
Der Cast-Operator XtArgVal wird empfohlen. |
Hinweis In den noch kommenden Beispielen werden nur der Hardcoding-Stil und wenige Ressourcen verwendet. Sie können aber gerne mithilfe der Ressourcendsateien in /usr/lib/X11/app-defaults an den eigenen Programmen experimentieren.
|
Zurück zur Funktionsbeschreibung XtVaCreateManagedWidget(): Als Rückgabewert der Funktion XtVaCreateManagedWidget() erhalten Sie auch hier einen Wert vom Typ Widget, der in der Variablen quit abgespeichert wurde.
Gewöhnlich werden Sie wollen, dass bei Betätigung eines Widgets etwas passiert – im Falle des Drückens eines Mausbuttons soll eine Aktion aufgerufen werden. Um eine bestimmte Funktion aufzurufen, können Sie XtAddCallback() verwenden.
XtAddCallback (quit, XtNcallback, (XtCallbackProc)quitCB, NULL);
Der erste Parameter ist der Name des Widgets, zu dem die Callback-Funktion gehören soll. Mit dem dritten Parameter geben Sie die Callback-Funktion an, die aufgerufen werden soll, wenn der Button mit der linken Maustaste betätigt wurde. In diesem Fall ist es die Funktion quitCB, die wie folgt aussehen könnte:
/* Callback-Funktion für den Button 'quit' */
static void
quitCB(Widget button, XtPointer client_data, XtPointer call_data) {
printf ("Button 'quit' gedrückt\n");
exit (EXIT_SUCCESS);
}
Wollen Sie der Callback-Funktion Daten übergeben, können Sie dies mit dem letzten Parameter der Funktion XtAddCallback() machen.
Jede Funktion, die durch XtAddCallback() eingefügt wird, wird in der Regel immer mit drei Parametern von den Toolkit-Funktionen aufgerufen. Dies zu wissen ist besonders wichtig bei der Definition der Funktion:
/* Callback-Funktion für den Button 'quit' */
static void
quitCB(Widget button,XtPointer client_data,XtPointer call_data);
Um jetzt die Widgets auf dem Bildschirm anzuzeigen, müssen Sie, wie schon bei der Xlib, mit XMapRaised() die Funktion XtRealizeWidget(w_root) aufrufen. Weil Sie das in der Hierarchie am höchsten platzierte Widget aufrufen, werden damit alle zugeordneten Unter-Widgets ebenso am Bildschirm mit ausgegeben. Allmählich erkennt man die Stärke der hierarchischen Anordnung der Widgets.
Jetzt fehlt Ihnen nur noch die Event-Bearbeitungsschleife, was Ihnen im Gegensatz zur Xlib mit nur einem Befehl abgenommen wird:
XtAppMainLoop(context);
Nach diesem Befehl kann allerdings nichts mehr X-Spezifisches folgen und sollte somit auch der letzte X-Befehl in der Anwendung sein.
Das Grundgerüst einer Anwendung mit dem Xt-Toolkit sei hier nochmals kurz zusammengefasst:
1. Xt-Toolkit initialisieren – XtVaAppInitialize()
2. Ein Widget erzeugen – XtVaCreateManagedWidget()
3. (Callback-)Funktion(en) zum Widget hinzufügen (ist nicht immer nötig)
4. Widget(s) am Bildschirm darstellen – XtRealizeWidget()
5. Event-Bearbeitungsschleife setzen – XtAppMainLoop()
Das folgende Listing zeigt Ihnen die hier vorgestellten Funktionen im Einsatz. Es wird ein Fenster mit zwei Buttons erzeugt. Ein Button gibt in der Konsole eine Textfolge mit einem übergebenen Parameter aus, und bei Betätigung des anderen Buttons 'quit' wird das Programm beendet. Es wurde außerdem die Klasse formWidgetClass, die in der Headerdatei <X11/Xaw/Form.h> definiert ist, verwendet, um überhaupt eine Arbeitsfläche für zwei Buttons bzw. Widgets im Allgemeinen zu erstellen. Als alte Bekannte (aus der Xlib-Abteilung) geben Sie diesem Widget eine Callback-Funktion, womit Sie einige Maus-Events im Fenster abfragen und auf der Konsole ausgeben.
/* toolkit1.c */
#include <stdio.h>
#include <stdlib.h>
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/Shell.h>
#include <X11/Xaw/Form.h>
#include <X11/Xaw/Command. h.>
#define MAUS_LINKS 1
#define MAUS_RECHTS 3
#define MAUS_MITTE 2
static void quitCB (Widget, XtPointer, XtPointer);
static void halloCB (Widget, XtPointer, XtPointer);
static void quit_proc (Widget, caddr_t, XEvent *);
/* Callback-Funktion für den Button 'quit' */
static void
quitCB(Widget button,XtPointer client_data,XtPointer call_data) {
printf ("Button 'quit' gedrückt\n");
exit (EXIT_SUCCESS);
}
/* Callback-Funktion für den Button 'hallo' */
static void
halloCB(Widget button,XtPointer client_data,XtPointer call_data){
printf ("Button 'hallo' gedrückt mit der Nachricht '%s'\n",
(char *)client_data);
}
/* Eventhandler für die Anwendung */
static void
quit_proc (Widget form, caddr_t client_data, XEvent * event) {
switch (event->type) {
case ButtonPress:
switch (event->xbutton.button) {
case MAUS_RECHTS:
printf ("Rechte Maustaste\n");
break;
case MAUS_LINKS:
printf ("Linke Maustaste\n");
break;
case MAUS_MITTE:
printf ("Mittlere Maustaste\n");
break;
default:
break;
}
break;
default:
break;
}
}
int main (int argc, char **argv) {
XtAppContext ac;
Widget toplevel, form, quit, hallo;
/* Fenster erzeugen*/
toplevel = XtVaAppInitialize (
&ac, NULL, NULL, 0, &argc, argv, NULL, NULL);
/* Form-Widget erzeugen */
form = XtVaCreateManagedWidget (
"form", formWidgetClass,
toplevel,
XtNbackground, (XtArgVal) "red",
NULL );
XtAddEventHandler ( form, ButtonPressMask, False,
(XtEventHandler) quit_proc, NULL);
/* quit-Button erzeugen */
quit = XtVaCreateManagedWidget (
"quit", commandWidgetClass,
form,
/* Eigenschaften des Buttons ... */
XtNheight, (XtArgVal) 20,
XtNwidth, (XtArgVal) 50, NULL);
/* Callback-Funktion für quit-Button */
XtAddCallback (
quit, XtNcallback, (XtCallbackProc)quitCB, NULL);
/* hallo-Button erzeugen */
hallo = XtVaCreateManagedWidget (
"hallo", commandWidgetClass,
form,
/* Eigenschaften des Buttons ... */
XtNhorizDistance, (XtArgVal) 50,
XtNfromHoriz, (XtArgVal) quit,
XtNheight, (XtArgVal) 20,
XtNwidth, (XtArgVal) 50, NULL);
/* Callback-Funktion für hallo-Button */
XtAddCallback ( hallo, XtNcallback,
(XtCallbackProc)halloCB, "Eine Nachricht");
/* Widget toplevel mitsamt Unter-Widgets anzeigen */
XtRealizeWidget (toplevel);
/* Event-Bearbeitungsschleife einrichten */
XtAppMainLoop (ac);
return EXIT_SUCCESS;
}
Das Programm bei der Ausführung:
$ gcc -o toolkit1 toolkit1.c -L/usr/X11R6/lib -lX11 -lXaw -lXt
$ ./toolkit1
Button 'hallo' gedrückt mit der Nachricht 'Eine Nachricht'
Button 'quit' gedrückt
|