GTK-Programmierung mit C++

Die Übersichtlichkeit der Seite wird durch Javascript erhöht. Ist dies aktiviert, werden die Texte unter den Überschriften durch Anklicken der Überschriften ein- und ausgeblendet.

Voraussetzungen

Unter Gnome oder XFCE sind die gtk-libs automatisch installiert. Dennoch wird der Versuch, ein Programm, wie ich es in diesem Tutorial schreibe zu kompilieren scheitern, wenn die Libs und Flags dem Compiler nicht mit übergeben werden.

Die nötigen Übergabe-Parter können wie folgt ermittelt werden:

Libs
pkg-config --libs gtk+-3.0
Flags
pkg-config --cflags gtk+-3.0

Dies ergibt jeweils eine recht lange Ausgabe an zu übergebenden Parametern. Um nicht die kompletten Listen in den jeweiligen Befehl hineinkopieren zu müssen, kann man die genannten Befehle in Backticks (`) eingeschlossen direkt als Parameter übergeben.

g++ -Wall  cpp-Dateiname `pkg-config --libs gtk+-3.0 --cflags gtk+-3.0` Programmname

Um diesen immer noch recht langen Befehl nicht immer wieder eingeben zu müssen, habe ich einen Alias vergeben:

alias cgtk='g++ -Wall `pkg-config gtk+-3.0 --libs --cflags`'
alias cpgtk='g++ -Wall `pkg-config gtkmm-3.0 --libs --cflags`'

Ab sofort diese GTK-Programme auf einfache Weise wie folgt compilieren:

cgtk CPP-Dateiname [-o Programmname]

oder für Programme, die gtkmm benutzen:

cpgtk CPP-Dateiname [-o Programmname]

Wird der Programmname weg gelassen, ist der Name des Compilats a.out.

GTK-Übergabeparameter

Libs: pkg-config --libs gtk+-3.0

Der Befehl:

pkg-config --libs gtk+-3.0

erzeugt bei mir folgende Ausgabe:

-lgtkmm-3.0 -latkmm-1.6 -lgdkmm-3.0 -lgiomm-2.4
-lpangomm-1.4 -lglibmm-2.4 -lgtk-3 -lgdk-3 -lpangocairo-1.0
-lpango-1.0 -latk-1.0 -lcairo-gobject -lgio-2.0
-lcairomm-1.0 -lcairo -lsigc-2.0 -lgdk_pixbuf-2.0
-lgobject-2.0 -lglib-2.0
llibrary
-l library Search the library named library when linking. -(The second alternative with the library as a separate -argument is only for POSIX compliance and is not recommended.)

It makes a difference where in the command you write this option; the linker searches and processes libraries and object files in the order they are specified. Thus, foo.o -lz bar.o searches library z after file foo.o but before bar.o. If bar.o refers to functions in z, those functions may not be loaded. The linker searches a standard list of directories for the library, which is actually a file named liblibrary.a. The linker then uses this file as if it had been specified precisely by name. The directories searched include several standard system directories plus any that you specify with -L. Normally the files found this way are library files---archive files whose members are object files. The linker handles an archive file by scanning through it for members which define symbols that have so far been referenced but not defined. But if the file that is found is an ordinary object file, it is linked in the usual fashion. The only difference between using an -l option and specifying a file name is that -l surrounds library with lib and .a and searches several directories.

nach oben

pkg-config gtkmm-3.0 --cflags

Der Befehl:

pkg-config --cflags                                                  

erzeugt bei mir folgende Ausgabe:

-pthread -I/usr/include/gtkmm-3.0 -I/usr/lib/gtkmm-3.0/include -I/usr/include/atkmm-1.6 -I/usr/include/gtk-3.0/unix-print -I/usr/include/gdkmm-3.0 -I/usr/lib/gdkmm-3.0/include -I/usr/include/giomm-2.4 -I/usr/lib/giomm-2.4/include -I/usr/include/pangomm-1.4 -I/usr/lib/pangomm-1.4/include -I/usr/include/glibmm-2.4 -I/usr/lib/glibmm-2.4/include -I/usr/include/gtk-3.0 -I/usr/include/at-spi2-atk/2.0 -I/usr/include/at-spi-2.0 -I/usr/include/dbus-1.0 -I/usr/lib/dbus-1.0/include -I/usr/include/gtk-3.0 -I/usr/include/gio-unix-2.0/ -I/usr/include/cairo -I/usr/include/pango-1.0 -I/usr/include/atk-1.0 -I/usr/include/cairo -I/usr/include/cairomm-1.0 -I/usr/lib/cairomm-1.0/include -I/usr/include/cairo -I/usr/include/pixman-1 -I/usr/include/freetype2 -I/usr/include/libpng16 -I/usr/include/harfbuzz -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include -I/usr/include/freetype2 -I/usr/include/harfbuzz -I/usr/include/libdrm -I/usr/include/libpng16 -I/usr/include/sigc++-2.0 -I/usr/lib/sigc++-2.0/include -I/usr/include/gdk-pixbuf-2.0 -I/usr/include/libpng16 -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include

Zitat aus der man-Page von g++:

pthread
Adds support for multithreading with the pthreads library. This option sets flags for both the preprocessor and linker.
I dir
Add the directory dir to the list of directories to be searched for header files. Directories named by -I are searched before the standard system include directories. If the directory dir is a standard system include directory, the option is ignored to ensure that the default search order for system directories and the special treatment of system headers are not defeated . If dir begins with "=", then the "=" will be replaced by the sysroot prefix; see --sysroot and -isysroot.

Somit Stellt die Option -I die Möglichkeit dar ein Verzeichnis zu benennen, in dem sich die Header einer Bibliothek befinden.

nach oben

Tutorial

Einfaches Fenster

Um ein einfaches Fenster zu erzeugen muss zunächst immer folgendes Grundgerüst erstellt werden:


#include <gtk/gtk.h>

int main (int argc, char* argv[])
{
  gtk_init(&argc, &argv);
  GtkWidget *window; // *eventuelle *weitere *Widgets
  
  window = gtk_window_new(GTK_WINDOWTOPLEVEL);
  g_signal_connect(window,"delte-event",G_CALLBACK(gtk_main_quit),NULL);

  // Einbetten der weiteren Steuerelemente

  gtk_widget_show_all(window);  

  // Damit das Fenster offen bleibt, bis es geschlossen wird
  gtk_main(); 
  return 0
}

#include <gtk/gtk.h>

Dies ist der entsprechende Header für die Verwendung der GTK-Bibliothek

nach oben

int main( int argc, char* argv[]){ ... }

Die Parameterübernahme in den Klammern ist für die GTK-Programmierung wesentlich. In der Folge wird auf diese Parameter zugegriffen.

nach oben

gtk_init( &argc, &argv);

Damit wird das UI initialisiert. Dadurch erhält zum Beispiel, das Fenster vom Standard her immer den Programmnamen, der unter anderm über diese Parameter übergeben wird.

nach oben

GtkWidget window, weiter Variablen-Liste der Steuerelemente;

Alle Steuerelemente werden als GtkWidget-Variabeln deklariert, wobei jeder Widget-Variable ein Sternchen vorgesetzt wird.

nach oben

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

mit = gtk_window_new werden die Fenster definiert. Der Funktion muss als Parameter die Classe des Fensters übergeben werden. Für das Hauptprogrammfenster ist dies GTK_WINDOW_TOPLEVEL

nach oben

g_signal_connect(window,"delete-event",G_CALLBACK(gtk_main_quit),NULL);

Um ein Ereignis zu empfangen, wird die Funktion g_signal_connect() genutzt. Diese hat 4 Parameter:

  • Das Steuerelement von dem das Ereignis ausgeht (Bsp. window)
  • Der Name des Ereignisses:
    "delete-event"
    für das Betätigen des Schließenkreuzes des Fensters
  • Die Funktion, die bei Eintreten des Ereignisses ausgeführt werden soll, wobei der Funktionsname immer der Funktion G_CALLBACK() als Parameter übergeben wird.
  • NULL - da dieser Parameter für das Fenster nicht benötigt wird

nach oben

Einbetten weiterer Steuerelemente

Allgemein

Wichtig: Ein Fenster kann als Kontainer maximal ein Steuerelement aufnehmen. Deshalb muss dem Fenster zunächst ein Container zugeordnet werden, der die weiteren Steuerelemente aufnimmt, wenn mann mehr als ein Steuerelement anzeigen möchte!

Zum Einbetten weiterer Steuerelmente werden für diese zunächst Variablen deklariert und definiert. Die Deklaration ( GtkWidget widgetVariable;) kann auch direkt als mit Komma getrennte Liste, bei der Deklaration der Fenster-Widget-Variable erfolgen. Die Definiton erfolgt nach folgendem Schema:

widgetVariable = gtk_STEUERELEMENTNAME_new(Parameter);

Bsp. LABEL: lblEinLabel = gtk_label_new("Hallo Welt");

Dannach muss das Steuerelement einem Kontainer zugeordnet werden, indem es angezeigt werden soll. Dies geschiet im einfachsten Fall mit der Funktion: gtk_container_add(KONTAINER,STEUERELEMENTVARIABLE). Der Kontainer wird dabei über die Funktion GTK_CONTAINER(VARIABLE-DES-KONTAINERSTEUERELEMENTES) übergeben.

Bsp. LABEL: GtKWidget fEinFenster, *lblEinLabel; gtk_container_add(GTK_CONTAINER(fEinFenster),lblEinLabel);

Da das Widget window nur ein Steuerelement aufnehmen kann gibt es andere Container, die in window aufgenommen werden und Ihrerseits mehrere Steuerelemente aufnehmen können.

nach oben

Container

box

gtk_box_new(Orientierung, Abstand)

Orientierung kann sein:

GTK_ORIENTATION_HORIZONTAL
für eine horizontale Ausrichtung
GTK_ORIENTATION_VERTICAL
für eine verticale Ausrichtung

Der Abstand wird in Pixel angegeben und bewirkt, dass alle Steuerelemente, die in die Box aufgenommen werden, diesen Abstand zueinander einhalten. Zum Rand der Box, wird hierdurch kein Abstand errzeugt.

Standardmäßig verwenden die Steuerelemente innerhalb der Box den gesamten Platz für alle Steuerelemente in der Richtung in der die Box ausgerichtet ist, nicht aber in der jeweils andern. Das heist bei einer Horizontalen Ausrichtung, dass sich die Steuerelemente unabhängig von Ihren benötigten Platz über die gesammte Breite ausdehnen, in der Höhe allerdings nur den tatsächlich benötigten Platz beanspruchen und der restliche zur Verfügung stehende Bereich frei bleibt. Möchte man die Ausdehnung auch in der jeweils anderen Richtung einschalten, nutzt man die Funktion: gtk_box_set_homogeneous(GtkBox *box, gboolean homogeneous); Bsp: Wenn die Box box heißt: gtk_box_set_homogeneous(GTK_BOX(box),true);

Anstatt die Steuerelemente mit der gtk_container_add Funktion einer Box zuzuordnen und so ein Standard-Layout zu bekommen, bietet die gtk_box_pack_start() bzw gtk_box_pack_end() Funktion mehr Möglichkeiten.

Beide Funktionen haben folgende Parameter:

GtkBox box
die Box gecastet: GTK_BOX(box)
GtkWidget child
die Steuerelementvariable, die gepackt werden soll: label
gboolean expand
wenn true, verteilt sich das Steuerelement auf dem zur Verfügung stehenden Platz. Ist es das einzige Steuerelement, wird es zentriert.
gboolean fill
wenn true, dehnt sich das Steuerelement auf dem gesamten zur Verfügung stehenden Platz aus. Dies macht meiner Meinung nach nur Sinn, wenn expand auch true ist, da es nur dann mehr Platz zur Verfügung bekommt.
guint padding
gibt dem Abstand zum nächsten Steuerelement oder zum Rand der Box in Pixeln an.

nach oben

table

LABEL

GtkWidget win, *box, *lblEinLabel; ... lblEinLabel = gtk_label_new("Text, der angezeigt werden soll"); gtk_container_add(GTK_CONTAINER(box),lblEinLabel); ...

Funktionen für das LABEL

gtk_label_set_text(Label,Text);

Setzt den als Zeichenkette angegbenen Text für das Label. Das Label wird durch die Funktion GTK_LABEL(label-Widget-Variable) übergeben.

Beispiel: gtk_label_set_text(GTK_LABEL(lblEinLabel),"Das soll angezeigt werden");

nach oben

Button

GtkWidget win, *box, *btnEinButton; btnEinButton = gtk_button_new(); // Button als leerer Kontainer besser: btnEinButton = gtk_button_new_with_label("Button-Text"); gtk_container_add(GTK_CONTAINER(box),btnEinButton);

Ereignisverarbeitung

Klick
g_signal_connect(btnEinButton, "clicked",G_CALLBACK(Funktionsname),NULL);

Event-Funktion

static void element_Ereignis(GtkWidget* widget, gpointer data) { // Ausgabe in der Konsole g_print("Es ist etwas passiert\n"); }

nach oben

gtk_widget_show_all(window);

Zum Anzeigen des Fensters und aller dazugehörigen Elemente.

nach oben

gtk_main();

Ist der Loop, der das Fenster geöffnet hält, bis es durch den Benutzer oder das Programm geschlossen wird.

nach oben

return 0;

Der Abschluss der main-Funktion. Das Zurückgeben von 0 zeigt an, dass das Prtogramm ordnungsgemäß beendet wurde.

nach oben