15.13 Events
Die Einführung in GTK+ ist fast zu Ende, und es wurde kaum ein Wort über Events und deren Bearbeitung erwähnt. Sie haben bisher immer Ihre GTK+-Anwendungen erstellt und sich beim Einrichten der Callback-Funktion das ausgesucht, was Sie benötigt haben (clicked, activate ...), das war’s. Mehr ist im Prinzip eigentlich auch gar nicht nötig, wenn Sie mit GTK+ Programme schreiben wollen. Die Events in GTK+ werden mit Wrapper von den GDK-Events verwendet und machen Ihnen das Leben recht einfach. GTK+ übernimmt alles für Sie. Die GDK-Events sind in der GTK+Bibliothek als Signale vorhanden. In der Regel könnten Sie es also dabei belassen und so weiterarbeiten.
Was ist aber, wenn Sie z. B. bei einem Mausklick im Fenster die exakte Position benötigen, wo und welcher Mausbutton gedrückt wurde – wie bei einem Zeichenprogramm? Sie haben zwar dafür die Signale button-press-event, button-release-event oder clicked, aber keine genaue Positionsangaben. In solch einem Fall müssen Sie tatsächlich ein wenig in die niedrigere Ebene von GDK einsteigen. In diesem Fall geht es zwar vorwiegend um die Maus-Events. Aber wenn das Thema verstanden wurde, lässt sich dieses genauso gut auf die anderen Events übertragen.
Sie haben häufig in Callback-Funktionen den Parameter (genauer die Struktur) GdkEvent verwendet, ohne eigentlich gewusst zu haben, was Sie da eigentlich nutzen. Im Prinzip konnte Ihnen das auch egal sein, da Ihnen ja GTK+ die ganze Arbeit abgenommen hat. Ein Beispiel einer solchen Callback-Funktion:
gboolean
callback(GtkWidget * widget, GdkEvent* event, GtkWidget * label);
Es wird jetzt davon ausgegangen, dass ein Mausbutton an einer bestimmten Position im Fenster betätigt wurde und die Callback-Funktion show_pos mit dem Signal button-press-event aufgerufen wurde. Jetzt benötigen Sie erst die Struktur von GdkEvent. Da in der Regel nur ein Event auf einmal auftritt, wird hierzu eine union verwendet:
union GdkEvent {
GdkEventType type;
GdkEventAny any;
GdkEventExpose expose;
GdkEventNoExpose no_expose;
GdkEventVisibility visibility;
GdkEventMotion motion;
GdkEventButton button;
GdkEventScroll scroll;
GdkEventKey key;
GdkEventCrossing crossing;
GdkEventFocus focus_change;
GdkEventConfigure configure;
GdkEventProperty property;
GdkEventSelection selection;
GdkEventProximity proximity;
GdkEventClient client;
GdkEventDND dnd;
GdkEventWindowState window_state;
GdkEventSetting setting;
};
In dieser union finden Sie jetzt alle möglichen Event-Strukturen, die bei einer Anwenderaktion zurückgegeben werden können. In diesem Fall interessiert uns das Event der Mausbuttons, das Sie an GdkEventButton erkennen können. Die Spur soll nun weiterverfolgt werden. Ein Blick in die Dokumentation bringt folgende Struktur ans Tageslicht:
struct GdkEventButton {
GdkEventType type;
GdkWindow *window;
gint8 send_event;
guint32 time;
gdouble x;
gdouble y;
gdouble *axes;
guint state;
guint button;
GdkDevice *device;
gdouble x_root, y_root;
};
Und schon haben Sie alles, was Sie für die Event-_Bearbeitung der Maus benötigen. Im Großen und Ganzen sind wir hier ja nur an x, y und button interessiert – aber ein wenig Weiterbildung kann nicht schaden. type beinhaltet die Art des Mausereignisses (GDK_BUTTON_PRESS, GDK_2BUTTON_PRESS (Doppelklick), GDK_3BUTTON_PRESS (Dreifachklick) oder GDK_BUTTON_RELEASE. window ist das Fenster, welches das Event erhalten hat. time enthält die Zeit des Events in Millisekunden. x und y sind die relativen Koordinaten des Mauszeigers im Fenster. Mit axes werden die x-, y-Achsen des Gerätes übersetzt. Im Falle einer Maus ist der Wert NULL. state ist eine Bitmaske, die den Zustand der modifizierten Tasten (wie (Ctrl), (Ş)oder (Alt)) und den Zeigerbutton anzeigt. Welche Maustaste gedrückt wurde, finden Sie in button. Der Wert wird von 1 bis 5 nummeriert und entspricht denselben Werten wie schon beim X Window. device macht eine Aussage zum Gerät, welches das Event ausgelöst hat (es muss nicht immer eine Maus sein). x_root und y_root sind relative Koordinaten des Zeigers vom Ursprung des Bildschirms.
Somit ergibt sich folgende Callback-Funktion:
gboolean show_pos ( GtkWidget * widget,
GdkEvent * event,
GtkWidget * label );
Wollen Sie jetzt hier ermitteln, welcher Button gedrückt wurde, können Sie dies mit folgender Angabe machen:
gint z = event->button.button;
event ist der Zeiger auf die Struktur GdkEvent in der Callback-Funktionsdefinition, button ist vom Typ GdkEventButton in union GdkEvent, und der zweite button ist eine Strukturvariable in GdkEventButton. Sie können dies aber abkürzen, indem Sie bei dem Funktionsprototypen der Callback-Funktion gleich GdkEventButton verwenden:
static gboolean show_pos ( GtkWidget * widget,
GdkEventButton * event,
GtkWidget * label );
Der Rückgabewert der Callback-Funktion sollte TRUE sein, wenn alles beim Behandeln des Events glatt verlief – bei einem Fehler sollten Sie FALSE zurückgeben.
Wie schon mit dem Xt-Toolkit müssen Sie die Events, die Sie bearbeiten wollen, selektieren – denn auch hier macht es wenig Sinn, von vornherein auf alle Events reagieren zu wollen. Der X-Server wäre nur noch mit den Events beschäftigt. Die Events, die Sie speziell bearbeiten wollen, selektieren Sie mit der Funktion gtk_widget_set_event(). Die Syntax dazu:
void gtk_widget_set_events (GtkWidget *widget, gint events);
Damit fügen Sie zu einem Widget eine oder mehrere Eventmasken hinzu, die Sie bearbeiten wollen. Mehrere Masken werden mit dem bitweisen ODER-Operator getrennt. Hierzu einige der wichtigsten GDK-Eventmasken:
Tabelle 15.29
Einige Eventmasken und deren Signale
Eventmaske
|
Signal
|
Bedeutung
|
GDK_EXPOSURE_MASK
|
expose-event
|
Fenster oder Teil davon wurde sichtbar.
|
GDK_BUTTON_MOTION_MASK
|
motion-notify-event
|
Maus wurde bewegt, während gleichzeitig ein Button gedrückt wurde.
|
GDK_BUTTON1_MOTION_MASK
GDK_BUTTON2_MOTION_MASK
GDK_BUTTON3_MOTION_MASK
|
motion-notify-event
|
Maus wurde bewegt, während gleichzeitig ein spezieller Button gedrückt wurde.
|
GDK_BUTTON_PRESS_MASK
|
button-press-event
|
Button wird gedrückt.
|
GDK_BUTTON_RELEASE_MASK
|
button-release-event
|
Button wird losgelassen.
|
GDK_KEY_PRESS_MASK
|
key-press-event
|
Taste wird gedrückt.
|
GDK_KEY_RELEASE_MASK
|
key-release-event
|
Taste wird losgelassen.
|
GDK_ENTER_NOTIFY_MASK
|
enter-notify-event
|
Zeiger ist in das Fenster gekommen.
|
GDK_LEAVE_NOTIFY_MASK
|
leave-notify-event
|
Zeiger hat das Fenster verlassen.
|
GDK_FOCUS_CHANGE_MASK
|
focus-in-event
focus-out-event
|
Tastaturfokus zwischen den Fenstern wurde gewechselt.
|
GDK_PROPERTY_CHANGE_MASK
|
property-notify-event
|
Eine Fenstereigenschaft wurde verändert.
|
Hierzu nun ein einfaches Beispiel, das sich vorwiegend um die Events GDK_BUTTON_PRESS_MASK und GDK_BUTTON_RELEASE_MASK kümmert, aber auch die Eventmasken GDK_ENTER_NOTIFY_MASK, GDK_LEAVE_NOTIFY_MASK und GDK_KEY_PRESS_MASK verwendet, ohne allerdings Wichtigeres damit zu machen.
/* gtk9.c */
#include <gtk/gtk.h>
#define LEFTBUT 1
#define MIDDLEBUT 2
#define RIGHTBUT 3
static gint
delete_Event(GtkWidget *widget, GdkEvent *event, gpointer data) {
return FALSE;
}
static void exitCB (GtkButton * clicked, gpointer data) {
gtk_main_quit ();
}
static gboolean show_pos (
GtkWidget *widget, GdkEventButton *event, GtkWidget *label ) {
const gchar *button[] = {
NULL, "linker Button", "mittlerer Button","rechter Button"
};
gint x = event->x;
gint y = event->y;
gint z = event->button;
gchar *pos = g_strdup_printf ("Event: %d, %d (%s)\n",
x, y, button[z]);
gtk_label_set_text (GTK_LABEL (label), pos);
return TRUE;
}
static gboolean change_label1 (
GtkWidget* widget, GdkEventButton* event, GtkWidget *label ) {
const gchar *new_label = {
"Event : Mauscursor tritt ins Fenster ein\n"
};
gtk_label_set_text (GTK_LABEL (label), new_label);
return TRUE;
}
static gboolean change_label2 (
GtkWidget * widget, GdkEventButton * event, GtkWidget * label) {
const gchar *new_label = {
"Event : Mauscursor hat Fenster verlassen\n"
};
gtk_label_set_text (GTK_LABEL (label), new_label);
return TRUE;
}
static gboolean key_press (
GtkWidget * widget, GdkEventKey * event, GtkWidget * label) {
gchar *new_label = g_strdup_printf (
"Event : Taste '%s' (Wert:%d) wurde gedrueckt\n",
event->string, event->keyval);
gtk_label_set_text (GTK_LABEL (label), new_label);
return TRUE;
}
int main (int argc, char **argv) {
GtkWidget *fenster;
GtkWidget *label;
/*Gtk initialisieren */
gtk_init (&argc, &argv);
/*Ein neues Fenster erstellen */
fenster = g_object_new( GTK_TYPE_WINDOW,
"title", "Verschiedene Anzeigeelemente",
"default-width", 300,
"default-height", 200,
"resizable", TRUE,
"window-position", GTK_WIN_POS_CENTER,
"border-width", 5,
NULL );
label = g_object_new( GTK_TYPE_LABEL, NULL);
/* Versuch, über den Window-Manager zu beenden */
g_signal_connect ( GTK_OBJECT (fenster), "delete-event",
G_CALLBACK (delete_Event), NULL);
g_signal_connect ( GTK_OBJECT (fenster), "destroy",
G_CALLBACK (exitCB), NULL);
/* Callbacks für die Events einrichten */
g_signal_connect (GTK_OBJECT (fenster), "button-press-event",
G_CALLBACK (show_pos), label);
g_signal_connect ( GTK_OBJECT (fenster), "enter-notify-event",
G_CALLBACK(change_label1), label);
g_signal_connect ( GTK_OBJECT (fenster), "leave-notify-event",
G_CALLBACK (change_label2), label);
g_signal_connect ( GTK_OBJECT (fenster), "key-press-event",
G_CALLBACK (key_press), label);
gtk_widget_set_events (fenster,
GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
| GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK
| GDK_KEY_PRESS_MASK);
gtk_container_add (GTK_CONTAINER (fenster), label);
/* Zeigs uns ... */
gtk_widget_show_all (fenster);
gtk_main ();
return 0;
}
Das Programm bei der Ausführung:
$ gcc -Wall -o gkt9 gtk9.c `pkg-config gtk+-2.0 --cflags --libs`
$ ./gkt9
|