!init OPT_STYLE="paper" !define DOC_NAME "Ein 'Hello-World!' in Gtk+" !define DOC_TYPE "[Vortrag]" !define DOC_AUTHOR "(c) 2000 Marc Lehmann " !build_title !block abstract Gtk+ (das {{G}}NU {{T}}ool{{K}}it) ist ein relativ junges UI-ToolKit mit einem objektorientierten Design. Gtk+ selbst ist in C geschrieben, es existieren jedoch Bindungen für C++, Perl, Python und einige andere Sprachen. In diesem Vortrag wird Schritt für Schritt ein einfaches "Hello-World!"-Programm erzeugt, um das "look&feel" von Gtk+ zu erfahren !endblock H1: Gtk+ Gtk+ (das {{G}}NU {{T}}ool{{K}}it) ist ein relativ junges UI-ToolKit mit einem objektorientiertem Design. Gtk+ selbst ist in C geschrieben, es existieren jedoch Bindings für C++, Perl, Python und einige andere Sprachen. Das Perl-Interface ist noch nicht vollkommen ausgereift, ist aber schon hervorragend benutzbar und wird immer mehr eingesetzt (z.B. für Administrationsfrontends in Debian GNU/Linux). Die Vorteile von Gtk+ gegenüber Tk und anderen Toolkits ist die hervorragende Integration{{}}: Perl-Widgets können von anderen Sprachen benutzt werden (z.B. von C) und natürlich umgekehrt. Eigene Widgets können sehr einfach erzeugt werden (im Gegensatz zu Tk), und die Gtk+-Bibliothek ist frei (im Sinne der LGPL) -- auch auf anderen Betriebssystemen als UNIX. H1: Gtk C ist das Perl-Modul, das die Schnittstelle zur Gtk+-Bibliothek bildet. Da die meisten Menschen "Gtk" sagen, wenn sie Gtk+ meinen, wird die Perl-Schnittstelle meistens explizit "Gtk-Perl" genannt (genauso, wie das Gimp-Modul meistens Gimp-Perl genannt wird). Zur Zeit bietet das Gtk-Modul auch Bindungen für Gnome und einige andere Bibliotheken an, möglicherweise (das ist nicht sicher) werden diese Module aber aufgetrennt werden: Gtk+ war ursprünglich das {{G}}imp-{{T}}ool{{K}}it, und sowohl Gtk+ als auch Gimp sind {{kein}} Teil des Gnome-Projektes und können deshalb ohne Gnome verwendet werden. H2: Ein einfaches Programm Hier ist ein ganz einfaches Gtk-Programm. Nichtsdestotrotz öffnet es schon ein Fenster: !block perl use Gtk; Gtk->init; $main = new Gtk::Window; $main->show; Gtk->main; !endblock C sollte klar sein. Eine Eigenheit von Gtk+ ist es, daß es zuerst explizit initialisiert werden muß (in einem Gnome-Programm würde statt {{C:Gtk->init}} ein {{C:Gnome->init}} stehen), sonst gibt es jede Menge Fehler. Der Aufruf C ist {{ein}} Weg, ein neues Fenster zu erzeugen. Neue Gtk-{{Widgets}} sind Anfangs aber noch unsichtbar. Damit sie angezeigt werden, muß zuerst die C-Methode aufgerufen werden (es gibt auch das entsprechende C). Das letzte, was unser Programm macht, ist, in die Hauptschleife von Gtk+ zu springen. Die Hauptschleife zeigt das Fenster und und -- wartet auf Ereignisse{{}}: !import "hello1"; title="Das erste Fenster" Das Fenster ist natürlich leer und es wurden auch keine Reaktionen auf Ereignisse. Die einzige Möglichkeit, das Programm zu beenden, ist es, es zu "killen" (entweder C oder z.B. C). H2: Das Signalsystem von Gtk+ Gtk+ setzt intern auf einer Bibliothek namens C (GNU Drawing ToolKit) auf, die eine Schnittstelle auf niedriger Ebene zum jeweiligen Fenstersystem (X, Win32, BeOS...) bildet. Diese Schnittstelle erzeugt Ereignisobjekte und reicht sie an Gtk+ weiter. Gtk+ verteilt diese Ereignisse an seine Widgets, indem es diesen Signale schickt. Ein Signal ist z.B. eine Mausklick (C) oder das anklicken des "Fenster-Schließen"-Knopfes (C). Gtk+ kennt aber auch eigene Signal, z.B. wenn ein Objekt zerstört wird (C), was einem Destruktor entspricht. Jeder Signal-Handler kann das Signal konsumieren, an andere Widgets weiterreichen oder einfach ignorieren (wodurch es z.B. an das Vaterwidget gelangt). Um eine Aktion an ein Signal zu binden, kann man die C-Methode aufrufen: !block perl $main = new Gtk::Window; $main->signal_connect(delete_event => sub { Gtk->main_quit }); !endblock Nun kann man den Schließknopf des Fensters betätigen, und das ausgelöste Signal (C) führt zu einem Aufruf von {{C:Gtk->main_quit}}. Das Beendet die Hauptschleife und damit das Programm. H2: Der erste Button Nun zu etwas mehr Aktion. Ein "Hello-World!"-Button muß her. Dies ist ganz einfach: !block perl $button = new Gtk::Button "Hello, World!"; $main->add($button); $button->show; !endblock Das einzig neue (neben der Erkenntnis, daß es auch Cs gibt), ist der Aufruf von C<$main->add>. C {{ist ein}} C, und erbt von diesem einige Methoden (unter anderem C). Das Fenster sieht nun so aus (nicht schön, aber es wird): !import "hello2"; title="Das Fenster -- mit Button!" Nun besetzen wir noch das C-Ereignis mit einem überflüssigen Signal-Handler: !block perl $button->signal_connect(clicked => sub { print "I was here!\n" }); !endblock H2: Vereinfachungen Das ganze Programm kann man (wenn man will) noch etwas einfacher haben. Zuersteinmal: mir geht dieses dauernde {{C:$xxx->show}} auf die Nerven. Weil es anderen wohl auch so ging, gibt es die Methode C. Sie ruft C für das Widget und, rekursiv, für alle seine Kinder auf. Man kann also alle (naja, zwei) C-Aufrufe durch ein einzelnes {{C:$main->show_all}} ersetzen. Die zweite Vereinfachung ist eher Geschmackssache: Man kann die Erzeugung des Widgets und alle Initialisierungen (z.B. Signale) in einem Aufruf erledigen: !block perl use Gtk; init Gtk; $main = new Gtk::Widget "Gtk::Window", -signal::delete_event => sub { Gtk->main_quit }; add $main (new Gtk::Widget "Gtk::Button", -label => "Hello, World!", -signal::clicked => sub { print "I was here!\n" }); $main->show_all; Gtk->main; !endblock H2: Verschönerungen Um noch ein paar Features vorzustellen sollen noch ein paar Knöpfe hinzukommen. Der "Hello-World"-Knopf soll in einen Rahmen. Unter den Rahmen sollen zwei Knöpfe: der erste soll den Text an- und abschalten, der zweite soll das Programm beenden: !import "hello-layout"; title="Schematisches Layout" !import "hello31"; title="Das Programm beim Start" !import "hello32"; title="Nach dem abschalten der Nachricht" Die Technik, Widgets an- und abzuschalten wird gerne für Dialoge in der Art "Advanced Options >>" verwendet, um dem Benutzer mehr Optionen anzubieten. Gtk+ bietet viele Arten von Layout-Managern. Grundsätzlich möchte es aber das Layouten des Fensters gerne selbst übernehmen. Man {{kann}} die Größe und Lage von Widgets selbst bestimmen, aber Gtk+ arbeitet wesentlich besser, wenn man nur Hinweise gibt. Ein solcher Hinweis kann in Form der C und C-Container geschehen. Diese sind Rechtecke, die mehrere Widgets enthalten können, die vertikal oder horizontal ausgerichtet werden. Das Hauptelement des Fensters soll also eine C werden. Oben soll eine C stehen, und im unteren Teil soll eine C mit den "Bedienelementen" sein (Eine C kann durchaus auch mehr als zwei Widgets enthalten). Die VBox wird einfach erzeugt mit: !block perl add $main (my $vbox = new Gtk::Widget "Gtk::VBox", homogeneous => 0, spacing => 5, border_width => 5); !endblock Eine C ist C, wenn alle Widgets die gleiche Größe haben. Unsere VBox soll das {{nicht}} sein, die C dagegen schon. Dann wird eine C erzeugt, in die das C mit dem "Hello, World!" gepackt wird (der Layoutvorgang wird auch "packen" genannt, da die Widgets dabei in den verfügbaren Raum "eingepackt werden"). !block perl # Message-Frame: add $vbox (my $frame = new Gtk::Frame "A Message:"); # Message-Label: add $frame (my $label = new Gtk::Label "Hello, World!"); !endblock Die horizontale Box für die Knöpfe wird mit einem vereinfachten Konstruktor (statt dem allgemeinen {{C:Gtk::Widget->new}}) erzeugt, der nur zwei Argumente ("homogen" und "Abstand") akzeptiert. !block perl add $vbox (my $hbox = new Gtk::HBox 1,5); !endblock Das Interessanteste an dem Programm ist zweifellos der Umschaltknopf, der die Nachricht an- und abschaltet. Dazu bedienen wir uns eines Cs, der zwei Zustände (aktiv und inaktiv) besitzt: !block perl add $hbox (new Gtk::Widget "Gtk::ToggleButton", label => "Message Shown", active => 1, signal::clicked => sub { $label->visible ? $label->hide : $label->show; }); !endblock Hier ist das gesamte Programm: !block perl use Gtk; init Gtk; $main = new Gtk::Widget "Gtk::Window", -signal::delete_event => sub { Gtk->main_quit }; add $main (my $vbox = new Gtk::Widget "Gtk::VBox", homogeneous => 0, spacing => 5, border_width => 5); # Message-Frame: add $vbox (my $frame = new Gtk::Frame "A Message:"); # Message-Label: add $frame (my $label = new Gtk::Label "Hello, World!"); # Button-Bar add $vbox (my $hbox = new Gtk::HBox 1,5); # Toggle-Button add $hbox (new Gtk::Widget "Gtk::ToggleButton", label => "Message Shown", active => 1, signal::clicked => sub { $label->visible ? $label->hide : $label->show; }); # Close Button add $hbox (new Gtk::Widget "Gtk::Button", label => "OK", signal::clicked => sub { main_quit Gtk }); $main->show_all; Gtk->main; !endblock H1: Eine Widget-Demo Als Abschluß folgt noch ein Screenshot, bei dem ich mich bemüht habe, die wichtigsten (schon verfügbaren) Widgets alle auf einen Bildschirm zu bekommen (das Gtk-Perl-Demoprogramm, C war sehr hilfreich ;) Natürlich kann man jederzeit auch eigene Widgets programmieren... !import "screenshot"; title="Der mega-unübersichtliche Screenshot"