ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/docs/pws2004/gtk2.pod
Revision: 1.10
Committed: Fri May 28 15:28:27 2004 UTC (22 years, 4 months ago) by root
Branch: MAIN
Changes since 1.9: +239 -10 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 root 1.1 =head1 Gtk+2 - ein erweiterbares GUI-Toolkit
2    
3     =head2 Abstract
4    
5 root 1.3 Gtk+2 (das {{G}}IMP {{T}}ool{{K}}it, Version 2) unterlief bei der
6     Entwicklung zu Version 2 tiefgreifende Änderungen. Das Typsystem wurde
7 root 1.9 stark abstrahiert, so daß sich Schnittstellen zu anderen Sprachen im
8 root 1.3 wesentlichen auf das Bereitstellen des Objektsystems beschränken.
9    
10 root 1.9 In diesem Dokument wird das Toolkit gtk+2 kurz vorgestellt, wobei
11     Erfahrung mit GUI-Programmierung vorausgesetzt wird. Im Wesentlichen geht
12     es darum, einige besondere Techniken vorzustellen sowie das Toolkit von
13     Perl und C aus selbst zu erweitern.
14 root 1.1
15 root 1.6 =head1 Gtk2 und Glib, eine Übersicht
16 root 1.1
17 root 1.3 =head2 Übersicht über die G-Module
18 root 1.1
19 root 1.3 Es gibt eine Menge Bibliotheken, deren Namen mit G anfängt und im
20     Dunstkreis von Gtk+ stehen. Vielleicht wäre es sinnvoll gewesen, eine neue
21     G-Hierarchie in Perl zu begründen, so muß man sich mit einer teilweise
22     nicht sehr logischen Namensgebung zurechtzufinden.
23    
24     =over 4
25    
26     =item Glib
27    
28     Glib ist die Schnittstelle zur C<libglib> (sowie zu C<libgobject>,
29     C<libgmodule> und C<libgthread>). Es bildet die Grundlage für das Objekt-
30     und Typ- und Signalsystem, bietet Hilfsfunktionen zum Umwandeln von
31     Dateinamen, Logging usf.
32    
33     =item Gtk2
34    
35     Dieses Modul ist eine Sammelstelle für diverse Bilbiotheken: neben
36     C<libgtk+> auch C<libgdk>, C<libgdk_pixbuf> und C<libpango>.
37    
38     Sie reicht für fast alle Anwendungen aus.
39    
40     =item Gtk2::GLExt, Gtk2::GladeXML, Gtk2::TrayIcon und Gtk2::Spell
41    
42     Bieten Integration von OpenGL, GladeXML, EggTryIcon und Spell-checking für
43     Text-Widgets.
44    
45     =item Gnome2
46    
47     Schnittstelle zur C<libgnome> und vielem mehr. Muss man nicht
48     benutzen, kann mir aber vorstellen, das sie für die Erstellung von
49     Gnome-Applikationen ganz nützlich sind :)
50    
51     =item Gnome2::Canvas, Gnome2::GConf, Gnome2::PanelApplet, Gnome2::Print,
52     Gnome2::Rsvg, Gnome2::VFS, Gnome2::Vte, Gnome2::Wnck.
53    
54 root 1.6 Jede Menge Gnome-Zeugs.
55 root 1.3
56     =back
57 root 1.1
58 root 1.6 =head2 Die Widgets von Gtk2 sind GObjects
59 root 1.1
60 root 1.3 Wie jedes andere Toolkit auch, bietet Gtk2 eine Menge vorgefertigter
61     I<Widgets>: I<Buttons>, I<Labels>, I<Frames> und andere "einfache"
62     Widgets, aber auch komplexe, wie das I<Text>- (komplett in Unicode,
63     einbettbare Objekte) oder das I<TreeView>- (Listen und Bäume im
64     MVC-Modell) Widget.
65    
66     =begin latex
67    
68     \begin{figure}[h]\includegraphics{gtk2-textwidget.png}\label{Das Text-Widget}\end{figure}
69    
70     =end latex
71    
72 root 1.6 Alle diese Widgets sind sogenannte I<GObjects> (davon abgeleitet sind
73     in Gtk2 dann sogenannte I<Widgets>, GUI-Objekte), und haben folgende
74     Features:
75 root 1.4
76     =over 4
77    
78     =item Eine Klasse
79    
80 root 1.6 Jedes GObject ist eine Instanz einer Klasse (C<GObjectClass>), eine
81     Datenstruktur, die Informationen über Methoden u.ä. speichert.
82    
83     Eine C<GObjectClass> ist allerdings nur ein Spezialfall eines
84     C<GType>s: Die Glib bildet ein komplettes Typsystem mit Klassen,
85     Aufzählungen, Flags, einfachen Datentypen wie Integer oder
86     Fließkommazahlen oder auch C-structs an. All dies ist zur Laufzeit in
87     einer Form verfügbar, in der sich auch neue - zur Compielzeit nicht
88     bekannte - Datentypen verwenden lassen.
89    
90     Schnittstellen für andere Sprachen werden dadurch einfach, da man
91     "lediglich" die Konzepte "Datentyp", "Konverter", "Klasse", "Methode",
92     "Bitset" usw. umsetzen muß. Konkrete Instanzen dieser Typen funktionieren
93     zur Laufzeit automatisch.
94    
95     Bei einer gut geschriebenen C-Bibliothek, die auf Glib bzw. GObject
96 root 1.10 aufsetzt, muß man eigentlich nur die Initialisierungsfunktion aufrufen,
97 root 1.6 die die Datentypen registriert, und kann dann sofort von Perl darauf
98     zugreifen - ohne eine einzige XS-Funktion geschrieben zu haben.
99 root 1.4
100     =item Methoden
101    
102     Jedes GObjekt hat mindestens Methoden zum Erzeugen (C<constructor>)
103 root 1.6 und Zerstören (C<finalize>), Setzen und Lesen von Properties und eine
104 root 1.4 Spezialität von GObject - C<dispose>. Letzteres ist eine Art Anfrage an
105     ein Objekt, sich freiwillig aufzulösen.
106    
107 root 1.6 Gtk2 fügt noch einige weitere hinzu, wie die C<destroy>-Methode, die aktiv
108     versucht, ein Objekt zu zerstören: In GUI-Programmen ist es manchmal
109     schwer, alle Referenzen im richtigen Moment aufzulösen, da sie häufig
110     zirkulär sind. Manche Objekte "leben" auch ganz eigenständig, z.B. wird
111     ein Toplevel-Fenster im allgemeinen vom Benutzer "referenziert", was sich
112     aber nicht notwendigerweise im Referenz-Zähler niederschlägt.
113    
114     Um solche Objekte loszuwerden, kann man sie bitten, sich zu
115     I<destroyen>. Dabei gibt es alle Referenzen auf andere Objekte frei und
116     bittet diese, sich ebenfalls zu destroyen. Dadurch werden zirkuläre
117     Referenzen effektiv aufgebrochen.
118    
119 root 1.7 Methoden kann man in Perl nicht direkt implementieren, dafür aber eigene
120     Signale, die beliebige Parameter oder Rückgabewerte besitzen können und
121     mehr oder weniger direkt von C oder anderen Sprachen aus aufgerufen werden
122     können.
123 root 1.6
124 root 1.4 =item Signale
125    
126 root 1.6 Signale ähneln Methoden, Callbacks oder sogenannten "Delegates". Wie
127     Methoden sind sie fest an eine Klasse gebunden und rufen (in C) eine
128     Methode der Klasse auf, sobald sie ausgelöst werden.
129    
130     Ein Signal kann von jedem ausgelöst werden, insbesondere lösen aber
131     Ereignisse (Events) in Gtk2 Signale aus. Der Signal-Handler eines Objektes
132     verarbeitet das Ereignis (und beendet die Signalauslieferung) oder tut
133     nichts, woraufhin Gtk2 ein anderes Objekt als Empfänger sucht.
134    
135     Zusätzlich kann man vor oder nach dem eigentlichen Signal-Handler
136     des Objektes weitere Callbacks einklinken. Z.B. löst das Text-Widget
137     beim Mausklick rechts ein C<populate-menu>-Signal aus, das
138     normalerweise ein Popup-Menü generiert. Das kann man verändern oder
139     ganz überschreiben. Und wenn man Mausklicks abfangen möchte, kann man
140     das C<button-press-event>-Signal überschreiben und so Mausklicks vom
141     Text-Widget fernhalten.
142    
143 root 1.4 =item Properties (Eigenschaften)
144    
145 root 1.6 Properties sind von der jeweiligen Klasse definierte Eigenschaften, die
146     das Objektverhalten beeinflussen.
147    
148 root 1.7 #TODO#
149    
150 root 1.6 =item Schlüssel-Wert-Paare
151    
152 root 1.7 #TODO#
153    
154 root 1.4 =back
155 root 1.3
156 root 1.9 =head2 Leben und sterben lassen: Wie man seine Objekte wieder loskriegt
157    
158     #TODO
159    
160     =head2 Informationen über Typen und Klassen zur Laufzeit abfragen
161    
162     Die Typinformationen, wie Werte von Aufzählungen, Namen von Datentypen,
163     Objekt-Signale, -Properties uvm., sind alle zur Laufzeit abfragbar.
164    
165     Dadurch wird das Schreiben von Klassenbrowsern oder RAD-Tools serh
166     einfach. Aber auch beim Entwickeln von Programmen ist das nützlich, da man nicht immer
167     die Dokumentation zu Rate ziehen muss.
168    
169     Z.B. liefert C<< $obj->list_properties >> (oder
170     C<Glib::Type::list_properties> Klassenname) eine Liste von Arrayrefs mit
171     Name, Typ, Flags und Beschreibung zurück, die man sich z.B. mit folgender
172     Funktion ausgeben lassen kann:
173    
174     use Gtk2;
175    
176     sub gobj_props {
177     for ($_[0]->list_properties) {
178     printf "%-16s %-24s %-24s %s\n", $_->{name}, $_->{type}, (join ":", @{$_->{flags}}), $_->{descr};
179     }
180     }
181    
182     Der Aufruf C<gobj_props "Gtk2::Widget"> (oder mit einem
183     C<Gtk2::Window>-Object als Argument) liefert folgende Properties, die alle
184     Gtk2-Widgets besitzen:
185    
186     user-data gpointer readable:writable
187     Anonymous User Data Pointer
188     name Glib::String readable:writable
189     The name of the widget
190     parent Gtk2::Container readable:writable
191     The parent widget of this widget. Must be a Container widget
192     width-request Glib::Int readable:writable
193     Override for width request of the widget, or -1 if natural request should be used
194     ...
195     extension-events Gtk2::Gdk::ExtensionMode readable:writable
196     The mask that decides what kind of extension events this widget gets
197     no-show-all Glib::Boolean readable:writable
198     Whether gtk_widget_show_all() should not affect this widget
199    
200     Signale lassen sich ebenso abfragen:
201    
202     sub gobj_signals($) {
203     # Signale haben Argumente und Parameter, daher Dumpvalue
204     use Dumpvalue;
205     Dumpvalue->new (bareStringify => 0)->dumpValue ($_)
206     for Glib::Type->list_signals ($_[0]);
207     }
208    
209     gobj_signals "Gtk2::Widget";
210    
211     ...
212     'signal_id' => 23
213     'signal_name' => 'button-press-event'
214     'itype' => 'Gtk2::Widget'
215     'param_types' => ARRAY(0x83b4f28)
216     0 'Gtk2::Gdk::Event'
217     'return_type' => 'Glib::Boolean'
218     'signal_flags' => [ run-last ]
219     -> 2
220     ...
221    
222     Das C<button-press-event>-Signal möchte also ein Event als einziges
223     Argument und liefert einen Wahrheitswert.
224    
225     Die Abstammung von C<Gtk2::Window> ist schnell gefunden:
226    
227     warn join " ", Glib::Type->list_ancestors ("Gtk2::Window");
228     Gtk2::Window Gtk2::Bin Gtk2::Container Gtk2::Widget
229     Gtk2::Object Glib::Object at gtk.pl line xx.
230    
231     # und list_interfaces wollte ich auch erwähnen:
232     warn join " ", Glib::Type->list_interfaces ("Gtk2::Window");
233     Gtk2::Atk::ImplementorIface at gtk.pl line xx.
234    
235     Wenn man gtk+ von C her kennt (oder eben die C-Dokumentation benutzt),
236     kann man aus dem C-Typnamen den Namen in Perl erfahren:
237    
238     warn Glib::Type->package_from_cname ("GtkWindowPosition");
239     Gtk2::WindowPosition at gtk.pl line xx.
240    
241     warn Glib::Type->package_from_cname ("GdkPixbuf");
242     Gtk2::Gdk::Pixbuf at gtk.pl line xx.
243    
244     Aufzählungen und Flags kann man ebenso hinterfragen:
245    
246     sub g_vals($) {
247     for (Glib::Type->list_values ($_[0])) {
248     printf "%-30s %s\n", $_->{name}, $_->{nick};
249     }
250     }
251    
252     g_vals "Gtk2::Gdk::EventMask";
253    
254     GDK_EXPOSURE_MASK exposure-mask
255     GDK_POINTER_MOTION_MASK pointer-motion-mask
256     ...
257     GDK_SCROLL_MASK scroll-mask
258     GDK_ALL_EVENTS_MASK all-events-mask
259    
260     =head3 Aufzählungen und Flags
261    
262     Natürlich kann man zur Laufzeit auch eigene Typen hinzufügen. Z.B. ein
263     neuer Flag-Typ:
264    
265     Glib::Type->register_flags (Meine::Flags =>
266     'eins', # implizit 1<<0
267     'zwei', # implizit 1<<1
268     ['drei' => 15 ], # explizit 15
269     ['vier' => 35 ], # explizit 35
270     'fuenf', # implizit 1<<4
271     );
272    
273     Diese neuen Typen sind gleichwertig zu den vorhandenen, können also von C
274     oder anderen Programmiersprachen aus benutzt werden.
275    
276     Aufzählungen und Flags sind in Perl überladen. Das ist besonders hilfreich
277     in Event-Handlern:
278    
279     $widget->signal_connect (button_press_event => sub {
280     if ($_[1]->type eq "button-press") {
281     if ($_[1]->button == 1) {
282     if ($_[1]->state == "shift-mask") {
283     # Umschalt-Klick
284     if ($_[1]->state * "shift-mask") { # oder '&'
285     # Umschalt-Klick (eventuell plus anderen Modifiern)
286     if ($_[1]->state * ["control-mask", "shift-mask"]) { # oder '&'
287     # Klick + Umschalt ODER Strg (eventuell plus anderen Modifieern)
288     if ($_[1]->state >= ["control-mask", "shift-mask"]) {
289     # Klick + Umschalt UND Strg (Übermenge)
290    
291     Oder beim Manipulieren von Event-Masken:
292    
293     $widget->set (events =>
294     $widget->get ('events') - [qw(key_press_mask button-motion-mask)]
295     );
296    
297     Auch eigene Klassen sind möglich, siehe Teil 2 :)
298 root 1.1
299 root 1.4
300     =head2 Beispiel: Session-Management
301 root 1.1
302 root 1.9 Häufig gibt es in C sogenannte I<convinience methods>
303     ("Bequemlichkeitsfunktionen"). Sofern sie sinnvoll sind, wurden sie
304     bei Glib und Gtk2 umgesetzt. So gibt es z.B. eine C<set_events>- und
305     eine C<add_events>-Methode, die bei Gtk2-Widgets die Event-Maske
306     verändert. Doch für alle Properties kann man die generischen C<set>- und
307     C<get>-Methoden verwenden.
308    
309     Damit läßt sich ein einfaches Session-Management schreiben:
310    
311     use Scalar::Util;
312    
313     my %get = (
314     window_size => sub { [ ($_[0]->allocation->values)[2,3] ] },
315     column_size => sub { $_[0]->get ("width") || $_[0]->get ("fixed_width") },
316     modelsortorder => sub { [ $_[0]->get_sort_column_id ] },
317     );
318    
319     my %set = (
320     window_size => sub { $_[0]->set_default_size (@{$_[1]}) },
321     column_size => sub { $_[0]->set (fixed_width => $_[1]) },
322     modelsortorder => sub { $_[0]->set_sort_column_id (@{$_[1]}) },
323     );
324    
325     my $state = Storable::retrieve "session.dat"; # Alte Session-Daten laden
326     my %widget;
327    
328     sub state {
329     my ($widget, $class, %attr) = @_;
330    
331     while (my ($k, $v) = each %attr) {
332     $v = $state->{$class}{$k}
333     if exists $state->{$class} && exists $state->{$class}{$k};
334    
335     $set{$k} ? $set{$k}->($widget, $v) : $widget->set ($k => $v);
336     }
337    
338     $widget{$widget} = [$widget, $class, \%attr];
339     Scalar::Util::weaken $widget{$widget}[0];
340     }
341    
342     Naja, I<gaanz> so einfach ist es nicht: Man kann damit shcon recht viel
343     anfangen, und es sind zugegebenermaßen mehr als drei Zeilen.
344    
345     Was macht der Code? Es gibt Eigenschaften, die man über eine Session
346     hinweg retten möchte, aber keine Properties sind. Für diese Eigenschaften
347     (z.B. C<window_size>) definiere ich eigene C<set>- und C<get>-Funktionen.
348    
349     Die Eigentliche Schnittstelle ist die C<state>-Funktion. Sie wird so benutzt:
350    
351     my $window = new Gtk2::Window;
352     state $window, "game::window", window_size => [600, 500];
353    
354     my $hpane = new Gtk2::HPaned;
355     state $hpane, "game::hpane", position => 500;
356    
357     Diese beiden Widgets werden in einem (nicht sehr fiktiven) Go-Programm
358     benutzt um ein Spiel darzustellen. Der erste Aufruf von C<state>
359     sagt: Beim Widget vom Typ C<game::window> (ein willkürlicher Name) soll
360     die Property C<window_size> von einer Session in die nächste Übertragen
361     werden. Falls keine Sessiondaten existieren, nimm als Default C<[600,
362     500]>. Der zweite Aufruf macht das gleiche für die C<Position>-Property
363     des C<Gtk2::HPaned>-Widgets.
364    
365     Beides Zusammen sorgt dafür, daß neue Spielfenster in der gleichen
366     Größe erscheinen wie bei der letzten Benutzung und daß die horizontale
367     Unterteiling (links Spielbrett, rechts Chat-Bereich) ebenfalls erhalten
368     bleibt.
369    
370     In der Realität benutze ich eine etwas erweiterte C<state>-Funktion mit
371     einem C<$instance>-Argument: manche Benutzer möchten z.B. verschiedene
372     Chat-Räume in verschieden großen Fenstern darstellen. Das zusätzliche
373     Argument dient der Unterscheidung: Wird ein neuer Raum betreten, werden
374     die Werte des Raum-Typs benutzt, wird ein bekannter Raum betreten, werden
375     dessen bekannte Werte benutzt.
376    
377     Die Funktion liest übrigens nur Werte aus den Sessiondaten C<$state> aus,
378     aber irgendwie müssen sie auch dort abgelegt werden. Dies geschieht in
379     meinen Programmen meistens durch Benutzeraufforderung: der Benutzer stellt
380     sein gewünschtes Layout zusammen und drückt dann C<Layout Speichern> o.ä.,
381     worauf dann folgende Funktion aufgerufen wird.
382    
383     sub save_state {
384     for (grep $_, values %widget) {
385     my ($widget, $class, $attr) = @$_;
386    
387     next unless $widget; # widget schon tot...
388     $widget->realize if $widget->can ("realize");
389    
390     while (my ($k, $v) = each %$attr) {
391    
392     $state->{$class}{$k} =
393     $get{$k} ? $get{$k}->($widget) : $widget->get ($k);
394     }
395     }
396    
397     Storable::nstore $state, "session.dat";
398     }
399    
400     Sie geht alle Widgets durch, die mit C<state> behandelt wurden, prüft, ob
401     sie noch existieren, sorgt (mit C<realize>) dafür, daß die Eigenschaften
402     auch mit sinnvollen Werten gefüllt sind, und speichert dann die
403     entsprechenden Eigenschaften in C<$state>.
404    
405     =head2 Beispiel: Rapid Application Development mit Glade - mein Weg
406    
407     Ich will es nicht verschweigen: Ich I<HASSE> GUI-Programmierung. Nichts
408     ist langweiliger bzw. nerviger, als Dialogboxen zusammenzustöpseln oder
409     Widgets in der Hierarchie zu verschieben.
410    
411     Glücklicherweise gibt es RAD-Tools, mit denen man sich seine Applikation
412     zusammenklicken kann. Was mich an RAD-Tools allerdings stört ist ihre
413     Starrheit: meistens wollen sie gleich den Quelltext schreiben, inklusive
414     des Hauptprogramms. Und häufig ist es schwierig, generelle Klassen zu
415     definieren, z.B. ein Chat-Room-Widget, von dem es zur Laufzeit beliebig
416     viele geben kann.
417    
418     Auch I<Glade>, ein (das!) Gtk+ und Gtk+2 RAD-Tool, ist keine Ausnahme
419     davon. Zum Glück kann man Glade auch etwas anders zum Entwickeln benutzen,
420     und diesen anderen Weg (ich kenne niemanden, der Glade so benutzt), möchte
421     ich hier beschreiben.
422    
423     Zuallererst ist es notwendig, die Widget oder die UI in Glade zu
424     erstellen und als GladeXML-Datei zu speichern. Z.B. habe ich für
425     ein Bilder-Anzeige-Programm (Gtk2::CV, reiner Beta-Code) einen
426     "Ausdrucken"-Dialog erstellt:
427    
428     =for latex
429     \begin{center}\includegraphics{freenet1.png}\end{center}
430    
431     Die Beschreibung ist in der XML-Datei C<cv.glade> gespeichert. Darin habe
432     ich den einzelnen Widgets Namen gegeben, z.B. heißt das Dialogfenster
433     C<PrintDialog>, das Option-Menü zum Auswählen der Papiergröße C<papersize>
434     usw.
435    
436     Den ganzen Dialog kapsele ich in einem Perl-Modul namens
437     C<Gtk2::CV::PrintDialog> ab, mit insgesamt 70 Zeilen und zwei Methoden.
438     Die kürzeste davon benutzt ein anderes Modul C<Gtk2::CV::PostScript>
439     zum Ausdrucken und ist völlig uninteressant :) Die längste ist die
440     C<new>-Methode, die ein neues PrintDialog-Widget erzeugt. Zuerst der
441     Standardkopf:
442    
443     use Gtk2;
444     use Gtk2::GladeXML;
445     # uvm.
446    
447     sub new {
448     my $class = shift;
449     my $self = bless { @_ }, $class;
450    
451     Als nächstes wird die GladeXML-Datei geladen:
452    
453     $self->{dialog} = my $d = new Gtk2::GladeXML ".../cv.glade", "PrintDialog";
454    
455     Mit C<PrintDialog> wird ein Teilbaum aus dem Widget-Baum geladen, und zwar
456     der mit dem C<PrintDialog>-Namen. So kann man viele verschiedene Dialoge
457     in einer Datei ablegen. Das Laden ist nicht unbedingt eine schnelle
458     Angelegenheit, ist aber in den meisten Fällen flott genug, um bei jedem
459     Aufruf von neuem gemacht zu werden (und man hat sowieso keine Wahl :)
460    
461     Das Ergebnis ist I<kein> Widget, sondern ein C<Gtk2::GladeXML>-Objekt, das
462     die Methode C<get_widget> besitzt, mit der man anhand des Namens einzelne
463     Widgets "herausgreifen" kann.
464    
465     Damit fülle ich den Dialog mit Leben:
466    
467     $d->get_widget ("destination")->set (text => $ENV{CV_PRINT_DESTINATION} || "| lpr");
468    
469     my $menu = $d->get_widget ("papersize")->get_menu;
470     for (Gtk2::CV::PostScript->papersizes) {
471     my ($code, $name, $w, $h) = @$_;
472     $menu->append (my $item = new Gtk2::MenuItem $name);
473     $item->set_name ($code);
474     }
475     $menu->show_all;
476     $d->get_widget ("papersize")->set_history (0);
477    
478     $d->get_widget ("PrintDialog")->signal_connect (close => sub {
479     $_[0]->destroy;
480     });
481     $d->get_widget ("PrintDialog")->signal_connect (response => sub {
482     if ($_[1] eq "ok") {
483     $self->print (
484     size => (Gtk2::CV::PostScript->papersizes)
485     [$d->get_widget ("papersize")->get_history],
486     margin => $d->get_widget ("margin")->get_value,
487     ...
488     destination => $d->get_widget ("destination")->get ("text"),
489     binary => $d->get_widget ("encoding_binary")->get ("active"),
490     );
491     }
492     $_[0]->destroy;
493     });
494    
495     return $self; # Ende
496     }
497    
498     Jetzt wird klar, warum das Ding C<$d> heißt und nicht einen längeren Namen trägt.
499    
500     Wer Das GladeXML-Modul kennt, weiß, daß es noch viel "einfacher" geht,
501     z.B. mit C<signal_autoconnect_from_package>. Leider weiß man in den
502     Callbacks, die diese Methode mit den Widgets verbindet, nichts mehr über
503     das ursprüngliche C<PrintDialog>-Objekt, so daß sie mir recht nutzlos
504     erscheint, wenn man nicht alles in globalen Variablen ablegen will (was
505     häufig der Fall ist).
506    
507     Daher habe ich die Funktion C<signal_autoconnect_all> geschrieben. Sie
508     geht einen etwas anderen Weg und erlaubt es, die Signal-Callbacks als
509     Closures anzugeben. Damit wäre das Namensraum-Problem gelöst. Leider
510     muss man dazu jedem Signal-Handler einen Namen geben, und das ist so
511     umständlich, daß ich die Funktion selbst nie benutzt habe und es einfach
512     "per Hand" Anhand der Widget-Namen mache.
513    
514     Dies alles heißt nicht, daß man es nicht einfacher anders besser machen
515     könnte. Es ist einfach "mein Weg", Glade zu benutzen. Und wenn ich noch
516     etwas darüber nachdenke, muß ich sagen, daß es mit Glade nicht wirklich
517     einfacher ist, Widgets umzustellen oder GUIs zu designen.
518    
519     *seufz*.
520    
521 root 1.1
522     =head1 Teil 2 - Gtk+ als solide Grundlage für Eigenentwicklungen
523    
524 root 1.8 #TODO
525    
526 root 1.1 =head2 Perl-Objekte vs. Glib-Objekte
527 root 1.8
528 root 1.10 Meiner Meinung nach ist der Hauptgrund für das typisch "skripthafte"
529     Aussehen vieler Perl/Tk (oder Perl/Gtk oder Perl/Qt oder auch Tcl/Tk)
530     Programme die ausschließliche Verwendung von Standard-Widgets: Die
531     Programme sehen aus wie aus dem Baukasten, weil sie nur aus
532     Baukastenteilen bestehen. "Richtige" Anwendungen (wie z.B. Gimp) benutzen
533     aber eigene Widgets, sofern das Sinn macht.
534    
535     Viele Toolkits machen es einem sehr schwer, eigene Widgets zu
536     programmieren. Bei Gtk2 kann man zwischen einfachen Ableiten und "echten"
537     Gtk2-Widgets wählen.
538    
539     =head3 Die Perl-Methode
540    
541     Die "Perl-Methode" besteht darin, eine neue Klasse von einem bestehenden
542     Widget abzuleiten. Das ist einfach und erfordert keinerlei Umdenken:
543    
544     package IntegerWidget;
545    
546     use base Gtk2::Entry;
547    
548     sub new {
549     my ($class, %prop) = @_;
550     my $self = $class->SUPER::new;
551    
552     $self->set_text ($prop{text});
553 root 1.8
554 root 1.10 $self->signal_connect (insert_text => sub {
555     my ($self, $text, $length, $position) = @_;
556     $text =~ y/0-9//cd;
557     ($text, $position);
558     });
559    
560     $self;
561     }
562    
563     ...
564    
565     $container->add (new IntegerWidget text => 50);
566    
567     Das geht mit fast jedem Toolkit. Aber ein richtig neues Widget kommt dabei
568     nicht heraus. Trotzdem ist dieses Vorgehen meistens nützlich, wenn man
569     eine große Menge an Widgets benötigt, die mehr oder weniger eine speziell
570     konfigurierte Version eines Standard-Widgets ist.
571    
572     Einige mehr oder minder wichtige Dinge kann man damit allerdings nicht
573     implementieren: Eigene Signale und Eigenschaften. Sicher kann man
574     eigene C<set_xxx>-Methoden schreiben, aber sie verhalten sich nicht wie
575     Glib-Properties (das Session-Management-Beispiel müßte speziell abgeändert
576     werden). Und Signale kann man durch Callbacks implementieren, aber der
577     Nutzer des Widgets muß dann auf C<signal_connect> verzichten und das
578     Widget verhält sich insgesamt nicht wie andere Widgets,
579    
580     Und schließlich kann man nur sehr schlecht selbst Zeichnen und auf
581     Ereignisse (z.B. C<Expose>) reagieren.
582    
583     =head3 Die Glib-Methode
584    
585     Mit der "Glib-Methode" wird keine normale Perl-Klasse erstellt, sondern
586     eine echte Glib-Klasse (und zusätzlich eine Perl-Klasse, aber eben keine
587     ganz normale).
588    
589     Dabei hilft einem das C<Glib::Object::Subclass>-Modul, das ähnlich wie ein
590     Pragma wirkt:
591    
592     package IntegerWidget;
593    
594     use Glib::Object::Subclass
595     Gtk2::Entry,
596     signals => {
597     insert_text => sub {
598     my ($self, $text, $length, $position) = @_;
599     $text =~ y/0-9//cd;
600     $self->signal_chain_from_overridden ($self, $text, $length, $position);
601     },
602     };
603    
604     C<Gtk2:Entry> ist die Basisklasse und mit C<signals> werden vorhandene
605     Signale überschrieben bzw. neue deklariert. Überschreibt man ein
606     vorhandenes Signal (wie hier) kann man das ursprüngliche Signal mit
607     C<signal_chain_from_overridden> aufrufen, was einem C<SUPER>-Aufruf in
608     Perl entspricht.
609    
610     Das Modul setzt C<@ISA> und liefert auch gleich einige Methoden,
611     z.B. C<new>. Der Grund liegt darin, daß die C<new>-Methoden der
612     existierenden C<Gtk2>-Widgets das Klassenargument ignorieren, wie auch die
613     C-Funktionen, die sie aufrufen, kein Klassenargument besitzen. Bei der
614     Umsetzung von gtk+ in Perl dachte man, die normalen Konstruktoren seien
615     wichtiger, da sie häufiger aufgerufen werden.
616    
617     Die von C<Glib::Object::Subclass> gelieferte C<new>-Methode ruft lediglich
618     C<Glib::Object::new> auf. Das ist der generische Objekt-Konstruktor,
619     mit dem man jedes Glib-Objekt (bzw. auch davon abgeleitete) erzeugen
620     kann. Er akzeptiert C<< property => wert >>-Paare, was für dieses Beispiel
621     ausreicht, man muss also nichts weiter implementieren.
622    
623     Interessant wird es, wenn man zusätzliche Properties und Signale registriert:
624    
625     package IntegerWidget;
626    
627     use Glib::Object::Subclass
628     Gtk2::Entry,
629     signals => {
630     # neues signal, range-exception
631     range_exception => {
632     class_closure => sub { <default-implementation> },
633     flags => [qw(run-first)],
634     param_types => [Glib::Integer],
635     },
636     # vorhandene Signale überschreiben
637     insert_text => sub {
638     ...
639     },
640     changed => sub {
641     my ($self) = @_;
642     my $text = $self->get_text;
643     $self->signal_emit (range_exception => $text)
644     if $text < $self->{min} || $text > $self->{max};
645     },
646     },
647     properties => [
648     Glib::ParamSpec->string (
649     min => "Minimum",
650     "Values smaller than this value cause a range_exception",
651     0, [qw(readable writable)]
652     ),
653     Glib::ParamSpec->string (
654     max => "Maximum",
655     "Values larger than this value cause a range_exception",
656     0, [qw(readable writable)]
657     ),
658     ],
659    
660     Hier wird ein neues Signal C<range_exception> eingeführt (das
661     standardmäßig garnichts macht). Es wird ausgelöst, wenn die eingegebene
662     Zahl kleiner oder größer als die Properties C<min> oder C<max> ist, was in
663     der C<changed>-Methode festgestellt wird.
664    
665     Wird eine Property gelesen, ruft Glib die C<GET_PROPERTY>-Funktion
666     auf. Sie ist in Grossbuchstaben geschrieben, weil sie nur
667     von Glib und nie vom eigenen Code aus aufgerufen wird,
668     ähnlich wie C<AUTOLOAD>. Genauso wird beim Verändern die
669     C<SET_PROPERTY>-Funktion aufgerufen. C<Glib::Object::Subclass> liefert zwei
670     Default-Implementationen, die die Werte aus C<$self->{name}> lesen bzw.
671     dort ablegen. Man kann diese Funktionen natürlich überschreiben:
672    
673     sub SET_PROPERTY {
674     my ($self, $pspec, $newval) = @_;
675     $self->{$pspec->get_name} = $newval;
676     # nicht so sinnvoll:
677     warn "max wurde geändert!!!" if $pspec->get_name eq "max";
678     # sehr sinnvoll:
679     $self->emit_signal ("changed");
680     }
681 root 1.3
682 root 1.10 Wenn C<min> oder C<max> geändert wird, löst diese Implementation ein
683     C<changed>-Signal aus, um die neuen Grenzen zu prüfen.
684    
685     Die großgeschriebenen Funtkionen sind übrigens genau das: Funktionen. Sie
686     sollten niemals ihre SUPER-Implementation aufrufen, das erledigt Glib
687     automatisch.
688    
689     Die beiden anderen Funktionen, die Glib aufruft sind C<INIT_INSTANCE>
690     und C<FINALIZE_INSTANCE>. Sie sollten das Objekt initialisieren bzw.
691     auflösen. I<Niemals> sollte man C<DESTROY> implementieren, da C<DESTROY>
692     intern von Glib benutzt wird und mehrmals aufgerufen werden kann.
693    
694     =head3 Referenzprobleme
695    
696     Jetzt zu einem kleinen aber feinem Problem. Was ist der Unterschied der
697     beiden folgenden INIT_INSTANCE-Funktionen?
698    
699     use Glib::Object::Subclass
700     Gtk2::Frame;
701    
702     sub INIT_INSTANCE {
703     my ($self) = @_;
704     $self->add ($self->{entry} = new Gtk2::Entry);
705     $self->{entry}->signal_connect (changed => sub {
706     warn $self->{entry}->get_text;
707     });
708     }
709    
710     sub INIT_INSTANCE {
711     my ($self) = @_;
712 root 1.1
713 root 1.10 $self->add ($self->{entry} = new Gtk2::Entry);
714     $self->{entry}->signal_connect (changed => sub {
715     warn $_[0]->get_text;
716     });
717     }
718 root 1.9
719 root 1.10 Nun, die obere Version erzeugt eine neue zirkuläre Referenz: Die Closure
720     im ersten Fall referenziert das äußere C<$self>. Wie löst man diese
721     Referenzen auf?
722    
723     Das größte Problem neuer Widgets sind zusätzliche Referenzen, die das
724     Widget daran hindern, sich aufzulösen.
725    
726     Gtk2 löst dieses Problem durch ein explizites C<destroy>-Signal: Wird es
727     ausgelöst, sollte das Objekt möglichst alle Referenzen auf andere Objekte
728     freigeben. Fällt dabei der Referenz-Zähler auf 0 ab, wird das Objekt
729     aufgelöst.
730    
731     Container lösen dabei rekursiv das C<destroy>-Signal auf die in ihnen
732     enthaltenen Objekte auf. Dabei werden normalerweise alle Referenzen
733     aufgelöst, da z.B. Signal-Handler ebenfalls entfernt werden, die
734     Selbstreferenz im obigen Beispiel ist dabei also eigentlich kein Problem.
735    
736     Viele Perl-Widgets sind aber versteckte Container, da man häufig
737     zusammengesetzte Widgets erzeugt, wie im obigen Beispiel. Dann kann es
738     passieren, das auch nach einem C<destroy> ein eingebettetes Widget eine
739     Referenz auf den Vater enthält, und der hält gleichermaßen eine Referenz
740     (C<$self->{entry}>).
741    
742     Dies kann man umgehen, indem man bei einem C<destroy> einfach %$self löscht:
743    
744     $self->signal_connect (destroy => sub { %{$_[0]} = () });
745    
746     Das ist etwas radikal, daher gibt es zwei sauberere Lösungen. Einmal kann
747     man bei einem C<destroy> wirklich sauber aufräumen:
748    
749     $self->signal_connect (destroy => sub {
750     (delete $self->{entry})->destroy;
751     # etc. für andere Widgets
752     });
753    
754     Oder man verhindert zusätzliche Referenzen auf Perl-Seite, was natürlich
755     nur bei einfachen Referenzen geht:
756    
757     use Scalar::Util ();
758     Scalar::Util::weaken $self->{entry};
759    
760     =head3 Beispiel: ein Go-Brett
761 root 1.1
762 root 1.10 Für einen Go-Server-Client benötige ich ein Widget, das ein Go-Spielbrett
763     darstellt (das Bild zeigt es beim Auszählen der Punkte eines Spiels).
764 root 1.9
765 root 1.10 =for latex
766     \begin{center}\includegraphics{freenet1.png}\end{center}
767 root 1.1
768 root 1.9
769 root 1.1
770 root 1.7 =head2 Objekte aus anderen Sprachen nutzen
771 root 1.1
772 root 1.9 #TODO
773    
774 root 1.7 =head1 Verweise
775 root 1.1
776 root 1.7 =over 4
777 root 1.1
778 root 1.7 =item http://www.gtk.org/
779 root 1.1
780 root 1.9 Die Dokumentation zu Gtk+, Glib usw.
781 root 1.10
782     =item Glib, Glib::Objectm Gtk2, Gtk2::<widgetname>
783    
784     Das Gtk2-Modul hat für jedes Widget eine eine C<perldoc>-Seite, die (kurz)
785     die Hierarchiezugehörigkeit, Methoden, Signale und Properties auflistet.
786 root 1.1
787 root 1.7 =item apt-get install devhelp-book-gtk2 # oder devhelp-books
788 root 1.1
789 root 1.7 Installiert unter debian I<devhelp>, einen inetraktiven Help-Browser und
790     das Gtk2-Buch (dazu gehört auch GObject, Glib-Doku uvm.). Sehr hilfreich
791     beim Programmieren.
792 root 1.9
793     =item Gtk2::PodViewer, Gtk2::GoBoard, Gtk2::CV
794    
795     Verschiedene Module, die Gtk2-Widgets implementieren.
796 root 1.1
797 root 1.7 =back
798 root 1.1
799