Seitenleiste

Signale

Zu einer guten Benutzeroberfläche gehört nicht nur, dass sie gut aussieht und auch viele Widgets enthält. Sie soll auch nutzbar sein, das heißt ein Klick auf einen Button soll auch zu einer Reaktion führen. Hierfür gibt es sogenannte Signale. Auf ein Signal (oder Event) folgt ein Callback, die Reaktion.

Die Theorie

Wenn wir in GTK eine GUI schreiben, machen wir dies immer in zwei Teilen:

  1. Wir erstellen alle Widgets und passen sie für unseren Einsatz an.
  2. Wir verknüpfen ein Signal, welches von einem bestimmten Widget ausgeht, mit einer Callback-Funktion.

Uns soll es nun um den zweiten Teil gehen. Wie erkennt ein Programm, ob ein Signal eingetreten ist?

Der GTK-MainLoop

Klingt komplizierter als es ist. Der GTK-MainLoop sorgt dafür, dass bei einem Signal auch die registrierte Callback-Funktion ausgeführt wird. Dies tut er, indem er den Status jedes einzelnen Widgets überprüft und bei einer Veränderung (zum Bsp. dem Klicken auf einen Button) die verknüpfte Callback-Funktion aufruft.

Die Initialisierung des MainLoops erfolgt gewöhnlich nachdem die Widgets erstellt und die Signale verknüpft wurden. Dafür sorgt die Funktion:

gtk_main() 

In der Praxis

Wir wollen nun das Programm aus dem letzten Kapitel erweitern. Bei einem Klick auf den Button soll sich das Fenster nicht schließen, sondern das Label seinen Text ändern. Was brauchen wir dafür? Richtig, Signale!

Zuerst ändern wir die Zeile

g_signal_connect(button, "clicked", G_CALLBACK(gtk_main_quit), NULL);

um in

g_signal_connect(button, "clicked", G_CALLBACK(change_label), label);

Beim Kompilieren sollte es nun die Fehlermeldung geben, dass die Funktion „change_label“ nirgendwo zu finden sei. Dies ist unsere Callback Funktion. Sie sieht so aus:

void change_label(GtkButton *button, gpointer user_data)
{
    gtk_label_set_text(GTK_LABEL(label), "Jetzt ist es anders...");
}

Mit der Funktion gtk_label_set_text ändern wir hier den Text unseres Labels. Nun interessieren uns aber die Argumente der Funktion. Denn wenn wir uns unsere Signalverknüpfung ansschauen (g_signal_connect), werden dort scheinbar gar keine Argumente übergeben. Also: Woher kommen diese zwei Zeiger?

Die Callback Funktion

Bei jedem Signal wird eine registrierte Callback Funktion aufgerufen. Diese Callback Funktion wird, je nach Signal, mit unterschiedlichen Argumenten ausgeführt. Bei unserem „clicked“ Signal, welches vom Button ausgeht, wird eine Callback Funktion erwartet, die als erstes Argument einen GtkButton besitzt und als zweites einen gpointer.

In der Regel ist es bei allen Signalen so, dass das erste Argument des Callbacks das Auslöser-Widget ist.

Daneben übergibt jedes Signal als letztes Argument einen gpointer 1). Über diesen könne eigene Daten an die Callback-Funktion übergeben werden.

Der Vollständigkeit halber hier noch einmal der vollständige Quelltext, mit leicht angepassten Texten:

#include <gtk/gtk.h>
 
void change_label(GtkButton *button, gpointer user_data)
{
	gtk_label_set_text(GTK_LABEL(user_data), "Jetzt ist es anders...");
}
 
 
int main(int argc, char ** argv)
{
	GtkWidget *window;
	GtkWidget *vbox;
	GtkWidget *label;
	GtkWidget *button;
 
	gtk_init(&argc, &argv);
 
	window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 
	vbox = gtk_vbox_new(FALSE, 10);
	gtk_container_add(GTK_CONTAINER(window), vbox);
 
	label = gtk_label_new("Klick auf den Button!");
	gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
 
	button = gtk_button_new_with_label("Dies ist der Button");
	gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 0);
 
	g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
	g_signal_connect(button, "clicked", G_CALLBACK(change_label), label);
 
	gtk_widget_show_all(window);
 
	gtk_main();
 
	return 0;
}

Zusammenfassung

Mit Signalen haben wir nun unserer Anwendung ein wenig Leben eingehaucht. Dies tun wir, indem wir einem Signal eines Widgets eine Callback-Funktion zuweisen.

Im nächsten Kapitel ist dann wieder Zeit für ein neues Widget, welches einfache Texteingabe ermöglicht.

zurück || hoch zur Startseite || weiter

1)
typedef für void*