ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/docs/pws2004/gtk2.pod
Revision: 1.19
Committed: Mon Jun 28 17:12:45 2004 UTC (22 years, 3 months ago) by root
Branch: MAIN
CVS Tags: HEAD
Changes since 1.18: +2 -2 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.18 Gtk+2 (das B<G>IMP B<T>oolB<K>it, Version 2) durchlief bei der
6 root 1.3 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.18 =head2 Gtk2 und Glib, eine Übersicht
16 root 1.1
17 root 1.18 =head3 Ü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 root 1.11 Dieses Modul ist eine Sammelstelle für diverse Bibliotheken: neben
36 root 1.3 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 root 1.15 Bieten Integration von OpenGL, GladeXML, EggTrayIcon und Spell-checking für
43 root 1.3 Text-Widgets.
44    
45     =item Gnome2
46    
47 root 1.11 Schnittstelle zur C<libgnome> und vielem mehr.
48    
49     ... muß man nicht benutzen, kann mir aber vorstellen, daß sie für die
50     Erstellung von Gnome-Applikationen ganz nützlich sind :)
51 root 1.3
52 root 1.18 =for latex
53     \begin{sloppypar}
54    
55 root 1.3 =item Gnome2::Canvas, Gnome2::GConf, Gnome2::PanelApplet, Gnome2::Print,
56     Gnome2::Rsvg, Gnome2::VFS, Gnome2::Vte, Gnome2::Wnck.
57    
58 root 1.6 Jede Menge Gnome-Zeugs.
59 root 1.3
60 root 1.18 =for latex
61     \end{sloppypar}
62    
63 root 1.3 =back
64 root 1.1
65 root 1.18 =head3 Die Widgets von Gtk2 sind GObjects
66 root 1.1
67 root 1.3 Wie jedes andere Toolkit auch, bietet Gtk2 eine Menge vorgefertigter
68 root 1.18 I<Widgets>: I<Buttons>, I<Labels>, I<Frames> und andere ``einfache''
69 root 1.3 Widgets, aber auch komplexe, wie das I<Text>- (komplett in Unicode,
70     einbettbare Objekte) oder das I<TreeView>- (Listen und Bäume im
71     MVC-Modell) Widget.
72    
73 root 1.11 =for html
74     <img src="gtk2-textwidget.png" />
75 root 1.3
76 root 1.11 =for comment
77     Scheisslatex ignoriert natürlich alle Daten wie Auflösung.. grr...
78 root 1.3
79 root 1.11 =for latex
80     \begin{center}\includegraphics[scale=0.4]{gtk2-textwidget.png}\end{center}
81 root 1.3
82 root 1.18 =for perlpoint
83     \IMAGE{src="gtk2-textwidget.png"}
84    
85 root 1.6 Alle diese Widgets sind sogenannte I<GObjects> (davon abgeleitet sind
86     in Gtk2 dann sogenannte I<Widgets>, GUI-Objekte), und haben folgende
87     Features:
88 root 1.4
89     =over 4
90    
91     =item Eine Klasse
92    
93 root 1.6 Jedes GObject ist eine Instanz einer Klasse (C<GObjectClass>), eine
94     Datenstruktur, die Informationen über Methoden u.ä. speichert.
95    
96     Eine C<GObjectClass> ist allerdings nur ein Spezialfall eines
97     C<GType>s: Die Glib bildet ein komplettes Typsystem mit Klassen,
98     Aufzählungen, Flags, einfachen Datentypen wie Integer oder
99     Fließkommazahlen oder auch C-structs an. All dies ist zur Laufzeit in
100 root 1.11 einer Form verfügbar, in der sich auch neue - zur Compilezeit nicht
101 root 1.6 bekannte - Datentypen verwenden lassen.
102    
103     Schnittstellen für andere Sprachen werden dadurch einfach, da man
104 root 1.19 ``lediglich'' die Konzepte ``Datentyp'', ``Konverter'', ``Klasse'', ``Methode'',
105 root 1.18 ``Bitset'' usw. umsetzen muß. Konkrete Instanzen dieser Typen funktionieren
106 root 1.6 zur Laufzeit automatisch.
107    
108     Bei einer gut geschriebenen C-Bibliothek, die auf Glib bzw. GObject
109 root 1.10 aufsetzt, muß man eigentlich nur die Initialisierungsfunktion aufrufen,
110 root 1.6 die die Datentypen registriert, und kann dann sofort von Perl darauf
111     zugreifen - ohne eine einzige XS-Funktion geschrieben zu haben.
112 root 1.4
113     =item Methoden
114    
115     Jedes GObjekt hat mindestens Methoden zum Erzeugen (C<constructor>)
116 root 1.6 und Zerstören (C<finalize>), Setzen und Lesen von Properties und eine
117 root 1.4 Spezialität von GObject - C<dispose>. Letzteres ist eine Art Anfrage an
118     ein Objekt, sich freiwillig aufzulösen.
119    
120 root 1.6 Gtk2 fügt noch einige weitere hinzu, wie die C<destroy>-Methode, die aktiv
121     versucht, ein Objekt zu zerstören: In GUI-Programmen ist es manchmal
122     schwer, alle Referenzen im richtigen Moment aufzulösen, da sie häufig
123 root 1.18 zirkulär sind. Manche Objekte ``leben'' auch ganz eigenständig, z.B. wird
124     ein Toplevel-Fenster im allgemeinen vom Benutzer ``referenziert'', was sich
125 root 1.6 aber nicht notwendigerweise im Referenz-Zähler niederschlägt.
126    
127     Um solche Objekte loszuwerden, kann man sie bitten, sich zu
128     I<destroyen>. Dabei gibt es alle Referenzen auf andere Objekte frei und
129     bittet diese, sich ebenfalls zu destroyen. Dadurch werden zirkuläre
130     Referenzen effektiv aufgebrochen.
131    
132 root 1.7 Methoden kann man in Perl nicht direkt implementieren, dafür aber eigene
133     Signale, die beliebige Parameter oder Rückgabewerte besitzen können und
134     mehr oder weniger direkt von C oder anderen Sprachen aus aufgerufen werden
135     können.
136 root 1.6
137 root 1.4 =item Signale
138    
139 root 1.18 Signale ähneln Methoden, Callbacks oder sogenannten ``Delegates''. Wie
140 root 1.6 Methoden sind sie fest an eine Klasse gebunden und rufen (in C) eine
141     Methode der Klasse auf, sobald sie ausgelöst werden.
142    
143     Ein Signal kann von jedem ausgelöst werden, insbesondere lösen aber
144     Ereignisse (Events) in Gtk2 Signale aus. Der Signal-Handler eines Objektes
145     verarbeitet das Ereignis (und beendet die Signalauslieferung) oder tut
146     nichts, woraufhin Gtk2 ein anderes Objekt als Empfänger sucht.
147    
148     Zusätzlich kann man vor oder nach dem eigentlichen Signal-Handler
149     des Objektes weitere Callbacks einklinken. Z.B. löst das Text-Widget
150     beim Mausklick rechts ein C<populate-menu>-Signal aus, das
151     normalerweise ein Popup-Menü generiert. Das kann man verändern oder
152     ganz überschreiben. Und wenn man Mausklicks abfangen möchte, kann man
153     das C<button-press-event>-Signal überschreiben und so Mausklicks vom
154     Text-Widget fernhalten.
155    
156 root 1.4 =item Properties (Eigenschaften)
157    
158 root 1.6 Properties sind von der jeweiligen Klasse definierte Eigenschaften, die
159     das Objektverhalten beeinflussen.
160    
161 root 1.11 Sie werden von der Klasse festgelegt und auch von ihr benutzt.
162 root 1.7
163 root 1.6 =item Schlüssel-Wert-Paare
164    
165 root 1.11 Damit andere Benutzer mit einem Objekt Daten verknüpfen können, kann man
166     an ein GObject noch beliebige Schlüssel-Wert-Paare (sogar mit Destruktor)
167     packen, wobei sich dies von Perl aus auf Integer beschränkt, da das
168     Glib-Modul jedes GObject automatisch mit einem Perl-Hash verknüpft und
169     diesen auf Perl-Ebene zur Speicherung zur Verfügung stellt.
170 root 1.7
171 root 1.4 =back
172 root 1.3
173 root 1.18 =head3 Informationen über Typen und Klassen zur Laufzeit abfragen
174 root 1.9
175     Die Typinformationen, wie Werte von Aufzählungen, Namen von Datentypen,
176     Objekt-Signale, -Properties uvm., sind alle zur Laufzeit abfragbar.
177    
178 root 1.18 Dadurch wird das Schreiben von Klassenbrowsern oder RAD-Tools sehr
179 root 1.9 einfach. Aber auch beim Entwickeln von Programmen ist das nützlich, da man nicht immer
180     die Dokumentation zu Rate ziehen muss.
181    
182     Z.B. liefert C<< $obj->list_properties >> (oder
183     C<Glib::Type::list_properties> Klassenname) eine Liste von Arrayrefs mit
184     Name, Typ, Flags und Beschreibung zurück, die man sich z.B. mit folgender
185     Funktion ausgeben lassen kann:
186    
187     use Gtk2;
188    
189     sub gobj_props {
190     for ($_[0]->list_properties) {
191 root 1.11 printf "%-16s %-24s %-24s %s\n", $_->{name}, $_->{type},
192     (join ":", @{$_->{flags}}), $_->{descr};
193 root 1.9 }
194     }
195    
196     Der Aufruf C<gobj_props "Gtk2::Widget"> (oder mit einem
197     C<Gtk2::Window>-Object als Argument) liefert folgende Properties, die alle
198     Gtk2-Widgets besitzen:
199    
200     user-data gpointer readable:writable
201     Anonymous User Data Pointer
202     name Glib::String readable:writable
203     The name of the widget
204     parent Gtk2::Container readable:writable
205     The parent widget of this widget. Must be a Container widget
206     width-request Glib::Int readable:writable
207 root 1.11 Override for width request of the widget,
208     or -1 if natural request should be used
209 root 1.9 ...
210     extension-events Gtk2::Gdk::ExtensionMode readable:writable
211     The mask that decides what kind of extension events this widget gets
212     no-show-all Glib::Boolean readable:writable
213     Whether gtk_widget_show_all() should not affect this widget
214    
215     Signale lassen sich ebenso abfragen:
216    
217     sub gobj_signals($) {
218     # Signale haben Argumente und Parameter, daher Dumpvalue
219     use Dumpvalue;
220     Dumpvalue->new (bareStringify => 0)->dumpValue ($_)
221     for Glib::Type->list_signals ($_[0]);
222     }
223    
224     gobj_signals "Gtk2::Widget";
225    
226     ...
227     'signal_id' => 23
228     'signal_name' => 'button-press-event'
229     'itype' => 'Gtk2::Widget'
230     'param_types' => ARRAY(0x83b4f28)
231     0 'Gtk2::Gdk::Event'
232     'return_type' => 'Glib::Boolean'
233     'signal_flags' => [ run-last ]
234     -> 2
235     ...
236    
237     Das C<button-press-event>-Signal möchte also ein Event als einziges
238     Argument und liefert einen Wahrheitswert.
239    
240     Die Abstammung von C<Gtk2::Window> ist schnell gefunden:
241    
242     warn join " ", Glib::Type->list_ancestors ("Gtk2::Window");
243     Gtk2::Window Gtk2::Bin Gtk2::Container Gtk2::Widget
244     Gtk2::Object Glib::Object at gtk.pl line xx.
245    
246     # und list_interfaces wollte ich auch erwähnen:
247     warn join " ", Glib::Type->list_interfaces ("Gtk2::Window");
248     Gtk2::Atk::ImplementorIface at gtk.pl line xx.
249    
250     Wenn man gtk+ von C her kennt (oder eben die C-Dokumentation benutzt),
251     kann man aus dem C-Typnamen den Namen in Perl erfahren:
252    
253     warn Glib::Type->package_from_cname ("GtkWindowPosition");
254     Gtk2::WindowPosition at gtk.pl line xx.
255    
256     warn Glib::Type->package_from_cname ("GdkPixbuf");
257     Gtk2::Gdk::Pixbuf at gtk.pl line xx.
258    
259     Aufzählungen und Flags kann man ebenso hinterfragen:
260    
261     sub g_vals($) {
262     for (Glib::Type->list_values ($_[0])) {
263     printf "%-30s %s\n", $_->{name}, $_->{nick};
264     }
265     }
266    
267     g_vals "Gtk2::Gdk::EventMask";
268    
269     GDK_EXPOSURE_MASK exposure-mask
270     GDK_POINTER_MOTION_MASK pointer-motion-mask
271     ...
272     GDK_SCROLL_MASK scroll-mask
273     GDK_ALL_EVENTS_MASK all-events-mask
274    
275 root 1.18 =head4 Aufzählungen und Flags
276 root 1.9
277 root 1.18 Natürlich kann man zur Laufzeit auch eigene Typen hinzufügen. Z.B. einen
278     neuen Flag-Typ:
279 root 1.9
280     Glib::Type->register_flags (Meine::Flags =>
281     'eins', # implizit 1<<0
282     'zwei', # implizit 1<<1
283     ['drei' => 15 ], # explizit 15
284     ['vier' => 35 ], # explizit 35
285     'fuenf', # implizit 1<<4
286     );
287    
288     Diese neuen Typen sind gleichwertig zu den vorhandenen, können also von C
289     oder anderen Programmiersprachen aus benutzt werden.
290    
291     Aufzählungen und Flags sind in Perl überladen. Das ist besonders hilfreich
292     in Event-Handlern:
293    
294 root 1.18 $widget->signal_connect (button_press_event => sub {
295     if ($_[1]->type eq "button-press") {
296     if ($_[1]->button == 1) {
297     if ($_[1]->state == "shift-mask") {
298     # Umschalt-Klick
299     if ($_[1]->state * "shift-mask") { # oder '&'
300     # Umschalt-Klick (eventuell plus anderen Modifiern)
301     if ($_[1]->state * ["control-mask", "shift-mask"]) { # oder '&'
302     # Klick + Umschalt ODER Strg (eventuell plus anderen Modifieern)
303     if ($_[1]->state >= ["control-mask", "shift-mask"]) {
304     # Klick + Umschalt UND Strg (Übermenge)
305 root 1.9
306     Oder beim Manipulieren von Event-Masken:
307    
308     $widget->set (events =>
309     $widget->get ('events') - [qw(key_press_mask button-motion-mask)]
310     );
311    
312     Auch eigene Klassen sind möglich, siehe Teil 2 :)
313 root 1.1
314 root 1.4
315 root 1.18 =head3 Beispiel: Session-Management
316 root 1.1
317 root 1.9 Häufig gibt es in C sogenannte I<convinience methods>
318 root 1.18 (``Bequemlichkeitsfunktionen''). Sofern sie sinnvoll sind, wurden sie
319 root 1.9 bei Glib und Gtk2 umgesetzt. So gibt es z.B. eine C<set_events>- und
320     eine C<add_events>-Methode, die bei Gtk2-Widgets die Event-Maske
321 root 1.11 verändern. Doch für alle Properties kann man die generischen C<set>- und
322 root 1.9 C<get>-Methoden verwenden.
323    
324     Damit läßt sich ein einfaches Session-Management schreiben:
325    
326 root 1.18 use Scalar::Util;
327 root 1.9
328 root 1.18 my %get = (
329     window_size => sub { [ ($_[0]->allocation->values)[2,3] ] },
330     column_size => sub { $_[0]->get("width") || $_[0]->get("fixed_width")},
331     modelsortorder => sub { [ $_[0]->get_sort_column_id ] },
332     );
333    
334     my %set = (
335     window_size => sub { $_[0]->set_default_size (@{$_[1]}) },
336     column_size => sub { $_[0]->set (fixed_width => $_[1]) },
337     modelsortorder => sub { $_[0]->set_sort_column_id (@{$_[1]}) },
338     );
339    
340     my $state = Storable::retrieve "session.dat"; # Alte Session-Daten laden
341     my %widget;
342    
343     sub state {
344     my ($widget, $class, %attr) = @_;
345    
346     while (my ($k, $v) = each %attr) {
347     $v = $state->{$class}{$k}
348     if exists $state->{$class} && exists $state->{$class}{$k};
349    
350     $set{$k} ? $set{$k}->($widget, $v) : $widget->set ($k => $v);
351     }
352    
353     $widget{$widget} = [$widget, $class, \%attr];
354     Scalar::Util::weaken $widget{$widget}[0];
355     }
356 root 1.9
357 root 1.11 Naja, I<gaanz> so einfach ist es nicht: Man kann damit schon recht viel
358 root 1.9 anfangen, und es sind zugegebenermaßen mehr als drei Zeilen.
359    
360     Was macht der Code? Es gibt Eigenschaften, die man über eine Session
361     hinweg retten möchte, aber keine Properties sind. Für diese Eigenschaften
362     (z.B. C<window_size>) definiere ich eigene C<set>- und C<get>-Funktionen.
363    
364 root 1.11 Die eigentliche Schnittstelle ist die C<state>-Funktion. Sie wird so benutzt:
365 root 1.9
366     my $window = new Gtk2::Window;
367     state $window, "game::window", window_size => [600, 500];
368    
369     my $hpane = new Gtk2::HPaned;
370     state $hpane, "game::hpane", position => 500;
371    
372     Diese beiden Widgets werden in einem (nicht sehr fiktiven) Go-Programm
373     benutzt um ein Spiel darzustellen. Der erste Aufruf von C<state>
374     sagt: Beim Widget vom Typ C<game::window> (ein willkürlicher Name) soll
375     die Property C<window_size> von einer Session in die nächste Übertragen
376     werden. Falls keine Sessiondaten existieren, nimm als Default C<[600,
377     500]>. Der zweite Aufruf macht das gleiche für die C<Position>-Property
378     des C<Gtk2::HPaned>-Widgets.
379    
380 root 1.11 Beides zusammen sorgt dafür, daß neue Spielfenster in der gleichen
381 root 1.9 Größe erscheinen wie bei der letzten Benutzung und daß die horizontale
382     Unterteiling (links Spielbrett, rechts Chat-Bereich) ebenfalls erhalten
383     bleibt.
384    
385     In der Realität benutze ich eine etwas erweiterte C<state>-Funktion mit
386     einem C<$instance>-Argument: manche Benutzer möchten z.B. verschiedene
387     Chat-Räume in verschieden großen Fenstern darstellen. Das zusätzliche
388 root 1.11 Argument dient der Unterscheidung: wird ein neuer Raum betreten, werden
389     die Werte des Raum-Typs benutzt; wird ein bekannter Raum betreten, werden
390 root 1.9 dessen bekannte Werte benutzt.
391    
392     Die Funktion liest übrigens nur Werte aus den Sessiondaten C<$state> aus,
393     aber irgendwie müssen sie auch dort abgelegt werden. Dies geschieht in
394     meinen Programmen meistens durch Benutzeraufforderung: der Benutzer stellt
395     sein gewünschtes Layout zusammen und drückt dann C<Layout Speichern> o.ä.,
396     worauf dann folgende Funktion aufgerufen wird.
397    
398     sub save_state {
399     for (grep $_, values %widget) {
400     my ($widget, $class, $attr) = @$_;
401    
402     next unless $widget; # widget schon tot...
403     $widget->realize if $widget->can ("realize");
404    
405     while (my ($k, $v) = each %$attr) {
406    
407     $state->{$class}{$k} =
408     $get{$k} ? $get{$k}->($widget) : $widget->get ($k);
409     }
410     }
411    
412     Storable::nstore $state, "session.dat";
413     }
414    
415     Sie geht alle Widgets durch, die mit C<state> behandelt wurden, prüft, ob
416     sie noch existieren, sorgt (mit C<realize>) dafür, daß die Eigenschaften
417     auch mit sinnvollen Werten gefüllt sind, und speichert dann die
418     entsprechenden Eigenschaften in C<$state>.
419    
420 root 1.18 =head3 Beispiel: Rapid Application Development mit Glade -- mein Weg
421 root 1.9
422 root 1.11 Ich will es nicht verschweigen: Ich I<HASSE> (hoffentlich ist das deutlich
423     genug) GUI-Programmierung. Nichts ist langweiliger bzw. nerviger,
424     als Dialogboxen zusammenzustöpseln oder Widgets in der Hierarchie zu
425     verschieben.
426 root 1.9
427     Glücklicherweise gibt es RAD-Tools, mit denen man sich seine Applikation
428     zusammenklicken kann. Was mich an RAD-Tools allerdings stört ist ihre
429     Starrheit: meistens wollen sie gleich den Quelltext schreiben, inklusive
430     des Hauptprogramms. Und häufig ist es schwierig, generelle Klassen zu
431     definieren, z.B. ein Chat-Room-Widget, von dem es zur Laufzeit beliebig
432     viele geben kann.
433    
434 root 1.12 Auch I<Glade> -- ein (das!) Gtk+ und Gtk+2 RAD-Tool -- stellt hier keine
435 root 1.11 Ausnahme dar. Zum Glück kann man Glade auch etwas anders zum Entwickeln
436     benutzen, und diesen anderen Weg (ich kenne niemanden, der Glade so
437     benutzt), möchte ich hier beschreiben.
438 root 1.9
439     Zuallererst ist es notwendig, die Widget oder die UI in Glade zu
440     erstellen und als GladeXML-Datei zu speichern. Z.B. habe ich für
441 root 1.18 ein Bilder-Anzeige-Programm (C<Gtk2::CV>, reiner Beta-Code) einen
442 root 1.11 Ausdrucken-Dialog erstellt:
443    
444     =for html
445     <img src="gtk2-cv.png" />
446 root 1.9
447     =for latex
448 root 1.11 \begin{center}\includegraphics{gtk2-cv.png}\end{center}
449 root 1.9
450 root 1.18 =for perlpoint
451     \IMAGE{src="gtk2-cv.png"}
452    
453 root 1.9 Die Beschreibung ist in der XML-Datei C<cv.glade> gespeichert. Darin habe
454     ich den einzelnen Widgets Namen gegeben, z.B. heißt das Dialogfenster
455     C<PrintDialog>, das Option-Menü zum Auswählen der Papiergröße C<papersize>
456     usw.
457    
458     Den ganzen Dialog kapsele ich in einem Perl-Modul namens
459     C<Gtk2::CV::PrintDialog> ab, mit insgesamt 70 Zeilen und zwei Methoden.
460     Die kürzeste davon benutzt ein anderes Modul C<Gtk2::CV::PostScript>
461     zum Ausdrucken und ist völlig uninteressant :) Die längste ist die
462     C<new>-Methode, die ein neues PrintDialog-Widget erzeugt. Zuerst der
463     Standardkopf:
464    
465     use Gtk2;
466     use Gtk2::GladeXML;
467     # uvm.
468    
469     sub new {
470     my $class = shift;
471     my $self = bless { @_ }, $class;
472    
473     Als nächstes wird die GladeXML-Datei geladen:
474    
475 root 1.11 $self->{dialog} = my $d
476     = new Gtk2::GladeXML ".../cv.glade", "PrintDialog";
477 root 1.9
478     Mit C<PrintDialog> wird ein Teilbaum aus dem Widget-Baum geladen, und zwar
479     der mit dem C<PrintDialog>-Namen. So kann man viele verschiedene Dialoge
480     in einer Datei ablegen. Das Laden ist nicht unbedingt eine schnelle
481     Angelegenheit, ist aber in den meisten Fällen flott genug, um bei jedem
482     Aufruf von neuem gemacht zu werden (und man hat sowieso keine Wahl :)
483    
484     Das Ergebnis ist I<kein> Widget, sondern ein C<Gtk2::GladeXML>-Objekt, das
485     die Methode C<get_widget> besitzt, mit der man anhand des Namens einzelne
486 root 1.18 Widgets ``herausgreifen'' kann.
487 root 1.9
488     Damit fülle ich den Dialog mit Leben:
489    
490 root 1.18 $d->get_widget ("destination")->set (
491     text => $ENV{CV_PRINT_DESTINATION} || "| lpr");
492 root 1.9
493 root 1.18 my $menu = $d->get_widget ("papersize")->get_menu;
494     for (Gtk2::CV::PostScript->papersizes) {
495     my ($code, $name, $w, $h) = @$_;
496     $menu->append (my $item = new Gtk2::MenuItem $name);
497     $item->set_name ($code);
498     }
499     $menu->show_all;
500     $d->get_widget ("papersize")->set_history (0);
501    
502     $d->get_widget ("PrintDialog")->signal_connect (close => sub {
503     $_[0]->destroy;
504     });
505     $d->get_widget ("PrintDialog")->signal_connect (response => sub {
506     if ($_[1] eq "ok") {
507     $self->print (
508     size => (Gtk2::CV::PostScript->papersizes)
509     [$d->get_widget ("papersize")->get_history],
510     margin => $d->get_widget ("margin")->get_value,
511     ...
512     destination => $d->get_widget ("destination")->get ("text"),
513     binary => $d->get_widget ("encoding_binary")->get ("active"),
514     );
515 root 1.9 }
516 root 1.18 $_[0]->destroy;
517     });
518 root 1.9
519 root 1.18 return $self; # Ende
520     }
521 root 1.9
522     Jetzt wird klar, warum das Ding C<$d> heißt und nicht einen längeren Namen trägt.
523    
524 root 1.18 Wer das GladeXML-Modul kennt, weiß, daß es noch viel ``einfacher'' geht,
525 root 1.9 z.B. mit C<signal_autoconnect_from_package>. Leider weiß man in den
526     Callbacks, die diese Methode mit den Widgets verbindet, nichts mehr über
527     das ursprüngliche C<PrintDialog>-Objekt, so daß sie mir recht nutzlos
528     erscheint, wenn man nicht alles in globalen Variablen ablegen will (was
529 root 1.11 häufig ausreicht, aber nicht gerade sauber ist).
530 root 1.9
531     Daher habe ich die Funktion C<signal_autoconnect_all> geschrieben. Sie
532     geht einen etwas anderen Weg und erlaubt es, die Signal-Callbacks als
533     Closures anzugeben. Damit wäre das Namensraum-Problem gelöst. Leider
534     muss man dazu jedem Signal-Handler einen Namen geben, und das ist so
535     umständlich, daß ich die Funktion selbst nie benutzt habe und es einfach
536 root 1.18 ``per Hand'' anhand der Widget-Namen mache.
537 root 1.9
538     Dies alles heißt nicht, daß man es nicht einfacher anders besser machen
539 root 1.18 könnte. Es ist einfach ``mein Weg'', Glade zu benutzen. Und wenn ich noch
540 root 1.9 etwas darüber nachdenke, muß ich sagen, daß es mit Glade nicht wirklich
541     einfacher ist, Widgets umzustellen oder GUIs zu designen.
542    
543 root 1.18 *Seufz*.
544 root 1.9
545 root 1.1
546 root 1.18 =head2 Teil 2 - Gtk+ als solide Grundlage für Eigenentwicklungen
547 root 1.1
548 root 1.11 Gtk+ eignet sich hervorragend für Eigenentwicklungen - auch
549     größere. Belegt wird dies dadurch, daß man es vollständig von Perl aus
550     erweitern kann und nicht auf einige vorgefertigte Klassen beschränkt
551     ist. Außerdem kann man Widgets aus beliebigen Sprachen miteinander
552     integrieren und sogar Teile in Perl und einer anderen Sprache schreiben,
553 root 1.18 was große Anwendungen einfacher macht: zeitkritische Teile und Widgets
554 root 1.11 werden (z.B.) in C implementiert, während Perl bequem für die GUI-Logik
555     benutzt werden kann.
556 root 1.8
557 root 1.18 =head3 Perl-Objekte vs. Glib-Objekte
558 root 1.8
559 root 1.18 Meiner Meinung nach ist der Hauptgrund für das typisch ``skripthafte''
560 root 1.10 Aussehen vieler Perl/Tk (oder Perl/Gtk oder Perl/Qt oder auch Tcl/Tk)
561     Programme die ausschließliche Verwendung von Standard-Widgets: Die
562     Programme sehen aus wie aus dem Baukasten, weil sie nur aus
563 root 1.18 Baukastenteilen bestehen. ``Richtige'' Anwendungen (wie z.B. Gimp) benutzen
564 root 1.10 aber eigene Widgets, sofern das Sinn macht.
565    
566     Viele Toolkits machen es einem sehr schwer, eigene Widgets zu
567 root 1.18 programmieren. Bei Gtk2 kann man zwischen einfachen Ableiten und ``echten''
568 root 1.10 Gtk2-Widgets wählen.
569    
570 root 1.18 =head4 Die Perl-Methode
571 root 1.10
572 root 1.18 Die ``Perl-Methode'' besteht darin, eine neue Klasse von einem bestehenden
573 root 1.10 Widget abzuleiten. Das ist einfach und erfordert keinerlei Umdenken:
574    
575     package IntegerWidget;
576    
577     use base Gtk2::Entry;
578    
579     sub new {
580     my ($class, %prop) = @_;
581     my $self = $class->SUPER::new;
582    
583     $self->set_text ($prop{text});
584 root 1.8
585 root 1.10 $self->signal_connect (insert_text => sub {
586     my ($self, $text, $length, $position) = @_;
587     $text =~ y/0-9//cd;
588     ($text, $position);
589     });
590    
591     $self;
592     }
593    
594     ...
595    
596     $container->add (new IntegerWidget text => 50);
597    
598     Das geht mit fast jedem Toolkit. Aber ein richtig neues Widget kommt dabei
599     nicht heraus. Trotzdem ist dieses Vorgehen meistens nützlich, wenn man
600     eine große Menge an Widgets benötigt, die mehr oder weniger eine speziell
601     konfigurierte Version eines Standard-Widgets ist.
602    
603     Einige mehr oder minder wichtige Dinge kann man damit allerdings nicht
604 root 1.18 implementieren: eigene Signale und Eigenschaften. Sicher kann man
605 root 1.10 eigene C<set_xxx>-Methoden schreiben, aber sie verhalten sich nicht wie
606     Glib-Properties (das Session-Management-Beispiel müßte speziell abgeändert
607     werden). Und Signale kann man durch Callbacks implementieren, aber der
608     Nutzer des Widgets muß dann auf C<signal_connect> verzichten und das
609     Widget verhält sich insgesamt nicht wie andere Widgets,
610    
611 root 1.11 Und schließlich kann man nur sehr schlecht selbst zeichnen und auf
612 root 1.10 Ereignisse (z.B. C<Expose>) reagieren.
613    
614 root 1.18 =head4 Die Glib-Methode
615 root 1.10
616 root 1.18 Mit der ``Glib-Methode'' wird keine normale Perl-Klasse erstellt, sondern
617 root 1.10 eine echte Glib-Klasse (und zusätzlich eine Perl-Klasse, aber eben keine
618     ganz normale).
619    
620     Dabei hilft einem das C<Glib::Object::Subclass>-Modul, das ähnlich wie ein
621     Pragma wirkt:
622    
623 root 1.18 package IntegerWidget;
624 root 1.10
625 root 1.18 use Glib::Object::Subclass
626     Gtk2::Entry,
627     signals => {
628     insert_text => sub {
629     my ($self, $text, $length, $position) = @_;
630     $text =~ y/0-9//cd;
631 root 1.19 $self->signal_chain_from_overridden ($text, $length, $position);
632 root 1.18 },
633     };
634 root 1.10
635     C<Gtk2:Entry> ist die Basisklasse und mit C<signals> werden vorhandene
636     Signale überschrieben bzw. neue deklariert. Überschreibt man ein
637     vorhandenes Signal (wie hier) kann man das ursprüngliche Signal mit
638     C<signal_chain_from_overridden> aufrufen, was einem C<SUPER>-Aufruf in
639     Perl entspricht.
640    
641     Das Modul setzt C<@ISA> und liefert auch gleich einige Methoden,
642     z.B. C<new>. Der Grund liegt darin, daß die C<new>-Methoden der
643     existierenden C<Gtk2>-Widgets das Klassenargument ignorieren, wie auch die
644     C-Funktionen, die sie aufrufen, kein Klassenargument besitzen. Bei der
645     Umsetzung von gtk+ in Perl dachte man, die normalen Konstruktoren seien
646     wichtiger, da sie häufiger aufgerufen werden.
647    
648 root 1.18 =for latex
649     \begin{sloppypar}
650    
651 root 1.10 Die von C<Glib::Object::Subclass> gelieferte C<new>-Methode ruft lediglich
652     C<Glib::Object::new> auf. Das ist der generische Objekt-Konstruktor,
653     mit dem man jedes Glib-Objekt (bzw. auch davon abgeleitete) erzeugen
654     kann. Er akzeptiert C<< property => wert >>-Paare, was für dieses Beispiel
655     ausreicht, man muss also nichts weiter implementieren.
656    
657 root 1.18 =for latex
658     \end{sloppypar}
659    
660 root 1.10 Interessant wird es, wenn man zusätzliche Properties und Signale registriert:
661    
662     package IntegerWidget;
663    
664     use Glib::Object::Subclass
665     Gtk2::Entry,
666     signals => {
667     # neues signal, range-exception
668     range_exception => {
669 root 1.11 class_closure => sub { <default-implementierung> },
670 root 1.10 flags => [qw(run-first)],
671     param_types => [Glib::Integer],
672     },
673     # vorhandene Signale überschreiben
674     insert_text => sub {
675     ...
676     },
677     changed => sub {
678     my ($self) = @_;
679     my $text = $self->get_text;
680     $self->signal_emit (range_exception => $text)
681     if $text < $self->{min} || $text > $self->{max};
682     },
683     },
684     properties => [
685     Glib::ParamSpec->string (
686     min => "Minimum",
687     "Values smaller than this value cause a range_exception",
688     0, [qw(readable writable)]
689     ),
690     Glib::ParamSpec->string (
691     max => "Maximum",
692     "Values larger than this value cause a range_exception",
693     0, [qw(readable writable)]
694     ),
695     ],
696    
697     Hier wird ein neues Signal C<range_exception> eingeführt (das
698     standardmäßig garnichts macht). Es wird ausgelöst, wenn die eingegebene
699     Zahl kleiner oder größer als die Properties C<min> oder C<max> ist, was in
700     der C<changed>-Methode festgestellt wird.
701    
702     Wird eine Property gelesen, ruft Glib die C<GET_PROPERTY>-Funktion
703     auf. Sie ist in Grossbuchstaben geschrieben, weil sie nur
704     von Glib und nie vom eigenen Code aus aufgerufen wird,
705     ähnlich wie C<AUTOLOAD>. Genauso wird beim Verändern die
706     C<SET_PROPERTY>-Funktion aufgerufen. C<Glib::Object::Subclass> liefert zwei
707 root 1.11 Default-Implementierung, die die Werte aus C<$self->{name}> lesen bzw.
708 root 1.10 dort ablegen. Man kann diese Funktionen natürlich überschreiben:
709    
710     sub SET_PROPERTY {
711     my ($self, $pspec, $newval) = @_;
712     $self->{$pspec->get_name} = $newval;
713     # nicht so sinnvoll:
714     warn "max wurde geändert!!!" if $pspec->get_name eq "max";
715     # sehr sinnvoll:
716     $self->emit_signal ("changed");
717     }
718 root 1.3
719 root 1.18 Wenn C<min> oder C<max> geändert werden, löst diese Implementierung ein
720 root 1.10 C<changed>-Signal aus, um die neuen Grenzen zu prüfen.
721    
722     Die großgeschriebenen Funtkionen sind übrigens genau das: Funktionen. Sie
723 root 1.11 sollten niemals ihre SUPER-Implementierung aufrufen, das erledigt Glib
724 root 1.10 automatisch.
725    
726 root 1.18 =for latex
727     \begin{sloppypar}
728    
729 root 1.10 Die beiden anderen Funktionen, die Glib aufruft sind C<INIT_INSTANCE>
730     und C<FINALIZE_INSTANCE>. Sie sollten das Objekt initialisieren bzw.
731     auflösen. I<Niemals> sollte man C<DESTROY> implementieren, da C<DESTROY>
732     intern von Glib benutzt wird und mehrmals aufgerufen werden kann.
733    
734 root 1.18 =for latex
735     \end{sloppypar}
736    
737     =head4 Referenzprobleme
738 root 1.10
739 root 1.18 Jetzt zu einem kleinen aber feinen Problem. Was ist der Unterschied der
740 root 1.10 beiden folgenden INIT_INSTANCE-Funktionen?
741    
742     use Glib::Object::Subclass
743     Gtk2::Frame;
744    
745     sub INIT_INSTANCE {
746     my ($self) = @_;
747     $self->add ($self->{entry} = new Gtk2::Entry);
748     $self->{entry}->signal_connect (changed => sub {
749     warn $self->{entry}->get_text;
750     });
751     }
752    
753     sub INIT_INSTANCE {
754     my ($self) = @_;
755 root 1.1
756 root 1.10 $self->add ($self->{entry} = new Gtk2::Entry);
757     $self->{entry}->signal_connect (changed => sub {
758     warn $_[0]->get_text;
759     });
760     }
761 root 1.9
762 root 1.10 Nun, die obere Version erzeugt eine neue zirkuläre Referenz: Die Closure
763     im ersten Fall referenziert das äußere C<$self>. Wie löst man diese
764     Referenzen auf?
765    
766     Das größte Problem neuer Widgets sind zusätzliche Referenzen, die das
767     Widget daran hindern, sich aufzulösen.
768    
769     Gtk2 löst dieses Problem durch ein explizites C<destroy>-Signal: Wird es
770     ausgelöst, sollte das Objekt möglichst alle Referenzen auf andere Objekte
771     freigeben. Fällt dabei der Referenz-Zähler auf 0 ab, wird das Objekt
772     aufgelöst.
773    
774     Container lösen dabei rekursiv das C<destroy>-Signal auf die in ihnen
775     enthaltenen Objekte auf. Dabei werden normalerweise alle Referenzen
776     aufgelöst, da z.B. Signal-Handler ebenfalls entfernt werden, die
777     Selbstreferenz im obigen Beispiel ist dabei also eigentlich kein Problem.
778    
779     Viele Perl-Widgets sind aber versteckte Container, da man häufig
780     zusammengesetzte Widgets erzeugt, wie im obigen Beispiel. Dann kann es
781     passieren, das auch nach einem C<destroy> ein eingebettetes Widget eine
782     Referenz auf den Vater enthält, und der hält gleichermaßen eine Referenz
783 root 1.14 (C<< $self->{entry} >>).
784 root 1.10
785     Dies kann man umgehen, indem man bei einem C<destroy> einfach %$self löscht:
786    
787     $self->signal_connect (destroy => sub { %{$_[0]} = () });
788    
789     Das ist etwas radikal, daher gibt es zwei sauberere Lösungen. Einmal kann
790     man bei einem C<destroy> wirklich sauber aufräumen:
791    
792     $self->signal_connect (destroy => sub {
793     (delete $self->{entry})->destroy;
794     # etc. für andere Widgets
795     });
796    
797     Oder man verhindert zusätzliche Referenzen auf Perl-Seite, was natürlich
798     nur bei einfachen Referenzen geht:
799    
800     use Scalar::Util ();
801     Scalar::Util::weaken $self->{entry};
802    
803 root 1.18 =head4 Beispiel: ein Go-Brett
804 root 1.1
805 root 1.10 Für einen Go-Server-Client benötige ich ein Widget, das ein Go-Spielbrett
806     darstellt (das Bild zeigt es beim Auszählen der Punkte eines Spiels).
807 root 1.9
808 root 1.11 =for html
809     <img src="gtk2-kgsueme.png" />
810    
811 root 1.10 =for latex
812 root 1.11 \begin{center}\includegraphics[scale=0.3]{gtk2-kgsueme.png}\end{center}
813 root 1.1
814 root 1.18 =for perlpoint
815     \IMAGE{src="gtk2-kgsueme.png"}
816    
817 root 1.11 Der Code für Board-Widget fängt so an:
818    
819     use Glib::Object::Subclass
820     Gtk2::AspectFrame,
821     properties => [
822     Glib::ParamSpec->IV (
823     "size",
824     "Board Size",
825     "The Go Board size, 2..38",
826     2, 38, 19,
827     [qw(construct-only writable readable)],
828     ),
829     Glib::ParamSpec->IV (
830     "cursor-mask",
831     "cursor mask",
832     "The mask used to show a cursor",
833     0, 1<<30, 0,
834     [qw(writable readable)],
835     ),
836     Glib::ParamSpec->IV (
837     "cursor-value",
838     "cursor value",
839     "The value used to show a cursor",
840     0, 1<<30, 0,
841     [qw(writable readable)],
842     ),
843     ],
844     signals => {
845     "button-press" => {
846     flags => [qw/run-first/],
847     return_type => undef, # void return
848     param_types => [Glib::Int, Glib::Int, Glib::Int],
849     },
850     "button-release" => {
851     flags => [qw/run-first/],
852     return_type => undef, # void return
853     param_types => [Glib::Int, Glib::Int, Glib::Int],
854     },
855     destroy => sub {
856     $_[0]->signal_chain_from_overridden;
857     %{$_[0]} = ();
858     },
859     };
860    
861     Das Brett wird also von einer C<Gtk2::AspectFrame> abgeleitet: Das Brett
862     ist zwar nicht ganz quadratisch, hat aber immer dasselbe Seitenverhältnis
863 root 1.18 von ca. 11:10, so daß sich ein C<AspectFrame> anbietet.
864 root 1.11
865     Als erstes werden drei Properties definiert: die Größe (C<size>), die die
866     Anzahl der Linienschnittpunkte angibt. Sie kann nur beim Erzeugen gesetzt
867     werden (Flag C<construct-only>), nicht mehr später. Dies vereinfacht den
868     Code etwas. Die beiden anderen Properties definieren das Aussehen des
869 root 1.18 ``Cursors'': wenn man mit der Maus über das Brett fährt, kann man damit z.B.
870 root 1.11 einen halbdurchscheinenden Stein anzeigen.
871    
872     Dann folgen zwei neue Signale, die ausgelöst werden, wenn man
873     einen Mausknopf auf dem Brett drückt bzw. losläßt. Sie haben drei
874     Integer-Parameter: Die Nummer des Mausknopfes und die Koordinaten des
875     Klicks.
876    
877 root 1.18 =for latex
878     \begin{sloppypar}
879    
880 root 1.11 Das C<destroy>-Signal wird überschrieben, das C<%$self> brutal löscht. Da
881     ich von C<Gtk2::AspectFrame> ableite weiß ich, daß ich kein anderes
882     Perl-Widget in der Hierarchie über mir stören kann, da Gtk2-Widgets selbst
883     den Perl-Hash C<%$self> nicht antasten.
884    
885 root 1.18 =for latex
886     \end{sloppypar}
887    
888 root 1.11 Danach folgen einige Konstantendefinitionen für Steine, z.B. C<MARK_B>
889     oder C<MARK_SQUARE>, die zusammenaddiert das Aussehen einer Brettposition
890     festlegen oder die relative Größe von Steinen (schwarze Steine sind etwas
891     größer) sowie Funktionen zum Laden von Bildern.
892    
893     Interessanter wird dann die C<INIT_INSTANCE>-Funktion:
894    
895     sub INIT_INSTANCE {
896     my $self = shift;
897    
898     @::black_img
899     or load_images;
900    
901     $self->double_buffered (0);
902     $self->set (border_width => 0, shadow_type => 'none',
903     obey_child => 0, ratio => TRAD_RATIO);
904     $self->set(cursor_mask => MARK_B | MARK_W,
905     cursor_value => MARK_B | MARK_GRAYED);
906    
907     $self->add ($self->{canvas} = new Gtk2::DrawingArea);
908    
909     $self->{canvas}->signal_connect (
910     configure_event => sub { $self->configure_event ($_[1]) });
911     $self->{canvas}->signal_connect (
912     motion_notify_event => sub { $self->motion });
913     $self->{canvas}->signal_connect (
914     leave_notify_event => sub { $self->cursor (0) });
915     $self->{canvas}->signal_connect (
916     button_press_event => sub { $self->button ("press", $_[1]) });
917     $self->{canvas}->signal_connect (
918     button_release_event => sub { $self->button ("release", $_[1]) });
919    
920     $self->{canvas}->set_events ([
921     @{ $self->{canvas}->get_events },
922     'leave-notify-mask',
923     'button-press-mask',
924     'button-release-mask',
925     'pointer-motion-mask',
926     'pointer-motion-hint-mask'
927     ]);
928     }
929    
930     Hier wird eine C<Gtk2::DrawingArea> eingebettet, die das eigentliche
931     Bild darstellt. Eine C<Gtk2::DrawingArea> ist eine Art leeres Widget,
932     das selbst nichts zeichnet, aber an der Größenbelegung teilnimmt und eine
933     hervorragende Grundlage für eigene Widgets ist.
934    
935     Das Go-Widget verwendet einen Trick: Die Brett-Grafik befindet sich
936     im X-Server (Windows: keine Ahnung) und wird automatisch vom Server
937     gezeichnet, das Widget muss also keine C<Expose>-Events behandeln, sondern
938 root 1.18 ``nur'' zeichnen. Als Beispiel für ein Widget, das C<Expose>-Events selbst
939 root 1.11 behandelt, sei C<Gtk2::CV::Schnauzer> genannt, das ein komplettes Widget
940     inklusive Size-Allocation und Expose in Perl implementiert.
941    
942     Als nächstes die C<SET_PROPERTY>-Funktion:
943    
944     sub SET_PROPERTY {
945     my ($self, $pspec, $newval) = @_;
946    
947     $pspec = $pspec->get_name;
948    
949     $self->cursor (0) if $pspec =~ /^cursor/;
950     $self->{$pspec} = $newval;
951     $self->cursor (1) if $pspec =~ /^cursor/;
952     }
953    
954     Sie macht das gleiche wie die normale C<SET_PROPERTY>-Funktion, nur bei
955     den Cursor-Attributen wird der Cursor neugezeichnet (was die Funktion
956     C<cursor> erledigt).
957    
958     Dann folgt der Handler für das C<configure-notify-event>-Signal, das
959     erzeugt wird, wenn sich Größe oder Position des Widgets ändern:
960    
961 root 1.18 sub configure_event {
962     my ($self, $event) = @_;
963 root 1.11
964 root 1.18 $self->{window} = $self->{canvas}->window;
965 root 1.11
966 root 1.18 my $drawable = $self->{window};
967 root 1.11
968 root 1.18 $drawable->set_back_pixmap (undef, 0);
969 root 1.11
970 root 1.18 $self->{idle} ||= add Glib::Idle sub {
971     delete $self->{stack};
972    
973     $self->{width} = $self->{canvas}->allocation->width;
974     $self->{height} = $self->{canvas}->allocation->height;
975     $self->draw_background;
976    
977     $self->draw_board (delete $self->{board}, 0) if $self->{board};
978     $self->{window}->clear_area (0, 0, $self->{width}, $self->{height});
979    
980     delete $self->{idle}; # Handler lief
981     0; # Lösche Handler
982     };
983 root 1.11
984 root 1.18 1;
985     }
986 root 1.11
987     Sie löscht die Background-Pixmap, da sie nicht mehr gültig ist und
988     neugezeichnet wird. Da das initiale Zeichnen des Brettes clientseitig
989     geschieht und das Zeichnen und der Transfer zum Server einige Zeit in
990     Anspruch nimmt, geschieht es nicht synchron zum Signal, sondern später,
991     in einem Idle-Handler. Dieser wird aufgerufen, wenn die Hauptschleife von
992     Gtk2 alle anstehenden Ereignisse abgearbeitet hat.
993    
994     Das Gdk-Fenster, das man mit C<< $self->{canvas}->window >> erhält, kann
995     erst hier abgefragt werden und nicht schon in C<INIT_INSTANCE>, da zum
996     Zeitpunkt von C<INIT_INSTANCE> noch keine Gdk-Fenster für die Widgets
997 root 1.18 erzeugt wurden sondern erst, nachdem sie das erste mal angezeigt wurden
998 root 1.11 (bzw. etwas früher, wenn sie I<realized> werden).
999    
1000     Im Idle-Handler werden noch andere Daten invalidiert (C<< $self->{stack}
1001     >> speichert die vorberechneten Steine), die neue Größe abgefragt und der
1002     Bretthintergrund gezeichnet sowie die Steine gemalt und gesetzt.
1003    
1004     Am Ende wird dem X-Server mit C<clear_area> mitgeteilt, daß er die Grafik
1005     auffrischen soll.
1006    
1007 root 1.16 Danach folgen einige hundert Zeilen Code, der die Grafik zeichnet (und
1008     dazu im wesentlichen C<Gtk2::Gdk::Pixbuf> benutzt). Und dann:
1009 root 1.11
1010     sub do_button_press {
1011     my ($self, $button, $x, $y) = @_;
1012     }
1013    
1014     sub do_button_release {
1015     my ($self, $button, $x, $y) = @_;
1016     }
1017    
1018     Als der Code geschrieben wurde, gab es noch kein C<class_closure>-Feld für
1019     Signale und Signale wurden immer als Perl-Methoden aufgerufen, mit einem
1020 root 1.18 vorangestellten C<do_> (``weil Python das auch so macht'' :).
1021 root 1.11
1022     Und abschließend noch die Methode, die die Mausklick-Signale auslöst:
1023    
1024 root 1.18 sub button {
1025     my ($self, $type, $event) = @_;
1026 root 1.11
1027 root 1.18 $self->motion;
1028 root 1.11
1029 root 1.18 if ($self->{cursorpos}) {
1030     $self->signal_emit ("button-$type", $event->button, @{$self->{cursorpos}});
1031     }
1032     }
1033 root 1.11
1034     C<< $self->motion >> bewegt den Cursor, falls das notwendig ist, und,
1035     falls die Maus über einer gültigen Brettposition stelt, wird ein Signal
1036     ausgelöst.
1037    
1038     Wenn man so wollte, könnte man von C/C++/XS aus jetzt ein neues Go-Brett
1039     erzeugen:
1040    
1041     GtkWidget *w = g_object_new (
1042     "Gtk2__GoBoard", # ':' wird zu '_'
1043     "size", 19
1044     );
1045    
1046 root 1.14 ... und im wesentlichen so benutzen wie jedes andere Gtk2-Widget.
1047 root 1.9
1048 root 1.18 =head3 Objekte aus anderen Sprachen nutzen
1049 root 1.1
1050 root 1.11 Obwohl es eine aufregende Idee wäre, Python-Widgets in Perl und umgekehrt
1051     zu benutzen, ist dies eher unrealistisch: man müßte zuerst Python und
1052     Perl im selben Prozess vereinigen, was wahrscheinlich möglich ist, aber
1053     kostspielig.
1054    
1055     Deshalb beschränke ich mich auf Widgets/Objects aus C oder C++, die
1056     Vorgehensweise ist aber übertragbar auf andere Sprachen.
1057    
1058     Dazu muß man zwei Schritte durchführen: Erstens die Bibliothek oder
1059     den Code in den Prozess linken, zweitens die C<GType>s beim Glib-Modul
1060     registrieren.
1061    
1062     Am einfachsten (bzw. portabelsten) geschieht dies mit einem
1063     kleinen XS-Modul. Möchte man z.B. den C<GimpColorButton> aus der
1064 root 1.14 C<libgimpwidgets>, schreibt man ein kleines Modul (oder benutzt
1065     C<DynaLoader> und C<Inline::C>), das man gegen die C<libgimpwidgets>
1066     linkt.
1067 root 1.11
1068     In dessen BOOT-Section schreibt man:
1069    
1070     BOOT:
1071     gperl_register_object (GIMP_TYPE_COLOR_BUTTON, "Gimp::ColorButton");
1072    
1073 root 1.18 =for latex
1074     \begin{sloppypar}
1075    
1076 root 1.11 Damit ist die Verbindung zwischen C und Perl hergestellt, und man kann den
1077 root 1.14 C<Gimp::ColorButton> sofort benutzen:
1078 root 1.11
1079 root 1.18 =for latex
1080     \end{sloppypar}
1081    
1082 root 1.11 use MeinXSModul;
1083    
1084     my $button = Gimp::ColorButton->Glib::Object::new (...);
1085    
1086     Ähnlich verfährt man mit anderen Datentypen (z.B. Flags und Aufzählungen),
1087     die man registrieren möchte:
1088    
1089     gperl_register_fundamental (GDK_TYPE_CAP_STYLE, "Gtk2::Gdk::CapStyle");
1090    
1091 root 1.18 Sobald sie registriert sind, kann man sie wie gewohnt von Perl aus nutzen.
1092 root 1.11
1093 root 1.9
1094 root 1.18 =head2 Verweise
1095 root 1.1
1096 root 1.7 =over 4
1097 root 1.1
1098 root 1.18 =item * http://www.gtk.org/
1099 root 1.1
1100 root 1.9 Die Dokumentation zu Gtk+, Glib usw.
1101 root 1.10
1102 root 1.18 =item * perldoc Glib, Glib::Object, Gtk2, Gtk2::<widgetname>
1103 root 1.10
1104     Das Gtk2-Modul hat für jedes Widget eine eine C<perldoc>-Seite, die (kurz)
1105     die Hierarchiezugehörigkeit, Methoden, Signale und Properties auflistet.
1106 root 1.1
1107 root 1.18 =item * apt-get install devhelp-book-gtk2 # oder devhelp-books
1108 root 1.1
1109 root 1.11 Installiert unter Debian I<devhelp>, einen interaktiven Help-Browser und
1110 root 1.7 das Gtk2-Buch (dazu gehört auch GObject, Glib-Doku uvm.). Sehr hilfreich
1111     beim Programmieren.
1112 root 1.9
1113 root 1.18 =item * Gtk2::PodViewer, Gtk2::GoBoard, Gtk2::CV::Schnauzer
1114 root 1.9
1115 root 1.11 Verschiedene Gtk2-Widgets in Perl.
1116 root 1.1
1117 root 1.7 =back
1118 root 1.11
1119    
1120 root 1.18 =head2 Danke
1121 root 1.11
1122 root 1.18 ... an denjenigen, der Latex (a.k.a. ``the bag of dirty broken hacks'')
1123 root 1.11 endlich mal fixt.