GTK mit Builder: Erste Schritte
Hallo, Welt!
[Bearbeiten]Das erste GTK+ 3.x Programm soll ein Fenster erzeugen, in dem sich eine Beschriftung mit dem bekannten Text „Hallo, Welt!“ befindet.
#include <gtk/gtk.h>
static void on_window_closed (GtkWidget *widget, gpointer data)
{
gtk_main_quit ();
}
int main (int argc, char *argv[])
{
GtkWidget *window, *label;
/* Initialisieren */
gtk_init (&argc, &argv);
/* Fenster erzeugen */
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
/* "destroy"-Ereignis mit einer Funktion verknüpfen */
g_signal_connect (window, "destroy", G_CALLBACK(on_window_closed), NULL);
/* Textfeld erzeugen */
label = gtk_label_new ("Hallo, Welt!");
/* Textfeld dem Fenster hinzufügen */
gtk_container_add (GTK_CONTAINER(window), label);
/* Alle erzeugten Widgets anzeigen lassen */
gtk_widget_show (label);
gtk_widget_show (window);
/* Ereignisschleife */
gtk_main ();
return 0;
}
Innerhalb der main()
-Funktion werden zwei Widgets deklariert. Widgets sind in GTK+ alle vom Benutzer sichtbaren Dinge in einem Programm. Hierzu gehören Knöpfe, Menüs, Textfelder, Fenster, Schieberegler und viele andere Dinge.
Ein GTK+-Programm wird am Anfang mit der Funktion gtk_init()
initialisiert. Diese Funktion hat die Aufgabe, einige Standardargumente aus einer eventuell bereitgestellten Menge an Programmparametern zu filtern, wie auch das eigentliche Toolkit zu initialisieren. Wenn keine Kommunikation mit dem Fenstermanager möglich ist, dann sorgt diese Funktion dafür, dass das Programm sofort terminiert. Eine Alternative zu gtk_init()
ist gtk_init_check()
. Diese Funktion gibt FALSE
zurück, wenn GTK+ nicht initialisiert werden kann und ermöglicht es Ihnen somit, alternativ eine Textoberfläche statt der erwarteten grafischen Benutzeroberfläche bereitzustellen.
Das Hauptfenster wird mit der Funktion gtk_window_new()
erzeugt. Der Parameter kann entweder GTK_WINDOW_TOPLEVEL
für normale Hauptfenster und Dialoge oder GTK_WINDOW_POPUP
für undekorierte Hilfefenster, so genannte „Tooltips“, sein.
Widgets in GTK+ reagieren auf Ereignisse, so genannte „Events“. Diese Ereignisse werden zumeist durch Benutzerinteraktion ausgelöst. Wenn Sie einen Knopf drücken, ein Menü auswählen oder an einem Schieberegler ziehen werden Ereignisse generiert, auf die das Programm reagieren kann. In unserem einfachen Beispiel wird lediglich auf das „destroy“-Ereignis reagiert, das immer dann ausgesendet wird, wenn das Fenster geschlossen wird. Hierzu wird das Ereignis „destroy“ mit Hilfe der Funktion g_signal_connect()
mit einer Funktion verknüpft, die aufgerufen wird, wenn das Ereignis eintritt. In unserem Fall heißt die zu verknüpfende Funktion on_window_closed()
. Im „data“-Argument steht beim Aufruf genau das, was Sie als letztes Argument in der Funktion g_signal_connect()
hingeschrieben haben. Dieser Parameter wird also durchgereicht und hilft bei der Kommunikation zwischen verschiedenen Teilen des Programms. Hier können Sie beispielsweise mitteilen, ob in einer Textverarbeitung der aktuelle Text noch gespeichert werden muss.
Textfelder werden mit gtk_label_new()
erzeugt. Der Inhalt der Textfelder kann mehrzeilig sein und mit einer einfachen Auszeichnungssprache erzeugt werden. Das Textfeld wird mit der Funktion gtk_container_add()
dem Fenster hinzugefügt. Das im Parameter genutzte Makro GTK_CONTAINER()
deutet schon an, dass man Widgets auch anderen Widgets hinzufügen kann, nicht nur Fenstern.
Anschließend werden die im Programm erzeugten Widgets mit gtk_widget_show()
angezeigt. Statt für jedes Widget einzeln hätten wir auch mit gtk_widget_show_all()
das Hauptfenster wie auch das Textfeld mit einem Aufruf anzeigen lassen können. Mit der Funktion gtk_widget_hide()
können wir übrigens einzelne Widgets verstecken.
Zum Schluss wird die Hauptschleife mit gtk_main()
gestartet. In dieser Funktion kümmert sich das Toolkit hauptsächlich um Ereignisse, unabhängig davon, ob Sie durch den Benutzer ausgelöst wurden oder nicht. Diese Funktion kann explizit beendet werden durch einen Aufruf von gtk_main_quit()
. Beachten Sie bitte, dass man Aufrufe von gtk_main()
auch schachteln kann, auch wenn Sie vermutlich niemals in die Situation kommen werden. In diesem Fall beendet gtk_main_quit()
die tiefste Ebene.
Das Programm erstellen Sie mit
user@localhost:~$ gcc fenster1.c -o fenster1 -Wall `pkg-config --libs --cflags gtk+-3.0`
Beachten Sie, dass hier rückwärtsgeneigte Hochkommata verwendet werden („Backticks“). Diese können Sie auf einer Deutschen Tastatur über die Tastenkombination <Shift> + <Taste rechts neben Fragezeichen> erzeugen. Oder Sie verwenden $( ) statt den „Backticks“:
user@localhost:~$ gcc fenster1.c -o fenster1 -Wall $(pkg-config --libs --cflags gtk+-3.0)
Hauptfenster
[Bearbeiten]Dem Hauptfenster kann man einen Titel hinzufügen. Ebenfalls kann man dafür sorgen, dass das Fenster eine feste Ausdehnung einnimmt und sich diese Größe nicht ändern lässt.
#include <gtk/gtk.h>
static void on_window_closed (GtkWidget *widget, gpointer data)
{
gtk_main_quit ();
}
int main (int argc, char *argv[])
{
GtkWidget *window, *label;
gtk_init (&argc, &argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
g_signal_connect (window, "destroy", G_CALLBACK(on_window_closed), NULL);
label = gtk_label_new ("Hallo, Welt!");
gtk_container_add (GTK_CONTAINER(window), label);
/* Fenstertitel anzeigen */
gtk_window_set_title (GTK_WINDOW(window), "Mein kleines Fenster");
/* Größe des Fensters festlegen */
gtk_window_set_default_size (GTK_WINDOW(window), 640, 480);
/* Keine Größenänderung des Fensters zulassen */
gtk_window_set_resizable (GTK_WINDOW(window), FALSE);
/* Alles anzeigen */
gtk_widget_show_all (window);
gtk_main ();
return 0;
}
Der Fenstertitel wird mit dem Aufruf von gtk_window_set_title()
gesetzt. Die Anfangsgröße des Fensters kann man mit dem Aufruf von gtk_window_set_default_size()
festlegen. Man ermöglicht oder verhindert mit dem Aufruf von gtk_window_set_resizable()
die Größenänderung des Fensters. Verhindern kann man das mit dem Parameter FALSE
, wieder zulassen mit TRUE
.
Da GTK+ jedoch anstrebt die Fenster-Elemente (genauer gesagt die Elemente der Klasse GtkWidget, von welchen die Klasse GtkWindow erbt) so kompakt wie möglich und so groß wie nötig zu zeichnen, kann es notwendig sein auch eine Minimal-Größe zu setzten, welche das Fenster mindestens haben soll. Ansonsten kann es sein, dass GTK+ die Fenster-Größe optimiert und nicht die gesetzte Größe gewählt wird, sondern die kompaktest-Mögliche (also nur das Text-Label). Das Setzten der Minimal-Größe kann mit folgender Zeile erreicht werden, welche einfache nach der gtk_window_set_resizable()-Methode eingefügt werde kann:
gtk_widget_set_size_request(GTK_WIDGET(window), 640, 480);
Beschriftung
[Bearbeiten]Eine Beschriftung („Label“) kann Text enthalten, der mit HTML-ähnlichen Auszeichnungen („Markup“) versehen wurde. Die Beschriftung lässt sich auch drehen, wie folgender Code zeigt.
#include <gtk/gtk.h>
static void on_window_closed (GtkWidget *widget, gpointer data)
{
gtk_main_quit ();
}
int main (int argc, char *argv[])
{
GtkWidget *window, *label;
gtk_init (&argc, &argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
g_signal_connect (window, "destroy", G_CALLBACK(on_window_closed), NULL);
label = gtk_label_new (NULL);
/* Beschriftungstext mit Auszeichnungen */
gtk_label_set_markup (GTK_LABEL(label),
"<small>Kleiner Text</small>\n"
"<big>Large</big>\n"
"<b>Fett</b>\n"
"<span foreground=\"#ff0000\">ROT</span>");
/* Label um 45 Grad gegen den Uhrzeigersinn drehen */
gtk_label_set_angle (GTK_LABEL(label), 45);
gtk_container_add (GTK_CONTAINER(window), label);
gtk_window_set_title (GTK_WINDOW(window), "Mein kleines Fenster");
gtk_window_set_default_size (GTK_WINDOW(window), 640, 480);
gtk_window_set_resizable (GTK_WINDOW(window), FALSE);
gtk_widget_show_all (window);
gtk_main ();
return 0;
}
Die Funktion gtk_label_set_markup()
übergibt der Beschriftung gestalteten Text. Die Markup-Sprache wird von der Schriftsatz-Bibliothek Pango bereitgestellt, die von GTK+ benutzt wird. Vertikale oder diagonale Beschriftungen lassen sich mit der Funktion gtk_label_set_angle()
erzeugen, hier wird der Winkel in Dezimalgrad gegen den Uhrzeigersinn angegeben. 90 Grad bedeuten also vertikalen Text, den man von unten nach oben liest.
Druckknopf
[Bearbeiten]Das folgende Programm demonstriert, wie man mit Knöpfen umgeht. Diese senden ein Signal aus, wenn sie gedrückt werden. Das Signal kann man abfangen und nutzen, wie das folgende Programm zeigt.
#include <gtk/gtk.h>
static void on_window_closed (GtkWidget *widget, gpointer data)
{
gtk_main_quit ();
}
/* Liefert TRUE zurück, wenn gerade Zahl, FALSE, wenn ungerade */
static gboolean is_even (int number)
{
return number % 2 == 0;
}
static void on_button_clicked (GtkWidget *widget, gpointer data)
{
static int click_count;
/* Jedes 2. Mal soll "Danke!" bzw "Nochmal?" ausgegeben werden */
gtk_button_set_label (GTK_BUTTON(widget), is_even (click_count) ? "Danke!" : "Nochmal?");
/* Anzahl der Klicks vergrößern */
click_count++;
}
int main (int argc, char *argv[])
{
GtkWidget *window, *button;
gtk_init (&argc, &argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
g_signal_connect (window, "destroy", G_CALLBACK(on_window_closed), NULL);
/* Füge einen Druckknopf ein */
button = gtk_button_new_with_label ("Drück mich!");
/* Signal "draufklicken" wird verknüpft mit einer Rückruf-Funktion */
g_signal_connect(button, "clicked", G_CALLBACK(on_button_clicked), NULL);
gtk_container_add (GTK_CONTAINER(window), button);
gtk_window_set_title (GTK_WINDOW(window), "Mein kleines Fenster");
gtk_window_set_default_size (GTK_WINDOW(window), 200, 200);
gtk_widget_show_all (window);
gtk_main ();
return 0;
}
Ein neuer Knopf wird mit der Funktion gtk_button_new_with_label()
erzeugt. Wenn Sie keine Beschriftung im Knopf wünschen, oder eine eigene Beschriftung später einfügen wollen, reicht als Alternative auch gtk_button_new()
aus. Ein Druckknopf kann verschiedene Signale aussenden. Wird die Maus über dem Knopf gedrückt und anschließend losgelassen wird das Signal „clicked“ ausgesendet, welches wir abfangen wollen. In unserem Fall wollen wir zählen, wie oft der Knopf gedrückt wurde.
In der Callback-Funktion on_button_clicked()
haben wir dazu eine Zählvariable click_count
vorgesehen. Diese ist statisch („static“) deklariert, damit ihr Wert nach Ende der Funktion nicht verloren geht, sondern beibehalten wird. Wurde der Knopf einmal gedrückt, so wird „Danke“ ausgegeben, beim zweiten Mal „Nochmal“ und dann wieder von vorne. Dabei hilft die von uns definierte Funktion is_even()
, die uns sagt, ob eine Zahl gerade oder ungerade ist. Der Button-Text wird mit der Funktion gtk_button_set_label()
gesetzt.
Zusammenfassung
[Bearbeiten]In diesem Kapitel haben Sie die Grundlagen von GTK+ kennen gelernt. Sie kennen nun Fenster, Beschriftungen und Druckknöpfe und können diese mit einigen Funktionen manipulieren. Ebenfalls wissen Sie, dass Widgets Signale aussenden können und dass Signale mit Callbacks genutzt werden.